Skip to content

Commit b92c0c8

Browse files
committed
Fix stack manipulation in Premake's luaL_loadfilex override.
An example of code that did not work: ```lua local env = { ["foo"] = function(n) print(n) end } assert(pcall(loadfile("foo.lua", nil, env)) ```
1 parent db072b4 commit b92c0c8

File tree

4 files changed

+56
-6
lines changed

4 files changed

+56
-6
lines changed

src/host/lua_auxlib.c

+31-6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ LUALIB_API int luaL_loadfilex (lua_State* L, const char* filename, const char* m
2929
const char* script_dir;
3030
const char* test_name;
3131

32+
/* this function can be called with from 1 to 3 arguments on the stack,
33+
* the filename, the mode and an environment table */
34+
35+
int env = (!lua_isnone(L, 3) ? 1 : 0); /* 1 if there is an env or 0 if no 'env' */
3236
int bottom = lua_gettop(L);
3337
int z = !OKAY;
3438

@@ -60,11 +64,11 @@ LUALIB_API int luaL_loadfilex (lua_State* L, const char* filename, const char* m
6064
z = premake_load_embedded_script(L, test_name + 2); /* Skip over leading "$/" */
6165

6266
/* remove test_name */
63-
lua_remove(L, bottom + 1);
67+
lua_remove(L, -3);
6468
}
6569

6670
/* remove _SCRIPT_DIR */
67-
lua_remove(L, bottom);
71+
lua_remove(L, bottom + env);
6872
}
6973

7074
/* Try to locate the script on the filesystem */
@@ -100,7 +104,15 @@ LUALIB_API int luaL_loadfilex (lua_State* L, const char* filename, const char* m
100104
* script chunk on the stack. Turn these into a closure that will call my
101105
* wrapper below when the loaded script needs to be executed. */
102106
if (z == OKAY) {
103-
lua_pushcclosure(L, chunk_wrapper, 2);
107+
/* if we are called with an env, then our caller, luaB_loadfile, will
108+
* call load_aux, which sets up our env as the first up value via
109+
* lua_setupvalue, which would overwrite the one we are setting up here.
110+
* workaround this by pushing a nil value as our first up value */
111+
if (env) {
112+
lua_pushnil(L);
113+
lua_insert(L, -3);
114+
}
115+
lua_pushcclosure(L, chunk_wrapper, 2 + (env ? 1 : 0));
104116
}
105117
else if (z == LUA_ERRFILE) {
106118
lua_pushfstring(L, "cannot open %s: No such file or directory", filename);
@@ -124,9 +136,14 @@ static int chunk_wrapper(lua_State* L)
124136
const char* filename;
125137
char* ptr;
126138
int i, args;
139+
int upvalue_offset;
127140

128141
args = lua_gettop(L);
129142

143+
/* if the first up value is a table, then we have an env upvalue
144+
* and should take that into account by offsetting the rest of the up values */
145+
upvalue_offset = (lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) ? 1 : 0;
146+
130147
/* Remember the current _SCRIPT and working directory so I can
131148
* restore them after this new chunk has been run. */
132149

@@ -136,12 +153,12 @@ static int chunk_wrapper(lua_State* L)
136153

137154
/* Set the new _SCRIPT variable */
138155

139-
lua_pushvalue(L, lua_upvalueindex(1));
156+
lua_pushvalue(L, lua_upvalueindex(1 + upvalue_offset));
140157
lua_setglobal(L, "_SCRIPT");
141158

142159
/* And the new _SCRIPT_DIR variable (const cheating) */
143160

144-
filename = lua_tostring(L, lua_upvalueindex(1));
161+
filename = lua_tostring(L, lua_upvalueindex(1 + upvalue_offset));
145162
ptr = strrchr(filename, '/');
146163
if (ptr) *ptr = '\0';
147164
lua_pushlstring(L, filename, strlen(filename));
@@ -157,7 +174,15 @@ static int chunk_wrapper(lua_State* L)
157174
/* Move the function's arguments to the top of the stack and
158175
* execute the function created by luaL_loadfile() */
159176

160-
lua_pushvalue(L, lua_upvalueindex(2));
177+
lua_pushvalue(L, lua_upvalueindex(2 + upvalue_offset));
178+
179+
/* forward the env table to the closure as 1st upvalue */
180+
if (upvalue_offset) {
181+
lua_pushvalue(L, -1);
182+
lua_pushvalue(L, lua_upvalueindex(1));
183+
lua_setupvalue(L, -2, 1);
184+
}
185+
161186
for (i = 1; i <= args; ++i) {
162187
lua_pushvalue(L, i);
163188
}

tests/_tests.lua

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
return {
22
-- Base API tests
3+
"test_lua.lua",
34
"test_string.lua",
45
"base/test_aliasing.lua",
56
"base/test_binmodules.lua",

tests/test_lua.lua

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--
2+
-- tests/test_lua.lua
3+
-- Automated test suite for Lua base functions.
4+
-- Copyright (c) 2008 Jason Perkins and the Premake project
5+
--
6+
7+
local suite = test.declare("lua")
8+
9+
--
10+
-- loadfile with custom env
11+
--
12+
13+
function suite.loadfile_with_env()
14+
local file = path.join(_SCRIPT_DIR, "test_lua_loaded.lua")
15+
local value = 0
16+
local env = {
17+
["foobar"] = function(n) value = n end
18+
}
19+
local fn = assert(loadfile(file, nil, env))
20+
pcall(fn)
21+
test.isequal(10, value)
22+
end
23+

tests/test_lua_loaded.lua

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foobar(10)

0 commit comments

Comments
 (0)