Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scripting: Fix Lua 5.1/LuaJIT and Lua 5.2 problems #3376

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

nuive
Copy link
Contributor

@nuive nuive commented Dec 16, 2024

What's added:

-New option available for CMake USE_LUA option. Setting it to "JIT" will force CMake to look for LuaJIT when building.

What's fixed:

-Lua 5.1/JIT couldn't even load a script.
-Updated lua pairs/ipairs handling (Lua 5.1/JIT and 5.2 weren't able to manage things like mSTList iteration). Now pairs/ipairs work like Lua 5.4 (except for a lua_callk which stays lua_call).
-Custom objects show their propper names using Lua versions prior to 5.3.

What's not fixed:

I didn't find anything else needing a fix regarding specific Lua versions.

Copy link
Member

@endrift endrift left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tagging in @ahigerd for this. I'm not sure why some of these changes were made, and I'm concerned about the provenance of some of the code.

src/platform/cmake/FindLuaJIT.cmake Show resolved Hide resolved
Comment on lines 248 to 269
const char* luaL_tolstring (lua_State* lua, int idx, size_t* len) {
if (luaL_callmeta(lua, idx, "__tostring")) {
if (!lua_isstring(lua, -1)) {
luaL_error(lua, "'__tostring' must return a string");
}
} else {
switch (lua_type(lua, idx)) {
case LUA_TNUMBER:
case LUA_TSTRING:
lua_pushvalue(lua, idx);
break;
case LUA_TBOOLEAN:
lua_pushstring(lua, (lua_toboolean(lua, idx) ? "true" : "false"));
break;
case LUA_TNIL:
lua_pushliteral(lua, "nil");
break;
default:
luaL_getmetafield(lua, idx, "__name");
int tt = lua_type(lua, -1);
const char* name = (tt == LUA_TSTRING) ? lua_tostring(lua, -1) :
luaL_typename(lua, idx);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't copy source code directly from Lua, please. The license may permit this, but it'd be much better to replace the one call to this function with something like what Lua 5.1 does in its print function: it just calls tostring on the argument directly to coerce it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ironic: My print() shim calls luaL_tostring explicitly to avoid doing that.

That said, 5.1's tostring doesn't honor __name, so it becomes a question for @endrift: Is the better debugging/introspection worth the extra work?

src/script/engines/lua.c Show resolved Hide resolved
src/script/engines/lua.c Outdated Show resolved Hide resolved
src/script/engines/lua.c Show resolved Hide resolved
src/script/engines/lua.c Show resolved Hide resolved
CMakeLists.txt Outdated Show resolved Hide resolved
src/script/engines/lua.c Outdated Show resolved Hide resolved
@ahigerd
Copy link
Contributor

ahigerd commented Dec 23, 2024

I think the implementation of _luaLoad is missing some fenv fixes.

@nuive
Copy link
Contributor Author

nuive commented Dec 23, 2024

I think the implementation of _luaLoad is missing some fenv fixes.

Like what?

@ahigerd
Copy link
Contributor

ahigerd commented Dec 23, 2024

I think the implementation of _luaLoad is missing some fenv fixes.

Like what?

      // Create new _ENV
      lua_newtable(luaContext->lua);
  
      // Make the old _ENV the __index in the metatable
      lua_newtable(luaContext->lua);
      lua_pushliteral(luaContext->lua, "__index");
      lua_getupvalue(luaContext->lua, -4, 1);
      lua_rawset(luaContext->lua, -3);
  
      lua_pushliteral(luaContext->lua, "__newindex");
      lua_getupvalue(luaContext->lua, -4, 1);
      lua_rawset(luaContext->lua, -3);
  
      lua_setmetatable(luaContext->lua, -2);
  
      lua_pushliteral(luaContext->lua, "script");
      lua_newtable(luaContext->lua);

This is _ENV manipulation which means it needs to be handled using setfenv instead. It's probably a little nontrivial to do correctly but if you don't do it then require() won't behave correctly.

@nuive
Copy link
Contributor Author

nuive commented Dec 24, 2024

      // Create new _ENV
      lua_newtable(luaContext->lua);
  
      // Make the old _ENV the __index in the metatable
      lua_newtable(luaContext->lua);
      lua_pushliteral(luaContext->lua, "__index");
      lua_getupvalue(luaContext->lua, -4, 1);
      lua_rawset(luaContext->lua, -3);
  
      lua_pushliteral(luaContext->lua, "__newindex");
      lua_getupvalue(luaContext->lua, -4, 1);
      lua_rawset(luaContext->lua, -3);
  
      lua_setmetatable(luaContext->lua, -2);
  
      lua_pushliteral(luaContext->lua, "script");
      lua_newtable(luaContext->lua);

This is _ENV manipulation which means it needs to be handled using setfenv instead. It's probably a little nontrivial to do correctly but if you don't do it then require() won't behave correctly.

Yes, I know. In fact mGBA built with Lua 5.1 breaks if this is not handled propperly.

However if you look at the submited changes there's this:

		// Create new _ENV
		lua_newtable(luaContext->lua);

		// Make the old _ENV the __index in the metatable
		lua_newtable(luaContext->lua);
		lua_pushliteral(luaContext->lua, "__index");
#if LUA_VERSION_NUM >= 502
		lua_getupvalue(luaContext->lua, -4, 1);
#else
		lua_getfenv(luaContext->lua, -4);
#endif
		lua_rawset(luaContext->lua, -3);

		lua_pushliteral(luaContext->lua, "__newindex");
#if LUA_VERSION_NUM >= 502
		lua_getupvalue(luaContext->lua, -4, 1);
#else
		lua_getfenv(luaContext->lua, -4);
#endif
		lua_rawset(luaContext->lua, -3);

		lua_setmetatable(luaContext->lua, -2);

		lua_pushliteral(luaContext->lua, "script");
		lua_newtable(luaContext->lua);

		if (dirname[0]) {
			lua_pushliteral(luaContext->lua, "require");
			lua_pushstring(luaContext->lua, dirname);
			lua_pushcclosure(luaContext->lua, _luaRequireShim, 1);
			lua_rawset(luaContext->lua, -5);

			lua_pushliteral(luaContext->lua, "dir");
			lua_pushstring(luaContext->lua, dirname);
			lua_rawset(luaContext->lua, -3);
		}

		if (name[0] == '@') {
			lua_pushliteral(luaContext->lua, "path");
			lua_pushstring(luaContext->lua, &name[1]);
			lua_rawset(luaContext->lua, -3);
		}

		lua_rawset(luaContext->lua, -3);
#if LUA_VERSION_NUM >= 502
		lua_setupvalue(luaContext->lua, -2, 1);
#else
		lua_setfenv(luaContext->lua, -2);
#endif
		luaContext->func = luaL_ref(luaContext->lua, LUA_REGISTRYINDEX);
		return true;

Woudln't this be enough? Am I missing any (set/get)fenv?

@ahigerd
Copy link
Contributor

ahigerd commented Dec 24, 2024

Hmm... I may have misread the code. That might be correct after all.

@nuive nuive force-pushed the master branch 2 times, most recently from d48a3d9 to 964f505 Compare December 28, 2024 20:00
@nuive
Copy link
Contributor Author

nuive commented Dec 28, 2024

I think I made all requested changes...

lua_pop(lua, 1);
}

const char* _luaTolstring(lua_State* lua, int idx, size_t* len) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@endrift Bikeshed question: Should this function wrap luaL_tostring for 5.3+, or should we polyfill luaL_tostring so that 5.3+ code compiles under 5.1/5.2?

(Tangentially related, but not a task for this PR: Is there a particular reason why the internal functions in this file aren't declared static?)

luajit${LUAJIT_VERSION_MAJOR}.${LUAJIT_VERSION_MINOR}
luajit-${LUAJIT_VERSION_MAJOR}.${LUAJIT_VERSION_MINOR}
luajit.${LUAJIT_VERSION_MAJOR}.${LUAJIT_VERSION_MINOR}
lua51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does LuaJIT actually provide a lua51 soname? That seems weird...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. That is an inconsistency.

I'm using a win64 docker and MinGW's LuaJIT has same library names as standard Lua 5.1. In unix that's not true, names would be something like luajit51, luajit5.1...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, if that's the standard soname that's getting shipped for the package in MSYS2, then I guess it should stay as it is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I've added luajit51 variants in order to be able to build using unix systems. I don't know if I should left the luajit${LUAJIT_VERSION_MAJOR}${LUAJIT_VERSION_MINOR} variants into the list. I've found LuaJIT installations where the library was named something like libluajit2.1.so or something like that, but I supose those name conventions are customized. Should I remove ${LUAJIT_VERSION_MAJOR}${LUAJIT_VERSION_MINOR} variants from the list leaving only luajit51 and lua51 ones, or it's alrhigt like it is now? Currently possibly library names list has 12 items...

Copy link
Contributor

@ahigerd ahigerd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm basically satisfied?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scripting: Some issues building with Lua 5.1
4 participants