From 729bc44729731be0a92ceea071f9f43c2a2c992a Mon Sep 17 00:00:00 2001 From: Harold Cindy Date: Wed, 18 Jan 2023 10:00:13 -0400 Subject: [PATCH] Fix lua_*upvalue() when upvalue names aren't in debug info (#787) `lua_getupvalue()` and `lua_setupvalue()` don't behave as expected when working with Lua closure whose `Proto` has no debug info. The code currently uses `sizeupvalues` to do bounds checking of upvalue indices, but that's the size of the upvalue _names_ array. It will always be `0` if the `Proto` doesn't have debug info. This uses `nups` instead, and just returns `""` as the upvalue name if we don't have one, same as for C closures. Co-authored-by: Harold Cindy --- VM/src/lapi.cpp | 4 +++- tests/Conformance.test.cpp | 28 +++++++++++++++++++++++++++ tests/conformance/ndebug_upvalues.lua | 13 +++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/conformance/ndebug_upvalues.lua diff --git a/VM/src/lapi.cpp b/VM/src/lapi.cpp index d2091c6..1528aa3 100644 --- a/VM/src/lapi.cpp +++ b/VM/src/lapi.cpp @@ -1285,10 +1285,12 @@ static const char* aux_upvalue(StkId fi, int n, TValue** val) else { Proto* p = f->l.p; - if (!(1 <= n && n <= p->sizeupvalues)) + if (!(1 <= n && n <= p->nups)) // not a valid upvalue return NULL; TValue* r = &f->l.uprefs[n - 1]; *val = ttisupval(r) ? upvalue(r)->v : r; + if (!(1 <= n && n <= p->sizeupvalues)) // don't have a name for this upvalue + return ""; return getstr(p->upvalues[n - 1]); } } diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index d7340ce..13600cb 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -693,6 +693,34 @@ TEST_CASE("Debugger") CHECK(stephits > 100); // note; this will depend on number of instructions which can vary, so we just make sure the callback gets hit often } +TEST_CASE("NDebugGetUpValue") +{ + lua_CompileOptions copts = defaultOptions(); + copts.debugLevel = 0; + // Don't optimize away any upvalues + copts.optimizationLevel = 0; + + runConformance( + "ndebug_upvalues.lua", + nullptr, + [](lua_State* L) { + lua_checkstack(L, LUA_MINSTACK); + + // push the second frame's closure to the stack + lua_Debug ar = {}; + REQUIRE(lua_getinfo(L, 1, "f", &ar)); + + // get the first upvalue + const char* u = lua_getupvalue(L, -1, 1); + REQUIRE(u); + // upvalue name is unknown without debug info + CHECK(strcmp(u, "") == 0); + CHECK(lua_tointeger(L, -1) == 5); + lua_pop(L, 2); + }, + nullptr, &copts, /* skipCodegen */ false); +} + TEST_CASE("SameHash") { extern unsigned int luaS_hash(const char* str, size_t len); // internal function, declared in lstring.h - not exposed via lua.h diff --git a/tests/conformance/ndebug_upvalues.lua b/tests/conformance/ndebug_upvalues.lua new file mode 100644 index 0000000..bdb67f2 --- /dev/null +++ b/tests/conformance/ndebug_upvalues.lua @@ -0,0 +1,13 @@ +-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details +-- This tests that the lua_*upval() APIs work correctly even with debug info disabled +local foo = 5 +function clo_test() + -- so `foo` gets captured as an upval + print(foo) + -- yield so we can look at clo_test's upvalues + coroutine.yield() +end + +clo_test() + +return 'OK'