2013-08-14 14:17:44 +02:00
/***************************************************************************
* Copyright ( C ) 2013 by Chris Mitchell *
2014-08-15 02:11:43 -07:00
* Copyright ( C ) 2014 by Ahmed Charles - acharles @ outlook . com *
2023-02-20 21:46:15 +00:00
* Copyright ( C ) 2020 , 2023 by Stephen Lyons - slysven @ virginmedia . com *
2013-08-14 14:17:44 +02:00
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the *
* Free Software Foundation , Inc . , *
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-04-17 14:48:23 -04:00
# include <QDebug>
2013-08-14 14:17:44 +02:00
# include "LuaInterface.h"
# include "VarUnit.h"
2021-12-07 06:21:39 +01:00
# include "utils.h"
2014-08-15 02:11:43 -07:00
2018-08-30 13:48:42 +02:00
# include <csetjmp>
2014-08-15 02:11:43 -07:00
2021-04-17 14:48:23 -04:00
extern " C " {
2025-05-17 22:34:05 +01:00
# if defined(INCLUDE_VERSIONED_LUA_HEADERS)
# include <lua5.1/lauxlib.h>
# include <lua5.1/lua.h>
# include <lua5.1/lualib.h>
# else
# include <lauxlib.h>
# include <lua.h>
# include <lualib.h>
# endif
2021-04-17 14:48:23 -04:00
}
2013-08-14 14:17:44 +02:00
static jmp_buf buf ;
2021-04-17 14:48:23 -04:00
LuaInterface : : LuaInterface ( lua_State * L )
2025-09-25 09:26:12 +02:00
: mL ( L )
2013-08-14 14:17:44 +02:00
{
2014-09-29 04:08:18 -07:00
varUnit . reset ( new VarUnit ( ) ) ;
2013-08-14 14:17:44 +02:00
//set our panic function
2021-04-17 14:48:23 -04:00
lua_atpanic ( L , & onPanic ) ;
2013-08-14 14:17:44 +02:00
}
fix: two ways a profile save could lose or resurrect your data (#9704)
#### Brief overview of PR changes/additions
- Variables: the export skipped its refresh whenever the editor's
Variables view was on screen, so anything a script wrote into a saved
variable while it sat open was dropped from the save - including the
session's last save, which is taken with whatever view the editor was
left on. The variables are now read into a throwaway tree, which also
stops a save stranding the editor's variable search results.
- Packages: a save taken while a unit was still executing an item of a
package that had just been uninstalled wrote that package's items back
into the profile, where they returned as orphans the Package Manager
could not remove. The XML writers now skip what the units have queued
for a deferred delete, the module writer included - reloading a module
from a script used to write both the pre- and post-reload copies of its
items into the module file.
- `LuaInterface::getVars()` is now `setjmp`-guarded like every other
Lua-touching method there, so a panic cannot jump past the export's
scope with its variable tree and registry references still held.
#### Motivation for adding to Mudlet
Both are silent data loss in everyday use: quitting with the editor on
the Variables tab, and the `mpkg`/auto-updater shape of uninstalling a
package from a script.
#### Other info (issues closed, discussion etc)
From the 5.0 QA sweep, findings C13 and C14. The variables half re-opens
the loss that `20009c5ec` "fix: variables added while playing are no
longer lost when saving (#9492)" fixed, via the guard it added; the
packages half is the missing counterpart to the self-uninstall deferral
in `276e8bbfd` (#9383) and its follow-ups. #9492's own cases still pass
unchanged.
**Test case:** create a table from the command line, tick it to be saved
in the editor's Variables view, leave the editor there, run `lua
myTable.later = "x"`, quit and reopen - `later` is still there. `ctest
-R 'XMLexportVariablesTest|PackageSelfUninstallTest'` covers both
halves; all 12 new cases were verified to fail against the unfixed
source.
Assisted-by: Claude:claude-opus-5
2026-08-07 10:14:45 +02:00
// Does not release lrefs: a profile reset closes the lua_State before this
// object is replaced, so unref'ing here would write into a freed state.
2018-06-05 06:50:40 +02:00
LuaInterface : : ~ LuaInterface ( ) = default ;
2014-09-29 04:08:18 -07:00
2017-04-27 03:55:38 -07:00
int LuaInterface : : onPanic ( lua_State * L )
2013-08-14 14:17:44 +02:00
{
QString error = " Lua Panic, No error information " ;
2017-04-27 03:55:38 -07:00
if ( lua_isstring ( L , - 1 ) ) {
2020-10-11 13:26:59 +02:00
error = lua_tostring ( L , - 1 ) ;
2023-02-27 08:15:54 +01:00
qDebug ( ) < < " Lua panic: " < < error ;
2013-08-14 14:17:44 +02:00
}
2023-02-27 08:15:54 +01:00
2013-08-14 14:17:44 +02:00
longjmp ( buf , 1 ) ;
return 1 ;
}
2017-04-27 03:55:38 -07:00
VarUnit * LuaInterface : : getVarUnit ( )
{
2014-09-29 04:08:18 -07:00
return varUnit . data ( ) ;
2013-08-14 14:17:44 +02:00
}
fix: two ways a profile save could lose or resurrect your data (#9704)
#### Brief overview of PR changes/additions
- Variables: the export skipped its refresh whenever the editor's
Variables view was on screen, so anything a script wrote into a saved
variable while it sat open was dropped from the save - including the
session's last save, which is taken with whatever view the editor was
left on. The variables are now read into a throwaway tree, which also
stops a save stranding the editor's variable search results.
- Packages: a save taken while a unit was still executing an item of a
package that had just been uninstalled wrote that package's items back
into the profile, where they returned as orphans the Package Manager
could not remove. The XML writers now skip what the units have queued
for a deferred delete, the module writer included - reloading a module
from a script used to write both the pre- and post-reload copies of its
items into the module file.
- `LuaInterface::getVars()` is now `setjmp`-guarded like every other
Lua-touching method there, so a panic cannot jump past the export's
scope with its variable tree and registry references still held.
#### Motivation for adding to Mudlet
Both are silent data loss in everyday use: quitting with the editor on
the Variables tab, and the `mpkg`/auto-updater shape of uninstalling a
package from a script.
#### Other info (issues closed, discussion etc)
From the 5.0 QA sweep, findings C13 and C14. The variables half re-opens
the loss that `20009c5ec` "fix: variables added while playing are no
longer lost when saving (#9492)" fixed, via the guard it added; the
packages half is the missing counterpart to the self-uninstall deferral
in `276e8bbfd` (#9383) and its follow-ups. #9492's own cases still pass
unchanged.
**Test case:** create a table from the command line, tick it to be saved
in the editor's Variables view, leave the editor there, run `lua
myTable.later = "x"`, quit and reopen - `later` is still there. `ctest
-R 'XMLexportVariablesTest|PackageSelfUninstallTest'` covers both
halves; all 12 new cases were verified to fail against the unfixed
source.
Assisted-by: Claude:claude-opus-5
2026-08-07 10:14:45 +02:00
lua_State * LuaInterface : : getState ( ) const
{
return mL ;
}
void LuaInterface : : releaseVariableReferences ( )
{
for ( const int ref : std : : as_const ( lrefs ) ) {
luaL_unref ( mL , LUA_REGISTRYINDEX , ref ) ;
}
lrefs . clear ( ) ;
}
2017-04-27 03:55:38 -07:00
QStringList LuaInterface : : varName ( TVar * var )
{
2013-08-14 14:17:44 +02:00
QStringList names ;
2023-04-15 06:38:39 +02:00
if ( var - > getName ( ) = = qsl ( " _G " ) ) {
2013-08-14 14:17:44 +02:00
names < < " " ;
return names ;
}
names < < var - > getName ( ) ;
2023-05-11 07:44:16 +02:00
TVar * pParent = var - > getParent ( ) ;
while ( pParent & & pParent - > getName ( ) ! = qsl ( " _G " ) ) {
names . insert ( 0 , pParent - > getName ( ) ) ;
pParent = pParent - > getParent ( ) ;
2013-08-14 14:17:44 +02:00
}
return names ;
}
2025-12-24 10:04:10 +00:00
std : : pair < bool , QString > LuaInterface : : validMove ( QTreeWidgetItem * pWidget )
2017-04-27 03:55:38 -07:00
{
2023-05-11 07:44:16 +02:00
TVar * pNewParent = varUnit - > getWVar ( pWidget ) ;
if ( pNewParent & & pNewParent - > getValueType ( ) ! = LUA_TTABLE ) {
2025-12-24 10:04:10 +00:00
//: Error message shown when user tries to drag a variable onto a non-table variable
return { false , QObject : : tr ( " Cannot move variable here - the target is not a table " ) } ;
2017-06-26 16:46:54 +02:00
}
2025-12-24 10:04:10 +00:00
return { true , QString ( ) } ;
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
void LuaInterface : : getAllChildren ( TVar * var , QList < TVar * > * list )
{
QListIterator < TVar * > it ( var - > getChildren ( true ) ) ;
2017-06-26 16:46:54 +02:00
if ( varUnit - > isSaved ( var ) | | var - > saved ) {
2013-08-14 14:17:44 +02:00
list - > append ( var ) ;
2017-06-26 16:46:54 +02:00
}
2017-04-27 03:55:38 -07:00
while ( it . hasNext ( ) ) {
TVar * child = it . next ( ) ;
2017-06-26 16:46:54 +02:00
if ( child - > getValueType ( ) = = LUA_TTABLE ) {
2017-04-27 03:55:38 -07:00
getAllChildren ( child , list ) ;
2017-06-26 16:46:54 +02:00
} else if ( varUnit - > isSaved ( child ) | | var - > saved ) {
2013-08-14 14:17:44 +02:00
list - > append ( child ) ;
2017-06-26 16:46:54 +02:00
}
2013-08-14 14:17:44 +02:00
}
}
2017-04-27 03:55:38 -07:00
bool LuaInterface : : loadKey ( lua_State * L , TVar * var )
{
if ( setjmp ( buf ) = = 0 ) {
2023-05-14 15:06:15 +02:00
const int keyType = var - > getKeyType ( ) ;
2017-04-27 03:55:38 -07:00
if ( var - > isReference ( ) ) {
2013-08-14 14:17:44 +02:00
lua_rawgeti ( L , LUA_REGISTRYINDEX , var - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2023-02-27 08:15:54 +01:00
if ( keyType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( L , var - > getName ( ) . toDouble ( ) ) ;
2023-02-27 08:15:54 +01:00
} else if ( keyType = = LUA_TTABLE ) {
} else if ( keyType = = LUA_TBOOLEAN ) {
2017-04-27 03:55:38 -07:00
lua_pushboolean ( L , var - > getName ( ) . toLower ( ) = = " true " ? 1 : 0 ) ;
} else {
2018-01-26 04:07:28 +01:00
lua_pushstring ( L , var - > getName ( ) . toUtf8 ( ) . constData ( ) ) ;
2013-08-14 14:17:44 +02:00
}
}
2023-02-27 08:15:54 +01:00
return lua_type ( L , - 1 ) = = keyType ;
2013-08-14 14:17:44 +02:00
}
2023-02-27 08:15:54 +01:00
2013-08-14 14:17:44 +02:00
return false ;
}
2017-04-27 03:55:38 -07:00
bool LuaInterface : : loadValue ( lua_State * L , TVar * var , int index )
{
2013-08-14 14:17:44 +02:00
//puts a value on stack
2017-04-27 03:55:38 -07:00
if ( setjmp ( buf ) = = 0 ) {
if ( loadKey ( L , var ) ) {
2013-08-14 14:17:44 +02:00
//everything is tabled in lua, we need to just find what table
//we're using, if index == 0, we iterate to the closest table
2017-06-26 16:46:54 +02:00
if ( index ) {
2025-11-18 03:49:49 -05:00
// Validate stack before attempting table access
const int stackTop = lua_gettop ( L ) ;
const int actualIndex = ( index < 0 ) ? stackTop + index + 1 : index ;
2026-01-03 16:00:41 +01:00
2025-11-18 03:49:49 -05:00
if ( actualIndex < = 0 | | actualIndex > stackTop ) {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::loadValue() - Invalid stack index " < < index < < " for variable \" " < < var - > getName ( ) < < " \" . Stack size: " < < stackTop
< < " , resolved index: " < < actualIndex < < " . " ;
2025-11-18 03:49:49 -05:00
return false ;
}
2026-01-03 16:00:41 +01:00
2025-11-18 03:49:49 -05:00
if ( ! lua_istable ( L , index ) ) {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::loadValue() - Value at stack index " < < index < < " is not a table for variable \" " < < var - > getName ( )
< < " \" . Got type: " < < lua_typename ( L , lua_type ( L , index ) ) < < " . " ;
2025-11-18 03:49:49 -05:00
return false ;
}
2026-01-03 16:00:41 +01:00
2013-08-14 14:17:44 +02:00
lua_gettable ( L , index ) ;
2017-06-26 16:46:54 +02:00
} else {
2025-11-18 03:49:49 -05:00
// Find the closest table on the stack
bool foundTable = false ;
2017-04-27 03:55:38 -07:00
for ( int j = 1 ; j < = lua_gettop ( L ) ; j + + ) {
if ( lua_type ( L , j * - 1 ) = = LUA_TTABLE ) {
lua_gettable ( L , j * - 1 ) ;
2025-11-18 03:49:49 -05:00
foundTable = true ;
break ;
2013-08-14 14:17:44 +02:00
}
}
2025-11-18 03:49:49 -05:00
if ( ! foundTable ) {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::loadValue() - No table found on stack for variable \" " < < var - > getName ( ) < < " \" when index=0. Stack size: " < < lua_gettop ( L )
< < " . " ;
2025-11-18 03:49:49 -05:00
return false ;
}
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
} else {
2013-08-14 14:17:44 +02:00
return false ;
2017-04-27 03:55:38 -07:00
}
if ( lua_gettop ( L ) ) {
2014-09-15 16:59:25 -04:00
return lua_type ( L , - 1 ) = = var - > getValueType ( ) ;
2017-04-27 03:55:38 -07:00
}
2014-09-15 16:59:25 -04:00
return false ;
2013-08-14 14:17:44 +02:00
}
return false ;
}
2017-04-27 03:55:38 -07:00
bool LuaInterface : : reparentCVariable ( TVar * from , TVar * to , TVar * curVar )
{
2013-08-14 14:17:44 +02:00
//get the old parent on the stack
2017-04-27 03:55:38 -07:00
if ( setjmp ( buf ) = = 0 ) {
if ( ! from | | ! to | | ( from = = to ) ) {
// moving from global to global or nowhere
2013-08-14 14:17:44 +02:00
return true ;
2017-04-27 03:55:38 -07:00
}
2023-05-14 15:06:15 +02:00
const int stackSize = lua_gettop ( mL ) ;
const bool isSaved = varUnit - > isSaved ( curVar ) ;
2017-04-27 03:55:38 -07:00
if ( isSaved ) {
QList < TVar * > list ;
2013-08-14 14:17:44 +02:00
getAllChildren ( curVar , & list ) ;
2017-04-27 03:55:38 -07:00
QListIterator < TVar * > it ( list ) ;
while ( it . hasNext ( ) ) {
TVar * t = it . next ( ) ;
2013-08-14 14:17:44 +02:00
varUnit - > removeSavedVar ( t ) ;
}
}
2017-04-27 03:55:38 -07:00
QList < TVar * > vars = varOrder ( curVar ) ;
2023-02-20 21:46:15 +00:00
lua_getglobal ( mL , ( vars [ 0 ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
int i = 1 ;
for ( ; i < vars . size ( ) ; i + + ) {
2023-02-20 21:46:15 +00:00
if ( ! loadValue ( mL , vars [ i ] , - 2 ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
}
//redo the parenting in TVar
from - > removeChild ( curVar ) ;
curVar - > setParent ( to ) ;
to - > addChild ( curVar ) ;
vars = varOrder ( curVar ) ;
//do the actual reparenting part
2017-04-27 03:55:38 -07:00
if ( to = = varUnit - > getBase ( ) ) {
2013-08-14 14:17:44 +02:00
//we're going global
2023-02-20 21:46:15 +00:00
lua_setglobal ( mL , curVar - > getName ( ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2023-02-20 21:46:15 +00:00
lua_getglobal ( mL , ( vars [ 0 ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
i = 1 ;
for ( ; i < vars . size ( ) - 1 ; i + + ) {
2023-02-20 21:46:15 +00:00
if ( ! loadValue ( mL , vars [ i ] , - 2 ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
2023-02-20 21:46:15 +00:00
lua_remove ( mL , - 2 ) ;
2013-08-14 14:17:44 +02:00
}
2023-02-20 21:46:15 +00:00
lua_insert ( mL , - 2 ) ;
if ( ! loadKey ( mL , curVar ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
2023-02-20 21:46:15 +00:00
lua_insert ( mL , - 2 ) ;
if ( ! lua_istable ( mL , - 3 ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
2023-02-20 21:46:15 +00:00
lua_settable ( mL , - 3 ) ;
lua_pop ( mL , 1 ) ;
2013-08-14 14:17:44 +02:00
}
//delete the old copy
2017-04-27 03:55:38 -07:00
if ( from = = varUnit - > getBase ( ) ) {
2023-02-20 21:46:15 +00:00
lua_pushnil ( mL ) ;
lua_setglobal ( mL , curVar - > getName ( ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2023-02-20 21:46:15 +00:00
if ( ! loadKey ( mL , curVar ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
2023-02-20 21:46:15 +00:00
lua_pushnil ( mL ) ;
if ( ! lua_istable ( mL , - 3 ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
2023-02-20 21:46:15 +00:00
lua_settable ( mL , - 3 ) ;
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
if ( isSaved ) {
QList < TVar * > list ;
2013-08-14 14:17:44 +02:00
list . append ( to ) ;
getAllChildren ( curVar , & list ) ;
2017-04-27 03:55:38 -07:00
QListIterator < TVar * > it ( list ) ;
while ( it . hasNext ( ) ) {
TVar * t = it . next ( ) ;
2013-08-14 14:17:44 +02:00
varUnit - > addSavedVar ( t ) ;
}
}
2023-02-20 21:46:15 +00:00
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return true ;
}
2014-01-10 21:41:20 -05:00
return false ;
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
bool LuaInterface : : reparentVariable ( QTreeWidgetItem * newP , QTreeWidgetItem * cItem , QTreeWidgetItem * oldP )
{
2013-08-14 14:17:44 +02:00
//if oldParent doesn't exist:
//this means we were moved to a table from the global namespace
//if newParent doesn't exist:
//we were moved to the global namespace
//if both exist:
//this means we were moved from inside a table to inside another table
//and in both instances, this table was not _G
2019-08-13 10:27:54 +02:00
TVar * curVar = varUnit - > getWVar ( cItem ) ;
if ( ! curVar ) {
return false ;
}
2021-04-17 14:48:23 -04:00
2017-04-27 03:55:38 -07:00
TVar * newParent = varUnit - > getWVar ( newP ) ;
TVar * oldParent = varUnit - > getWVar ( oldP ) ;
TVar * from = oldParent ;
TVar * to = newParent ;
if ( newParent & & newParent - > getValueType ( ) ! = LUA_TTABLE ) {
2014-01-10 21:41:20 -05:00
//FIXME: report why this fails to user
2013-08-14 14:17:44 +02:00
return false ;
}
2020-06-01 17:35:35 +01:00
2017-04-27 03:55:38 -07:00
if ( ! newParent & & ! oldParent ) {
2013-08-14 14:17:44 +02:00
//happens when we move from _G to _G
return false ;
2020-06-01 17:35:35 +01:00
}
if ( ! oldParent ) {
2013-08-14 14:17:44 +02:00
from = varUnit - > getBase ( ) ;
2020-06-01 17:35:35 +01:00
// newParent cannot be a nullptr here as we would have returned in
// previous if - so to won't be either:
2013-08-14 14:17:44 +02:00
to = newParent ;
2017-04-27 03:55:38 -07:00
} else if ( ! newParent ) {
2020-06-01 17:35:35 +01:00
// oldParent cannot be a nullptr here as we would have returned in
// previous if - so from won't be either:
2013-08-14 14:17:44 +02:00
from = oldParent ;
to = varUnit - > getBase ( ) ;
}
2020-06-01 17:35:35 +01:00
// one of from and to must not be a nullptr here - so prior test for BOTH
// being a nullptr here and returning false in that case was dead code.
2017-04-27 03:55:38 -07:00
return reparentCVariable ( from , to , curVar ) ;
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
QList < TVar * > LuaInterface : : varOrder ( TVar * var )
{
QList < TVar * > vars ;
2023-04-15 06:38:39 +02:00
if ( var - > getName ( ) = = qsl ( " _G " ) ) {
2013-08-14 14:17:44 +02:00
return vars ;
}
vars < < var ;
2023-05-11 07:44:16 +02:00
TVar * pParent = var - > getParent ( ) ;
while ( pParent & & pParent - > getName ( ) ! = qsl ( " _G " ) ) {
vars . insert ( 0 , pParent ) ;
pParent = pParent - > getParent ( ) ;
2013-08-14 14:17:44 +02:00
}
return vars ;
}
2017-04-27 03:55:38 -07:00
void LuaInterface : : createVar ( TVar * var )
{
setValue ( var ) ;
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
bool LuaInterface : : setCValue ( QList < TVar * > vars )
{
2013-08-14 14:17:44 +02:00
//make the new stack
2017-04-27 03:55:38 -07:00
TVar * var = vars . back ( ) ;
if ( setjmp ( buf ) = = 0 ) {
2023-05-14 15:06:15 +02:00
const int stackSize = lua_gettop ( mL ) ;
2023-02-20 21:46:15 +00:00
lua_getglobal ( mL , ( vars [ 0 ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
int i = 1 ;
for ( ; i < vars . size ( ) - 1 ; i + + ) {
2023-02-20 21:46:15 +00:00
if ( ! loadValue ( mL , vars [ i ] , - 2 ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
}
//push our value onto the stack
2017-04-27 03:55:38 -07:00
switch ( var - > getValueType ( ) ) {
2013-08-14 14:17:44 +02:00
case LUA_TSTRING :
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , var - > getValue ( ) . toUtf8 ( ) . constData ( ) ) ;
2013-08-14 14:17:44 +02:00
break ;
case LUA_TNUMBER :
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , var - > getValue ( ) . toDouble ( ) ) ;
2013-08-14 14:17:44 +02:00
break ;
case LUA_TBOOLEAN :
2023-02-20 21:46:15 +00:00
lua_pushboolean ( mL , var - > getValue ( ) . toLower ( ) = = " true " ? 1 : 0 ) ;
2013-08-14 14:17:44 +02:00
break ;
case LUA_TTABLE :
2023-02-20 21:46:15 +00:00
lua_newtable ( mL ) ;
2013-08-14 14:17:44 +02:00
break ;
default :
2023-02-20 21:46:15 +00:00
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
//set it up
2023-02-20 21:46:15 +00:00
if ( lua_type ( mL , - 1 ) ! = var - > getValueType ( ) ) {
lua_settop ( mL , stackSize ) ;
2013-08-14 14:17:44 +02:00
return false ;
}
2023-02-20 21:46:15 +00:00
lua_settable ( mL , - 3 ) ;
2013-08-14 14:17:44 +02:00
}
2014-01-10 21:41:20 -05:00
return false ;
2013-08-14 14:17:44 +02:00
}
2018-01-26 04:07:28 +01:00
// sets the value of a Lua variable by running dynamically-generated Lua code
2017-04-27 03:55:38 -07:00
bool LuaInterface : : setValue ( TVar * var )
{
2013-08-14 14:17:44 +02:00
//This function assumes the var has been modified and then called
2021-04-17 14:48:23 -04:00
2013-08-14 14:17:44 +02:00
2017-04-27 03:55:38 -07:00
QList < TVar * > vars = varOrder ( var ) ;
2018-01-26 04:07:28 +01:00
QString variableChangeCode = vars [ 0 ] - > getName ( ) ;
2017-04-27 03:55:38 -07:00
for ( int i = 1 ; i < vars . size ( ) ; i + + ) {
2017-06-26 16:46:54 +02:00
if ( vars [ i ] - > isReference ( ) ) {
2017-04-27 03:55:38 -07:00
return setCValue ( vars ) ;
2017-06-26 16:46:54 +02:00
}
2026-07-18 11:27:01 +02:00
const int keyType = vars [ i ] - > getKeyType ( ) ;
if ( keyType = = LUA_TNUMBER | | keyType = = LUA_TBOOLEAN ) {
2021-12-07 06:21:39 +01:00
variableChangeCode . append ( qsl ( " [%1] " ) . arg ( vars . at ( i ) - > getName ( ) ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2021-12-07 06:21:39 +01:00
variableChangeCode . append ( qsl ( R " ([ " % 1 " ]) " ) . arg ( vars . at ( i ) - > getName ( ) ) ) ;
2013-08-14 14:17:44 +02:00
}
}
2017-04-27 03:55:38 -07:00
switch ( var - > getValueType ( ) ) {
2013-08-14 14:17:44 +02:00
case LUA_TSTRING :
2021-12-07 06:21:39 +01:00
variableChangeCode . append ( qsl ( " = [[%1]] " ) . arg ( var - > getValue ( ) ) ) ;
2013-08-14 14:17:44 +02:00
break ;
case LUA_TNUMBER :
2021-12-07 06:21:39 +01:00
variableChangeCode . append ( qsl ( " = %1 " ) . arg ( var - > getValue ( ) ) ) ;
2013-08-14 14:17:44 +02:00
break ;
case LUA_TBOOLEAN :
2021-12-07 06:21:39 +01:00
variableChangeCode . append ( qsl ( " = %1 " ) . arg ( var - > getValue ( ) ) ) ;
2013-08-14 14:17:44 +02:00
break ;
case LUA_TTABLE :
2018-01-26 04:07:28 +01:00
variableChangeCode . append ( QLatin1String ( " = {} " ) ) ;
2013-08-14 14:17:44 +02:00
break ;
default :
return false ;
}
2023-02-20 21:46:15 +00:00
int error = luaL_loadstring ( mL , variableChangeCode . toUtf8 ( ) . constData ( ) ) ;
2020-06-01 17:35:35 +01:00
if ( error ) {
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::setValue(...) WARNING - Internal Lua (parsing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" " < < variableChangeCode < < " \" . " ;
return false ;
}
2023-02-20 21:46:15 +00:00
error = lua_pcall ( mL , 0 , LUA_MULTRET , 0 ) ;
2017-04-27 03:55:38 -07:00
if ( error ) {
2020-06-01 17:35:35 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::setValue(...) WARNING - Internal Lua (executing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" " < < variableChangeCode < < " \" . " ;
2013-08-14 14:17:44 +02:00
return false ;
}
return true ;
}
2017-04-27 03:55:38 -07:00
void LuaInterface : : deleteVar ( TVar * var )
{
QList < TVar * > vars = varOrder ( var ) ;
2013-08-14 14:17:44 +02:00
QString oldName = vars [ 0 ] - > getName ( ) ;
2017-04-27 03:55:38 -07:00
for ( int i = 1 ; i < vars . size ( ) ; i + + ) {
2026-07-18 11:27:01 +02:00
const int keyType = vars [ i ] - > getKeyType ( ) ;
if ( keyType = = LUA_TNUMBER | | keyType = = LUA_TBOOLEAN ) {
2021-12-07 06:21:39 +01:00
oldName . append ( qsl ( " [%1] " ) . arg ( vars [ i ] - > getName ( ) ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2021-12-07 06:21:39 +01:00
oldName . append ( qsl ( R " ([ " % 1 " ]) " ) . arg ( vars [ i ] - > getName ( ) ) ) ;
2013-08-14 14:17:44 +02:00
}
}
//delete it
2021-12-07 06:21:39 +01:00
oldName . append ( qsl ( " = nil " ) ) ;
2023-02-20 21:46:15 +00:00
int error = luaL_loadstring ( mL , oldName . toUtf8 ( ) . constData ( ) ) ;
2020-06-01 17:35:35 +01:00
if ( error ) {
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::deleteVar(...) WARNING - Internal Lua (parsing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" " < < oldName < < " \" . " ;
return ;
}
2023-02-20 21:46:15 +00:00
error = lua_pcall ( mL , 0 , LUA_MULTRET , 0 ) ;
2017-04-27 03:55:38 -07:00
if ( error ) {
2020-06-01 17:35:35 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::deleteVar(...) WARNING - Internal Lua (executing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" " < < oldName < < " \" . " ;
2013-08-14 14:17:44 +02:00
}
}
2017-04-27 03:55:38 -07:00
void LuaInterface : : renameCVar ( QList < TVar * > vars )
{
2013-08-14 14:17:44 +02:00
//uses C Api to rename a variable.
//dangerous function since you can get an api panic
//and trash the stack
2021-04-17 14:48:23 -04:00
2017-04-27 03:55:38 -07:00
TVar * var = vars . back ( ) ;
2013-08-14 14:17:44 +02:00
//make the new stack
2023-02-20 21:46:15 +00:00
lua_getglobal ( mL , ( vars [ 0 ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
if ( setjmp ( buf ) = = 0 ) {
int i = 1 ;
int pushCount = 0 ;
2013-08-14 14:17:44 +02:00
int kType ;
2017-04-27 03:55:38 -07:00
for ( ; i < vars . size ( ) - 1 ; i + + ) {
2013-08-14 14:17:44 +02:00
kType = vars [ i ] - > getKeyType ( ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , QString ( vars [ i ] - > getName ( ) ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE ) {
2026-07-18 11:27:01 +02:00
// registry references are integer refs so must stay toInt()
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , vars [ i ] - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( vars [ i ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2013-08-14 14:17:44 +02:00
}
2023-02-20 21:46:15 +00:00
lua_gettable ( mL , - 2 ) ;
if ( lua_isnil ( mL , - 1 ) ) {
2013-08-14 14:17:44 +02:00
//value didn't exist, make it
2023-02-20 21:46:15 +00:00
lua_pop ( mL , - 1 ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , QString ( vars [ i ] - > getName ( ) ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE | | kType = = LUA_TFUNCTION ) {
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , vars [ i ] - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( vars [ i ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2013-08-14 14:17:44 +02:00
}
2023-02-20 21:46:15 +00:00
lua_newtable ( mL ) ;
lua_settable ( mL , - 3 ) ;
2017-04-27 03:55:38 -07:00
i - - ; //decrement since we want to reput this table on the stack on next iteration
2013-08-14 14:17:44 +02:00
}
}
kType = var - > getKeyType ( ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TSTRING ) {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( var - > getNewName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , var - > getNewName ( ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE ) {
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , var - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameCVar() - Unsupported key type: " < < lua_typename ( mL , kType ) < < " for variable \" " < < var - > getName ( )
< < " \" . Expected string, number, or table. " ;
2013-08-14 14:17:44 +02:00
return ;
}
//put the old value on the stack
2023-02-20 21:46:15 +00:00
lua_getglobal ( mL , ( vars [ 0 ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
i = 1 ;
for ( ; i < vars . size ( ) - 1 ; i + + ) {
2013-08-14 14:17:44 +02:00
kType = vars [ i ] - > getKeyType ( ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , QString ( vars [ i ] - > getName ( ) ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE | | kType = = LUA_TFUNCTION ) {
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , vars [ i ] - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( vars [ i ] - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2013-08-14 14:17:44 +02:00
}
2023-02-20 21:46:15 +00:00
lua_gettable ( mL , - 2 ) ;
2013-08-14 14:17:44 +02:00
pushCount + + ;
}
kType = var - > getKeyType ( ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TSTRING ) {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( var - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , var - > getName ( ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE | | kType = = LUA_TFUNCTION ) {
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , var - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameCVar() - Unsupported key type when retrieving old value: " < < lua_typename ( mL , kType ) < < " for variable \" " < < var - > getName ( )
< < " \" . Expected string, number, table, or function. " ;
2013-08-14 14:17:44 +02:00
return ;
}
2023-02-20 21:46:15 +00:00
lua_gettable ( mL , - 2 ) ;
2013-08-14 14:17:44 +02:00
pushCount + + ;
//old value is @ -1 now
//we want to put our new named key @ -2
kType = var - > getKeyType ( ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TSTRING ) {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( var - > getNewName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , var - > getNewName ( ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE ) {
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , var - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameCVar() - Unsupported key type when setting new key: " < < lua_typename ( mL , kType ) < < " for variable \" " < < var - > getName ( )
< < " \" . Expected string, number, or table. " ;
2013-08-14 14:17:44 +02:00
return ;
}
pushCount + + ;
2023-02-20 21:46:15 +00:00
lua_insert ( mL , - 2 ) ;
lua_settable ( mL , - 3 - pushCount ) ;
2013-08-14 14:17:44 +02:00
//key & value popped
//delete it, so we put the old key back on the stack and set to nil
kType = var - > getKeyType ( ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TSTRING ) {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( var - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , var - > getName ( ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE | | kType = = LUA_TFUNCTION ) {
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , var - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameCVar() - Unsupported key type when deleting old key: " < < lua_typename ( mL , kType ) < < " for variable \" " < < var - > getName ( )
< < " \" . Expected string, number, table, or function. " ;
2013-08-14 14:17:44 +02:00
return ;
}
2023-02-20 21:46:15 +00:00
lua_pushnil ( mL ) ;
lua_settable ( mL , - 3 ) ;
2013-08-14 14:17:44 +02:00
var - > clearNewName ( ) ;
}
}
2017-04-27 03:55:38 -07:00
bool LuaInterface : : loadVar ( TVar * var )
{
2013-08-14 14:17:44 +02:00
//puts the value of a variable on the -1 position of the stack
2017-04-27 03:55:38 -07:00
if ( setjmp ( buf ) = = 0 ) {
2023-05-14 15:06:15 +02:00
const int kType = var - > getKeyType ( ) ;
const int vType = var - > getValueType ( ) ;
2017-04-27 03:55:38 -07:00
if ( vType = = LUA_TTABLE ) {
if ( kType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , QString ( var - > getName ( ) ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE ) {
2023-02-20 21:46:15 +00:00
lua_rawgeti ( mL , LUA_REGISTRYINDEX , var - > getName ( ) . toInt ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( var - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2013-08-14 14:17:44 +02:00
}
2023-02-20 21:46:15 +00:00
if ( lua_istable ( mL , - 2 ) ) {
lua_gettable ( mL , - 2 ) ;
2013-08-14 14:17:44 +02:00
return true ;
}
2026-07-18 18:39:57 +02:00
lua_pop ( mL , 1 ) ;
return false ;
}
if ( vType = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , QString ( var - > getValue ( ) ) . toDouble ( ) ) ;
2017-04-27 03:55:38 -07:00
} else if ( vType = = LUA_TBOOLEAN ) {
2023-02-20 21:46:15 +00:00
lua_pushboolean ( mL , var - > getValue ( ) . toLower ( ) = = " true " ? 1 : 0 ) ;
2017-04-27 03:55:38 -07:00
} else if ( vType = = LUA_TSTRING ) {
2023-02-20 21:46:15 +00:00
lua_pushstring ( mL , QString ( var - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2013-08-14 14:17:44 +02:00
return false ;
2017-04-27 03:55:38 -07:00
}
} else {
2026-01-03 16:00:41 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::loadVar() - Lua panic occurred while loading variable \" " < < var - > getName ( ) < < " \" with key type " < < lua_typename ( mL , var - > getKeyType ( ) )
< < " and value type " < < lua_typename ( mL , var - > getValueType ( ) ) < < " . " ;
2013-08-14 14:17:44 +02:00
return false ;
}
2013-08-19 12:25:26 +02:00
return true ;
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
void LuaInterface : : renameVar ( TVar * var )
{
2013-08-14 14:17:44 +02:00
//this assumes anything like reparenting has been done
2021-04-17 14:48:23 -04:00
2017-04-27 03:55:38 -07:00
QList < TVar * > vars = varOrder ( var ) ;
2018-09-22 08:49:52 +02:00
QString oldVariable = vars . at ( 0 ) - > getName ( ) ;
2013-08-14 14:17:44 +02:00
QString newName ;
2017-06-26 16:46:54 +02:00
if ( vars . size ( ) > 1 ) {
2013-08-14 14:17:44 +02:00
newName = vars [ 0 ] - > getName ( ) ;
2017-06-26 16:46:54 +02:00
}
2020-06-01 17:35:35 +01:00
2017-04-27 03:55:38 -07:00
for ( int i = 1 ; i < vars . size ( ) ; i + + ) {
2023-05-14 15:06:15 +02:00
const int kType = vars [ i ] - > getKeyType ( ) ;
2026-07-18 11:27:01 +02:00
// numbers and booleans use unquoted subscripts: t[3.14], t[true]
if ( kType = = LUA_TNUMBER | | kType = = LUA_TBOOLEAN ) {
2021-12-07 06:21:39 +01:00
oldVariable . append ( qsl ( " [%1] " ) . arg ( vars . at ( i ) - > getName ( ) ) ) ;
2017-06-26 16:46:54 +02:00
if ( i < vars . size ( ) - 1 ) {
2021-12-07 06:21:39 +01:00
newName . append ( qsl ( " [%1] " ) . arg ( vars [ i ] - > getName ( ) ) ) ;
2017-06-26 16:46:54 +02:00
}
2017-04-27 03:55:38 -07:00
} else if ( kType = = LUA_TTABLE ) {
2013-08-14 14:17:44 +02:00
renameCVar ( vars ) ;
return ;
2026-07-18 11:27:01 +02:00
} else {
// that leaves LUA_TSTRING
oldVariable . append ( qsl ( R " ([ " % 1 " ]) " ) . arg ( vars . at ( i ) - > getName ( ) ) ) ;
if ( i < vars . size ( ) - 1 ) {
newName . append ( qsl ( R " ([ " % 1 " ]) " ) . arg ( vars . at ( i ) - > getName ( ) ) ) ;
}
2013-08-14 14:17:44 +02:00
}
}
2018-09-22 08:49:52 +02:00
if ( vars . size ( ) < = 1 ) {
// this variable is at root level on _G
2021-12-07 06:21:39 +01:00
newName . append ( qsl ( " _G[ \" %1 \" ] " ) . arg ( vars . last ( ) - > getNewName ( ) ) ) ;
2017-04-27 03:55:38 -07:00
} else {
2018-09-22 08:49:52 +02:00
// this variable is nested in a table
2026-07-18 11:27:01 +02:00
if ( var - > getNewKeyType ( ) = = LUA_TNUMBER | | var - > getNewKeyType ( ) = = LUA_TBOOLEAN ) {
2021-12-07 06:21:39 +01:00
newName . append ( qsl ( " [%1] " ) . arg ( vars . last ( ) - > getNewName ( ) ) ) ;
2018-09-22 08:49:52 +02:00
} else {
2021-12-07 06:21:39 +01:00
newName . append ( qsl ( R " ([ " % 1 " ]) " ) . arg ( vars . last ( ) - > getNewName ( ) ) ) ;
2018-09-22 08:49:52 +02:00
}
2017-04-27 03:55:38 -07:00
}
2018-09-22 08:49:52 +02:00
2021-12-07 06:21:39 +01:00
auto renameCode = qsl ( " %1 = %2 " ) . arg ( newName , oldVariable ) ;
2023-02-20 21:46:15 +00:00
int error = luaL_loadstring ( mL , renameCode . toUtf8 ( ) . constData ( ) ) ;
2020-06-01 17:35:35 +01:00
if ( error ) {
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameVar(...) WARNING - In copying (first) stage, internal Lua (parsing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" " < < renameCode
< < " \" . " ;
var - > clearNewName ( ) ;
return ;
}
2023-02-20 21:46:15 +00:00
error = lua_pcall ( mL , 0 , LUA_MULTRET , 0 ) ;
2017-04-27 03:55:38 -07:00
if ( error ) {
2020-06-01 17:35:35 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameVar(...) WARNING - In copying (first) stage, internal Lua (executing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" "
< < renameCode < < " \" . " ;
2013-08-14 14:17:44 +02:00
var - > clearNewName ( ) ;
return ;
}
2020-06-01 17:35:35 +01:00
2013-08-14 14:17:44 +02:00
//delete it
2023-02-20 21:46:15 +00:00
error = luaL_loadstring ( mL , oldVariable . append ( QLatin1String ( " = nil " ) ) . toUtf8 ( ) . constData ( ) ) ;
2020-06-01 17:35:35 +01:00
if ( error ) {
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameVar(...) WARNING - In deleting (second) stage, internal Lua (parsing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" "
< < renameCode < < " \" . " ;
var - > clearNewName ( ) ;
return ;
}
2023-02-20 21:46:15 +00:00
error = lua_pcall ( mL , 0 , LUA_MULTRET , 0 ) ;
2017-04-27 03:55:38 -07:00
if ( error ) {
2020-06-01 17:35:35 +01:00
qWarning ( ) . noquote ( ) . nospace ( ) < < " LuaInterface::renameVar(...) WARNING - In deleting (second) stage, internal Lua (executing) error: \" " < < lua_tostring ( mL , - 1 ) < < " \" in code: \n \" "
< < renameCode < < " \" . " ;
2013-08-14 14:17:44 +02:00
}
var - > clearNewName ( ) ;
}
2023-02-27 08:15:54 +01:00
// returns the value for a string/number/boolean datatype, or an empty string otherwise
2017-04-27 03:55:38 -07:00
QString LuaInterface : : getValue ( TVar * var )
{
if ( setjmp ( buf ) = = 0 ) {
2023-05-14 15:06:15 +02:00
QList < TVar * > const vars = varOrder ( var ) ;
2017-06-26 16:46:54 +02:00
if ( vars . empty ( ) ) {
2023-02-27 08:15:54 +01:00
return { } ;
2017-06-26 16:46:54 +02:00
}
2023-05-14 15:06:15 +02:00
const int pCount = vars . size ( ) ; //how many things we need to pop from the stack at the end
2013-08-14 14:17:44 +02:00
//load from _G first
2023-02-27 08:15:54 +01:00
auto firstVariable = vars . constFirst ( ) ;
if ( firstVariable - > getKeyType ( ) = = LUA_TSTRING ) {
lua_getglobal ( mL , ( firstVariable - > getName ( ) ) . toUtf8 ( ) . constData ( ) ) ;
} else if ( firstVariable - > getKeyType ( ) = = LUA_TNUMBER ) {
2026-07-18 11:27:01 +02:00
lua_pushnumber ( mL , firstVariable - > getName ( ) . toDouble ( ) ) ;
lua_gettable ( mL , LUA_GLOBALSINDEX ) ;
} else if ( firstVariable - > getKeyType ( ) = = LUA_TBOOLEAN ) {
lua_pushboolean ( mL , firstVariable - > getName ( ) . toLower ( ) = = " true " ? 1 : 0 ) ;
lua_gettable ( mL , LUA_GLOBALSINDEX ) ;
2023-02-27 08:15:54 +01:00
}
if ( lua_isnoneornil ( mL , lua_gettop ( mL ) ) ) {
2023-05-14 15:06:15 +02:00
qDebug ( ) < < " LuaInterface::getValue: Couldn't put root value " < < firstVariable - > getName ( ) < < " onto the Lua stack in order to get value of " < < var - > getName ( )
2023-02-27 08:15:54 +01:00
< < " , perhaps the key type isn't supported? " ;
return { } ;
}
2017-04-27 03:55:38 -07:00
for ( int i = 1 ; i < vars . size ( ) ; i + + ) {
2023-02-27 08:15:54 +01:00
if ( ! loadValue ( mL , vars . at ( i ) , - 2 ) ) {
return { } ;
2017-06-26 16:46:54 +02:00
}
2013-08-14 14:17:44 +02:00
}
2023-05-14 15:06:15 +02:00
const int valueType = lua_type ( mL , - 1 ) ;
2018-01-26 04:07:28 +01:00
QString value ;
2023-02-27 08:15:54 +01:00
if ( valueType = = LUA_TBOOLEAN ) {
2023-02-20 21:46:15 +00:00
value = lua_toboolean ( mL , - 1 ) = = 0 ? QLatin1String ( " false " ) : QLatin1String ( " true " ) ;
2023-02-27 08:15:54 +01:00
} else if ( valueType = = LUA_TNUMBER | | valueType = = LUA_TSTRING ) {
2023-02-20 21:46:15 +00:00
value = lua_tostring ( mL , - 1 ) ;
2017-06-26 16:46:54 +02:00
}
2023-02-20 21:46:15 +00:00
lua_pop ( mL , pCount ) ;
2013-08-14 14:17:44 +02:00
return value ;
}
2023-02-27 08:15:54 +01:00
return { } ;
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
void LuaInterface : : iterateTable ( lua_State * L , int index , TVar * tVar , bool hide )
{
2013-08-14 14:17:44 +02:00
depth + + ;
2017-04-27 03:55:38 -07:00
while ( lua_next ( L , index ) ) {
2023-05-14 15:06:15 +02:00
const int vType = lua_type ( L , - 1 ) ;
const int kType = lua_type ( L , - 2 ) ;
2017-04-27 03:55:38 -07:00
lua_pushvalue ( L , - 2 ) ; //we do this because extracting the key with tostring changes it
2013-08-14 14:17:44 +02:00
QString keyName ;
QString valueName ;
2017-04-09 19:49:02 +02:00
auto var = new TVar ( ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TTABLE ) {
keyName = QString : : number ( luaL_ref ( L , LUA_REGISTRYINDEX ) ) ; //this function pops the top item
2013-08-14 14:17:44 +02:00
lrefs . append ( keyName . toInt ( ) ) ;
var - > setReference ( true ) ;
2026-07-18 11:27:01 +02:00
} else if ( kType = = LUA_TBOOLEAN ) {
//lua_tostring() returns NULL for booleans, name the key ourselves
keyName = lua_toboolean ( L , - 1 ) ? qsl ( " true " ) : qsl ( " false " ) ;
lua_pop ( L , 1 ) ;
2017-04-27 03:55:38 -07:00
} else {
2020-10-11 13:26:59 +02:00
keyName = lua_tostring ( L , - 1 ) ;
2017-04-27 03:55:38 -07:00
if ( kType = = LUA_TFUNCTION & & keyName . isEmpty ( ) ) {
2013-08-14 14:17:44 +02:00
//we lost the reference
keyName = QString : : number ( luaL_ref ( L , LUA_REGISTRYINDEX ) ) ;
lrefs . append ( keyName . toInt ( ) ) ;
var - > setReference ( true ) ;
2017-04-27 03:55:38 -07:00
} else {
2013-08-14 14:17:44 +02:00
lua_pop ( L , 1 ) ;
2017-04-27 03:55:38 -07:00
}
2013-08-14 14:17:44 +02:00
}
2017-04-27 03:55:38 -07:00
if ( keyName = = " package " & & depth = = 1 ) { //don't load in the 'package' table
2013-08-14 14:17:44 +02:00
lua_pop ( L , 1 ) ;
tVar - > removeChild ( var ) ;
delete var ;
continue ;
}
var - > setName ( keyName , kType ) ;
var - > setValueType ( vType ) ;
var - > setParent ( tVar ) ;
var - > hidden = hide ;
tVar - > addChild ( var ) ;
2023-05-11 07:44:16 +02:00
const void * pKey = lua_topointer ( L , - 1 ) ;
var - > pKey = pKey ;
const void * pValue = lua_topointer ( L , - 2 ) ;
var - > pValue = pValue ;
2023-04-15 06:38:39 +02:00
if ( varUnit - > varExists ( var ) | | keyName = = qsl ( " _G " ) ) {
2013-08-14 14:17:44 +02:00
lua_pop ( L , 1 ) ;
tVar - > removeChild ( var ) ;
delete var ;
continue ;
}
varUnit - > addVariable ( var ) ;
2023-05-11 07:44:16 +02:00
varUnit - > addPointer ( pKey ) ;
2013-08-14 14:17:44 +02:00
2023-05-11 07:44:16 +02:00
varUnit - > addPointer ( pValue ) ;
2017-04-27 03:55:38 -07:00
if ( vType = = LUA_TTABLE ) {
if ( depth < = 99 & & lua_checkstack ( L , 3 ) ) { //depth is historical now
2013-08-14 14:17:44 +02:00
//put the table on top
lua_pushnil ( L ) ;
var - > setValue ( " {} " , LUA_TTABLE ) ;
iterateTable ( L , - 2 , var , hide ) ;
depth - - ;
}
2017-04-27 03:55:38 -07:00
} else if ( vType = = LUA_TSTRING | | vType = = LUA_TNUMBER ) {
lua_pushvalue ( L , - 1 ) ;
2020-10-11 13:26:59 +02:00
valueName = lua_tostring ( L , - 1 ) ;
2013-08-14 14:17:44 +02:00
var - > setValue ( valueName ) ;
2017-04-27 03:55:38 -07:00
lua_pop ( L , 1 ) ;
} else if ( vType = = LUA_TBOOLEAN ) {
2013-08-14 14:17:44 +02:00
valueName = lua_toboolean ( L , - 1 ) = = 0 ? " false " : " true " ;
var - > setValue ( valueName ) ;
2017-04-27 03:55:38 -07:00
} else if ( vType = = LUA_TFUNCTION
& & ( ! keyName . toLower ( ) . startsWith ( " alias " ) & & ! keyName . toLower ( ) . startsWith ( " trigger " ) & & ! keyName . toLower ( ) . startsWith ( " action " ) & & ! keyName . toLower ( ) . startsWith ( " timer " )
& & ! keyName . toLower ( ) . startsWith ( " key " ) ) ) {
2013-08-14 14:17:44 +02:00
//functions are compiled to bytecode so there is no reference
var - > setValue ( " function " ) ;
2017-04-27 03:55:38 -07:00
} else {
2013-08-14 14:17:44 +02:00
tVar - > removeChild ( var ) ;
varUnit - > removeVariable ( var ) ;
delete var ;
}
lua_pop ( L , 1 ) ;
}
}
2017-04-27 03:55:38 -07:00
void LuaInterface : : getVars ( bool hide )
{
2013-08-14 14:17:44 +02:00
//returns the base item
2020-11-26 20:54:01 +00:00
// QElapsedTimer t;
// t.start();
fix: two ways a profile save could lose or resurrect your data (#9704)
#### Brief overview of PR changes/additions
- Variables: the export skipped its refresh whenever the editor's
Variables view was on screen, so anything a script wrote into a saved
variable while it sat open was dropped from the save - including the
session's last save, which is taken with whatever view the editor was
left on. The variables are now read into a throwaway tree, which also
stops a save stranding the editor's variable search results.
- Packages: a save taken while a unit was still executing an item of a
package that had just been uninstalled wrote that package's items back
into the profile, where they returned as orphans the Package Manager
could not remove. The XML writers now skip what the units have queued
for a deferred delete, the module writer included - reloading a module
from a script used to write both the pre- and post-reload copies of its
items into the module file.
- `LuaInterface::getVars()` is now `setjmp`-guarded like every other
Lua-touching method there, so a panic cannot jump past the export's
scope with its variable tree and registry references still held.
#### Motivation for adding to Mudlet
Both are silent data loss in everyday use: quitting with the editor on
the Variables tab, and the `mpkg`/auto-updater shape of uninstalling a
package from a script.
#### Other info (issues closed, discussion etc)
From the 5.0 QA sweep, findings C13 and C14. The variables half re-opens
the loss that `20009c5ec` "fix: variables added while playing are no
longer lost when saving (#9492)" fixed, via the guard it added; the
packages half is the missing counterpart to the self-uninstall deferral
in `276e8bbfd` (#9383) and its follow-ups. #9492's own cases still pass
unchanged.
**Test case:** create a table from the command line, tick it to be saved
in the editor's Variables view, leave the editor there, run `lua
myTable.later = "x"`, quit and reopen - `later` is still there. `ctest
-R 'XMLexportVariablesTest|PackageSelfUninstallTest'` covers both
halves; all 12 new cases were verified to fail against the unfixed
source.
Assisted-by: Claude:claude-opus-5
2026-08-07 10:14:45 +02:00
// onPanic() longjmp()s to the shared buf, so without a setjmp of our own
// that jump lands in whichever frame set it last - usually one that has
// already returned, taking the caller's scope down with it.
if ( setjmp ( buf ) ! = 0 ) {
qWarning ( ) < < " LuaInterface::getVars() WARNING - Lua panicked while reading the variables in; the variable tree is incomplete. " ;
return ;
}
2023-02-20 21:46:15 +00:00
lua_pushnil ( mL ) ;
2013-08-14 14:17:44 +02:00
depth = 0 ;
2023-05-11 07:44:16 +02:00
auto global = new TVar ( ) ;
global - > setName ( " _G " , LUA_TSTRING ) ;
global - > setValue ( " {} " , LUA_TTABLE ) ;
fix: two ways a profile save could lose or resurrect your data (#9704)
#### Brief overview of PR changes/additions
- Variables: the export skipped its refresh whenever the editor's
Variables view was on screen, so anything a script wrote into a saved
variable while it sat open was dropped from the save - including the
session's last save, which is taken with whatever view the editor was
left on. The variables are now read into a throwaway tree, which also
stops a save stranding the editor's variable search results.
- Packages: a save taken while a unit was still executing an item of a
package that had just been uninstalled wrote that package's items back
into the profile, where they returned as orphans the Package Manager
could not remove. The XML writers now skip what the units have queued
for a deferred delete, the module writer included - reloading a module
from a script used to write both the pre- and post-reload copies of its
items into the module file.
- `LuaInterface::getVars()` is now `setjmp`-guarded like every other
Lua-touching method there, so a panic cannot jump past the export's
scope with its variable tree and registry references still held.
#### Motivation for adding to Mudlet
Both are silent data loss in everyday use: quitting with the editor on
the Variables tab, and the `mpkg`/auto-updater shape of uninstalling a
package from a script.
#### Other info (issues closed, discussion etc)
From the 5.0 QA sweep, findings C13 and C14. The variables half re-opens
the loss that `20009c5ec` "fix: variables added while playing are no
longer lost when saving (#9492)" fixed, via the guard it added; the
packages half is the missing counterpart to the self-uninstall deferral
in `276e8bbfd` (#9383) and its follow-ups. #9492's own cases still pass
unchanged.
**Test case:** create a table from the command line, tick it to be saved
in the editor's Variables view, leave the editor there, run `lua
myTable.later = "x"`, quit and reopen - `later` is still there. `ctest
-R 'XMLexportVariablesTest|PackageSelfUninstallTest'` covers both
halves; all 12 new cases were verified to fail against the unfixed
source.
Assisted-by: Claude:claude-opus-5
2026-08-07 10:14:45 +02:00
releaseVariableReferences ( ) ;
2013-08-14 14:17:44 +02:00
varUnit - > clear ( ) ;
2023-05-11 07:44:16 +02:00
varUnit - > setBase ( global ) ;
varUnit - > addVariable ( global ) ;
iterateTable ( mL , LUA_GLOBALSINDEX , global , hide ) ;
2020-11-26 20:54:01 +00:00
// FIXME: possible to keep and report? qDebug()<<"took"<<t.elapsed()<<"to get variables in";
2013-08-14 14:17:44 +02:00
}