From eddc259578d277e57864b455d34cdcb3c9b82292 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Mon, 3 Jan 2022 12:48:52 +0000 Subject: [PATCH 01/20] Initial support for function dump and binary load. * Add Function::dump() to dump the compiled version of a function. * Add unsafe Chunk::into_function_allow_binary() which allows binary (i.e. precompiled) Lua. --- src/context.rs | 27 ++++++++++++++++++---- src/function.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ src/util.rs | 2 +- 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/context.rs b/src/context.rs index 18e0ed39..0969b255 100644 --- a/src/context.rs +++ b/src/context.rs @@ -856,10 +856,16 @@ impl<'lua> Context<'lua> { source: &[u8], name: Option<&CString>, env: Option>, + allow_binary: bool, ) -> Result> { unsafe { let _sg = StackGuard::new(self.state); assert_stack(self.state, 1); + let mode = if allow_binary { + cstr!("bt") + } else { + cstr!("t") + }; match if let Some(name) = name { loadbufferx( @@ -867,7 +873,7 @@ impl<'lua> Context<'lua> { source.as_ptr() as *const c_char, source.len(), name.as_ptr() as *const c_char, - cstr!("t"), + mode, ) } else { loadbufferx( @@ -875,7 +881,7 @@ impl<'lua> Context<'lua> { source.as_ptr() as *const c_char, source.len(), ptr::null(), - cstr!("t"), + mode, ) } { ffi::LUA_OK => { @@ -958,7 +964,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> { expression_source.extend(self.source); if let Ok(function) = self.context - .load_chunk(&expression_source, self.name.as_ref(), self.env.clone()) + .load_chunk(&expression_source, self.name.as_ref(), self.env.clone(), false) { function.call(()) } else { @@ -978,7 +984,20 @@ impl<'lua, 'a> Chunk<'lua, 'a> { /// This simply compiles the chunk without actually executing it. pub fn into_function(self) -> Result> { self.context - .load_chunk(self.source, self.name.as_ref(), self.env) + .load_chunk(self.source, self.name.as_ref(), self.env, false) + } + + /// Load this chunk into a regular `Function`. + /// + /// This simply compiles the chunk without actually executing it. + /// Unlike `into_function`, this method allows loading code previously + /// compiled and saved with `Function::dump` or `string.dump()`. + /// This method is unsafe because there is no check that the precompiled + /// Lua code is valid; if it is not this may cause a crash or other + /// undefined behaviour. + pub unsafe fn into_function_allow_binary(self) -> Result> { + self.context + .load_chunk(self.source, self.name.as_ref(), self.env, true) } } diff --git a/src/function.rs b/src/function.rs index 5dc2a888..991e5ab9 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,6 +1,8 @@ use std::os::raw::c_int; use std::ptr; +use libc::c_void; + use crate::error::{Error, Result}; use crate::ffi; use crate::types::LuaRef; @@ -161,4 +163,61 @@ impl<'lua> Function<'lua> { Ok(Function(lua.pop_ref())) } } + + /// Dumps the compiled representation of the function into a binary blob, + /// which can later be loaded using the unsafe Chunk::into_function_allow_binary(). + /// + /// # Examples + /// + /// ``` + /// # use rlua::{Lua, Function, Result}; + /// # fn main() -> Result<()> { + /// # Lua::new().context(|lua_context| { + /// let add2: Function = lua_context.load(r#" + /// function(a) + /// return a + 2 + /// end + /// "#).eval()?; + /// + /// let dumped = add2.dump()?; + /// + /// let reloaded = unsafe { + /// lua_context.load(&dumped) + /// .into_function_allow_binary()? + /// }; + /// assert_eq!(reloaded.call::<_, u32>(7)?, 7+2); + /// + /// # Ok(()) + /// # }) + /// # } + /// ``` + pub fn dump(&self) -> Result> { + unsafe extern "C" fn writer( + _state: *mut ffi::lua_State, + p: *const c_void, + sz: usize, + ud: *mut c_void) -> c_int { + let input_slice = std::slice::from_raw_parts(p as *const u8, sz); + let vec = &mut *(ud as *mut Vec); + vec.extend_from_slice(input_slice); + 0 + } + let lua = self.0.lua; + let mut bytes = Vec::new(); + unsafe { + let _sg = StackGuard::new(lua.state); + check_stack(lua.state, 1)?; + let bytes_ptr = &mut bytes as *mut _; + protect_lua_closure(lua.state, 0, 0, |state| { + lua.push_ref(&self.0); + let dump_result = ffi::lua_dump( + state, + writer, + bytes_ptr as *mut c_void); + // It can only return an error from our writer. + debug_assert_eq!(dump_result, 0); + })?; + } + Ok(bytes) + } } diff --git a/src/util.rs b/src/util.rs index b425ee1b..984756fd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -96,7 +96,7 @@ pub unsafe fn protect_lua( // given function return type is not the return value count, instead the inner function return // values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does // not call checkstack. Provided function must *not* panic, and since it will generally be -// lonjmping, should not contain any values that implement Drop. +// longjmping, should not contain any values that implement Drop. pub unsafe fn protect_lua_closure( state: *mut ffi::lua_State, nargs: c_int, From 05c6285291e0d0497e07c59bb38e4b76828781f5 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Mon, 3 Jan 2022 16:01:33 +0000 Subject: [PATCH 02/20] Fix dump prototype and implement for other Lua versions. --- src/function.rs | 7 ++++--- src/util.rs | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/function.rs b/src/function.rs index 991e5ab9..7c7715be 100644 --- a/src/function.rs +++ b/src/function.rs @@ -7,7 +7,7 @@ use crate::error::{Error, Result}; use crate::ffi; use crate::types::LuaRef; use crate::util::{ - assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, rotate, StackGuard, + assert_stack, check_stack, dump, error_traceback, pop_error, protect_lua_closure, rotate, StackGuard, }; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti}; @@ -210,10 +210,11 @@ impl<'lua> Function<'lua> { let bytes_ptr = &mut bytes as *mut _; protect_lua_closure(lua.state, 0, 0, |state| { lua.push_ref(&self.0); - let dump_result = ffi::lua_dump( + let dump_result = dump( state, writer, - bytes_ptr as *mut c_void); + bytes_ptr as *mut c_void, + 0); // It can only return an error from our writer. debug_assert_eq!(dump_result, 0); })?; diff --git a/src/util.rs b/src/util.rs index 984756fd..4ced6ad3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -771,6 +771,20 @@ pub unsafe fn traceback( ffi::lua_pushstring(push_state, msg); } +#[cfg(any(rlua_lua53, rlua_lua54))] +pub use ffi::lua_dump as dump; + +#[cfg(rlua_lua51)] +pub unsafe fn dump( + state: *mut ffi::lua_State, + writer: ffi::lua_Writer, + data: *mut c_void, + _strip: c_int, +) -> c_int +{ + ffi::lua_dump(state, writer, data) +} + // In the context of a lua callback, this will call the given function and if the given function // returns an error, *or if the given function panics*, this will result in a call to lua_error (a // longjmp). The error or panic is wrapped in such a way that when calling pop_error back on From f6c47202c3ffec9f966d37db9ddefccc8c229815 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Mon, 3 Jan 2022 17:28:53 +0000 Subject: [PATCH 03/20] Make wrappers for load and related functions. These wrappers prevent loading binary Lua chunks. --- src/lua.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/util.rs | 11 ++++++ 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 086e925e..7bb55c83 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -19,8 +19,8 @@ use crate::hook::{hook_proc, Debug, HookTriggers}; use crate::markers::NoRefUnwindSafe; use crate::types::Callback; use crate::util::{ - assert_stack, init_error_registry, protect_lua_closure, push_globaltable, requiref, safe_pcall, - safe_xpcall, userdata_destructor, + assert_stack, init_error_registry, protect_lua_closure, push_globaltable, requiref, + dostring, safe_pcall, safe_xpcall, userdata_destructor, }; bitflags! { @@ -64,8 +64,10 @@ bitflags! { /// Flags describing the set of lua modules to load. pub struct InitFlags: u32 { const PCALL_WRAPPERS = 0x1; + const LOAD_WRAPPERS = 0x1; - const DEFAULT = InitFlags::PCALL_WRAPPERS.bits; + const DEFAULT = InitFlags::PCALL_WRAPPERS.bits | + InitFlags::LOAD_WRAPPERS.bits; const NONE = 0; } } @@ -568,7 +570,6 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); // Override pcall and xpcall with versions that cannot be used to catch rust panics. - if init_flags.contains(InitFlags::PCALL_WRAPPERS) { push_globaltable(state); @@ -583,6 +584,107 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { ffi::lua_pop(state, 1); } + // Override dofile, load, and loadfile with versions that won't load + // binary files. + if init_flags.contains(InitFlags::LOAD_WRAPPERS) { + // These are easier to override in Lua. + #[cfg(any(rlua_lua53, rlua_lua54))] + let wrapload = r#" + do + -- load(chunk [, chunkname [, mode [, env]]]) + local real_load = load + load = function(func, chunkname, mode, env) + return real_load(func, chunkname, "t", env) + end + + -- loadfile ([filename [, mode [, env]]]) + local real_loadfile = loadfile + loadfile = function(filename, mode, env) + return real_loadfile(filename, "t", env) + end + + -- dofile([filename]) + local real_dofile = dofile + dofile = function(filename) + -- Note: this is the wrapped loadfile above + local chunk = loadfile(filename) + if chunk then + return chunk() + end + end + end + "#; + #[cfg(rlua_lua51)] + let wrapload = r#" + do + -- load(chunk [, chunkname]) + local real_load = load + -- safe type() in case user code replaces it + local real_type = type + local real_error = error + load = function(func, chunkname) + local first_chunk = true + local wrap_func = function() + if not first_chunk then + return func() + else + local data = func() + if data == nil then return nil end + assert(real_type(data) == "string") + if data:len() > 0 then + if data:byte(1) == 27 then + real_error("rlua load: loading binary chunks is not allowed") + end + first_chunk = false + end + return data + end + end + return real_load(wrapfunc, chunkname) + end + + -- loadstring(string [, chunkname]) + local real_loadstring = loadstring + loadstring = function(string, chunkname) + if string:byte(1) == 27 then + -- This is a binary chunk, so disallow + real_error("rlua loadstring: loading binary chunks is not allowed") + else + return real_loadstring(string, chunkname) + end + end + + -- loadfile ([filename]) + local real_loadfile = loadfile + loadfile = function(filename) + local f, err = real_io_open(filename, "rb") + if not f then + return nil, err + end + local first_chunk = true + local func = function() + return f:read(4096) + end + -- Note: the safe load from above. + return load(func, filename) + end + + -- dofile([filename]) + local real_dofile = dofile + dofile = function(filename) + -- Note: this is the wrapped loadfile above + local chunk = loadfile(filename) + if chunk then + return chunk() + end + end + end + "#; + + let result = dostring(state, wrapload); + assert_eq!(result, 0); + } + // Create ref stack thread and place it in the registry to prevent it from being garbage // collected. diff --git a/src/util.rs b/src/util.rs index 4ced6ad3..1bc81f26 100644 --- a/src/util.rs +++ b/src/util.rs @@ -696,6 +696,17 @@ pub unsafe fn loadbufferx( ffi::luaL_loadbuffer(state, buf, size, name) } +pub unsafe fn dostring( + state: *mut ffi::lua_State, + s: &str, +) -> c_int { + if loadbufferx(state, s.as_ptr() as *const c_char, s.len(), cstr!(""), cstr!("t")) == 0 { + ffi::lua_pcall(state, 0, ffi::LUA_MULTRET, 0) + } else { + 0 + } +} + #[cfg(any(rlua_lua53, rlua_lua54))] // Like luaL_requiref but doesn't leave the module on the stack. pub unsafe fn requiref( From cf30a028c2d8d888405e649397ff6fc378871fee Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Tue, 4 Jan 2022 08:14:39 +0000 Subject: [PATCH 04/20] Add tests for `load`. Fix a problem with the wrapper: passing `nil` as the optional arguments turns out to be different to passing fewer arguments. --- src/lua.rs | 7 ++-- tests/tests.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 7bb55c83..5d63969b 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -593,8 +593,11 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { do -- load(chunk [, chunkname [, mode [, env]]]) local real_load = load - load = function(func, chunkname, mode, env) - return real_load(func, chunkname, "t", env) + load = function(...) + local args = table.pack(...) + args[3] = "t" + if args.n < 3 then args.n = 3 end + return real_load(table.unpack(args)) end -- loadfile ([filename [, mode [, env]]]) diff --git a/tests/tests.rs b/tests/tests.rs index f8ebb0cc..744f7e09 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use std::{error, f32, f64, fmt}; use rlua::{ - Error, ExternalError, Function, /* InitFlags, */ Lua, Nil, Result, StdLib, String, Table, + Error, ExternalError, Function, InitFlags, Lua, Nil, Result, StdLib, String, Table, UserData, Value, Variadic, }; @@ -406,6 +406,97 @@ fn test_error_nopcall_wrap() { } */ +#[test] +fn test_load_wrappers() { + Lua::new().context(|lua| { + let globals = lua.globals(); + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + lua.load( + r#" + assert(type(binchunk) == "string") + chunk = load(binchunk, "bad", "bt") + assert(chunk == nil) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + lua.load( + r#" + chunk = load("x = x + 4") + assert(chunk ~= nil) + chunk() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + }); +} + +#[test] +fn test_no_load_wrappers() { + unsafe { Lua::unsafe_new_with_flags(StdLib::ALL_NO_DEBUG, InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS).context(|lua| { + let globals = lua.globals(); + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + lua.load( + r#" + assert(type(binchunk) == "string") + chunk = load(binchunk, "fail", "t") + assert(chunk == nil) + chunk = load(binchunk, "good", "bt") + assert(chunk ~= nil) + chunk() + chunk = load("x = x + 3") + chunk() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + })}; +} + #[test] fn test_result_conversions() { Lua::new().context(|lua| { From 75f901c69162a1ba791227e14c532dce9f9e947d Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sat, 15 Jan 2022 11:43:28 +0000 Subject: [PATCH 05/20] Add test for loadfile wrapper. --- src/function.rs | 2 +- tests/tests.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/src/function.rs b/src/function.rs index 7c7715be..7786de26 100644 --- a/src/function.rs +++ b/src/function.rs @@ -212,7 +212,7 @@ impl<'lua> Function<'lua> { lua.push_ref(&self.0); let dump_result = dump( state, - writer, + Some(writer), bytes_ptr as *mut c_void, 0); // It can only return an error from our writer. diff --git a/tests/tests.rs b/tests/tests.rs index 744f7e09..76379518 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,3 +1,4 @@ +use bstr::BString; use std::iter::FromIterator; use std::panic::catch_unwind; use std::sync::Arc; @@ -497,6 +498,112 @@ fn test_no_load_wrappers() { })}; } +#[test] +fn test_loadfile_wrappers() { + let mut tmppath = std::env::temp_dir(); + tmppath.push("test_loadfile_wrappers.lua"); + + Lua::new().context(|lua| { + let globals = lua.globals(); + globals.set("filename", tmppath.to_str().unwrap()).unwrap(); + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + let binchunk = globals.get::<_, BString>("binchunk").unwrap(); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + std::fs::write(&tmppath, binchunk).unwrap(); + lua.load( + r#" + chunk = loadfile(filename) + assert(chunk == nil) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + std::fs::write(&tmppath, "x = x + 4").unwrap(); + lua.load( + r#" + chunk = loadfile(filename, "bt", _ENV) + assert(chunk ~= nil) + chunk() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + }); +} + +#[test] +fn test_no_loadfile_wrappers() { + let mut tmppath = std::env::temp_dir(); + let mut tmppath2 = tmppath.clone(); + tmppath.push("test_no_loadfile_wrappers.lua"); + tmppath2.push("test_no_loadfile_wrappers2.lua"); + + unsafe { Lua::unsafe_new_with_flags(StdLib::ALL_NO_DEBUG, InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS).context(|lua| { + let globals = lua.globals(); + globals.set("filename", tmppath.to_str().unwrap()).unwrap(); + globals.set("filename2", tmppath2.to_str().unwrap()).unwrap(); + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + let binchunk = globals.get::<_, BString>("binchunk").unwrap(); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + std::fs::write(&tmppath, binchunk).unwrap(); + std::fs::write(&tmppath2, "x = x + 3").unwrap(); + lua.load( + r#" + chunk = loadfile(filename, "t") + assert(chunk == nil) + chunk = loadfile(filename, "bt") + assert(chunk ~= nil) + chunk() + chunk = loadfile(filename2) + chunk() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + })}; +} + #[test] fn test_result_conversions() { Lua::new().context(|lua| { From bdb031120087b4510e7585a063a1aac289d1232f Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sat, 15 Jan 2022 11:46:39 +0000 Subject: [PATCH 06/20] Cargo fmt. --- src/context.rs | 10 +++-- src/function.rs | 12 +++--- src/lua.rs | 4 +- src/util.rs | 17 +++++--- tests/tests.rs | 112 +++++++++++++++++++++++++++--------------------- 5 files changed, 86 insertions(+), 69 deletions(-) diff --git a/src/context.rs b/src/context.rs index 0969b255..5f5ff0f0 100644 --- a/src/context.rs +++ b/src/context.rs @@ -962,10 +962,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> { // actual lua repl does. let mut expression_source = b"return ".to_vec(); expression_source.extend(self.source); - if let Ok(function) = - self.context - .load_chunk(&expression_source, self.name.as_ref(), self.env.clone(), false) - { + if let Ok(function) = self.context.load_chunk( + &expression_source, + self.name.as_ref(), + self.env.clone(), + false, + ) { function.call(()) } else { self.call(()) diff --git a/src/function.rs b/src/function.rs index 7786de26..cb98b756 100644 --- a/src/function.rs +++ b/src/function.rs @@ -7,7 +7,8 @@ use crate::error::{Error, Result}; use crate::ffi; use crate::types::LuaRef; use crate::util::{ - assert_stack, check_stack, dump, error_traceback, pop_error, protect_lua_closure, rotate, StackGuard, + assert_stack, check_stack, dump, error_traceback, pop_error, protect_lua_closure, rotate, + StackGuard, }; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti}; @@ -196,7 +197,8 @@ impl<'lua> Function<'lua> { _state: *mut ffi::lua_State, p: *const c_void, sz: usize, - ud: *mut c_void) -> c_int { + ud: *mut c_void, + ) -> c_int { let input_slice = std::slice::from_raw_parts(p as *const u8, sz); let vec = &mut *(ud as *mut Vec); vec.extend_from_slice(input_slice); @@ -210,11 +212,7 @@ impl<'lua> Function<'lua> { let bytes_ptr = &mut bytes as *mut _; protect_lua_closure(lua.state, 0, 0, |state| { lua.push_ref(&self.0); - let dump_result = dump( - state, - Some(writer), - bytes_ptr as *mut c_void, - 0); + let dump_result = dump(state, Some(writer), bytes_ptr as *mut c_void, 0); // It can only return an error from our writer. debug_assert_eq!(dump_result, 0); })?; diff --git a/src/lua.rs b/src/lua.rs index 5d63969b..3ac7b9df 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -19,8 +19,8 @@ use crate::hook::{hook_proc, Debug, HookTriggers}; use crate::markers::NoRefUnwindSafe; use crate::types::Callback; use crate::util::{ - assert_stack, init_error_registry, protect_lua_closure, push_globaltable, requiref, - dostring, safe_pcall, safe_xpcall, userdata_destructor, + assert_stack, dostring, init_error_registry, protect_lua_closure, push_globaltable, requiref, + safe_pcall, safe_xpcall, userdata_destructor, }; bitflags! { diff --git a/src/util.rs b/src/util.rs index 1bc81f26..1b0ff671 100644 --- a/src/util.rs +++ b/src/util.rs @@ -696,11 +696,15 @@ pub unsafe fn loadbufferx( ffi::luaL_loadbuffer(state, buf, size, name) } -pub unsafe fn dostring( - state: *mut ffi::lua_State, - s: &str, -) -> c_int { - if loadbufferx(state, s.as_ptr() as *const c_char, s.len(), cstr!(""), cstr!("t")) == 0 { +pub unsafe fn dostring(state: *mut ffi::lua_State, s: &str) -> c_int { + if loadbufferx( + state, + s.as_ptr() as *const c_char, + s.len(), + cstr!(""), + cstr!("t"), + ) == 0 + { ffi::lua_pcall(state, 0, ffi::LUA_MULTRET, 0) } else { 0 @@ -791,8 +795,7 @@ pub unsafe fn dump( writer: ffi::lua_Writer, data: *mut c_void, _strip: c_int, -) -> c_int -{ +) -> c_int { ffi::lua_dump(state, writer, data) } diff --git a/tests/tests.rs b/tests/tests.rs index 76379518..9a23f05a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -5,8 +5,8 @@ use std::sync::Arc; use std::{error, f32, f64, fmt}; use rlua::{ - Error, ExternalError, Function, InitFlags, Lua, Nil, Result, StdLib, String, Table, - UserData, Value, Variadic, + Error, ExternalError, Function, InitFlags, Lua, Nil, Result, StdLib, String, Table, UserData, + Value, Variadic, }; #[test] @@ -457,31 +457,36 @@ fn test_load_wrappers() { #[test] fn test_no_load_wrappers() { - unsafe { Lua::unsafe_new_with_flags(StdLib::ALL_NO_DEBUG, InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS).context(|lua| { - let globals = lua.globals(); - lua.load( - r#" + unsafe { + Lua::unsafe_new_with_flags( + StdLib::ALL_NO_DEBUG, + InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS, + ) + .context(|lua| { + let globals = lua.globals(); + lua.load( + r#" x = 0 function incx() x = x + 1 end binchunk = string.dump(incx) "#, - ) - .exec() - .unwrap(); - assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); - lua.load( - r#" + lua.load( + r#" incx() "#, - ) - .exec() - .unwrap(); - assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); - lua.load( - r#" + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + lua.load( + r#" assert(type(binchunk) == "string") chunk = load(binchunk, "fail", "t") assert(chunk == nil) @@ -491,11 +496,12 @@ fn test_no_load_wrappers() { chunk = load("x = x + 3") chunk() "#, - ) - .exec() - .unwrap(); - assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); - })}; + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + }) + }; } #[test] @@ -559,36 +565,43 @@ fn test_no_loadfile_wrappers() { tmppath.push("test_no_loadfile_wrappers.lua"); tmppath2.push("test_no_loadfile_wrappers2.lua"); - unsafe { Lua::unsafe_new_with_flags(StdLib::ALL_NO_DEBUG, InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS).context(|lua| { - let globals = lua.globals(); - globals.set("filename", tmppath.to_str().unwrap()).unwrap(); - globals.set("filename2", tmppath2.to_str().unwrap()).unwrap(); - lua.load( - r#" + unsafe { + Lua::unsafe_new_with_flags( + StdLib::ALL_NO_DEBUG, + InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS, + ) + .context(|lua| { + let globals = lua.globals(); + globals.set("filename", tmppath.to_str().unwrap()).unwrap(); + globals + .set("filename2", tmppath2.to_str().unwrap()) + .unwrap(); + lua.load( + r#" x = 0 function incx() x = x + 1 end binchunk = string.dump(incx) "#, - ) - .exec() - .unwrap(); - assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); - let binchunk = globals.get::<_, BString>("binchunk").unwrap(); + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + let binchunk = globals.get::<_, BString>("binchunk").unwrap(); - lua.load( - r#" + lua.load( + r#" incx() "#, - ) - .exec() - .unwrap(); - assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); - std::fs::write(&tmppath, binchunk).unwrap(); - std::fs::write(&tmppath2, "x = x + 3").unwrap(); - lua.load( - r#" + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + std::fs::write(&tmppath, binchunk).unwrap(); + std::fs::write(&tmppath2, "x = x + 3").unwrap(); + lua.load( + r#" chunk = loadfile(filename, "t") assert(chunk == nil) chunk = loadfile(filename, "bt") @@ -597,11 +610,12 @@ fn test_no_loadfile_wrappers() { chunk = loadfile(filename2) chunk() "#, - ) - .exec() - .unwrap(); - assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); - })}; + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + }) + }; } #[test] From 26e324355c793ab4aa0a684e84767f01130f1544 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sat, 15 Jan 2022 11:47:29 +0000 Subject: [PATCH 07/20] Move code format checks after build/test. --- .circleci/config.yml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3c6dc579..e14ef9bb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,18 +20,18 @@ jobs: - restore_cache: keys: - cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - name: Check Formatting - command: | - rustup component add rustfmt - rustfmt --version - cargo fmt --all -- --check --color=auto - run: name: Build all targets command: cargo build --all --all-targets - run: name: Run all tests command: cargo test --all + - run: + name: Check Formatting + command: | + rustup component add rustfmt + rustfmt --version + cargo fmt --all -- --check --color=auto - save_cache: paths: - /usr/local/cargo/registry @@ -58,18 +58,18 @@ jobs: - restore_cache: keys: - cargo-cache-lua53-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - name: Check Formatting - command: | - rustup component add rustfmt - rustfmt --version - cargo fmt --all -- --check --color=auto - run: name: Build all targets command: cargo build --no-default-features --features=builtin-lua53 --all --all-targets - run: name: Run all tests command: cargo test --no-default-features --features=builtin-lua53 --all + - run: + name: Check Formatting + command: | + rustup component add rustfmt + rustfmt --version + cargo fmt --all -- --check --color=auto - save_cache: paths: - /usr/local/cargo/registry @@ -102,18 +102,18 @@ jobs: - restore_cache: keys: - cargo-cache-lua51-{{ arch }}-{{ checksum "Cargo.lock" }} - - run: - name: Check Formatting - command: | - rustup component add rustfmt - rustfmt --version - cargo fmt --all -- --check --color=auto - run: name: Build all targets command: cargo build --no-default-features --features=system-lua51 --all --all-targets - run: name: Run all tests command: cargo test --no-default-features --features=system-lua51 --all + - run: + name: Check Formatting + command: | + rustup component add rustfmt + rustfmt --version + cargo fmt --all -- --check --color=auto - save_cache: paths: - /usr/local/cargo/registry From e46794ea0b540fcb17ced8d6d4bc869b57c59d7b Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sat, 15 Jan 2022 14:14:04 +0000 Subject: [PATCH 08/20] Fix the loadfile wrapper to allow for variable args. --- src/lua.rs | 15 ++++++++++----- tests/tests.rs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 3ac7b9df..369140ae 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -602,8 +602,11 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { -- loadfile ([filename [, mode [, env]]]) local real_loadfile = loadfile - loadfile = function(filename, mode, env) - return real_loadfile(filename, "t", env) + loadfile = function(...) + local args = table.pack(...) + args[2] = "t" + if args.n < 2 then args.n = 2 end + return real_loadfile(table.unpack(args)) end -- dofile([filename]) @@ -648,12 +651,14 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { -- loadstring(string [, chunkname]) local real_loadstring = loadstring - loadstring = function(string, chunkname) - if string:byte(1) == 27 then + loadstring = function(s, chunkname) + if type(s) ~= "string" then + real_error("rlua loadstring: string expected.") + elseif s:byte(1) == 27 then -- This is a binary chunk, so disallow real_error("rlua loadstring: loading binary chunks is not allowed") else - return real_loadstring(string, chunkname) + return real_loadstring(s, chunkname) end end diff --git a/tests/tests.rs b/tests/tests.rs index 9a23f05a..715c5650 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -547,7 +547,7 @@ fn test_loadfile_wrappers() { std::fs::write(&tmppath, "x = x + 4").unwrap(); lua.load( r#" - chunk = loadfile(filename, "bt", _ENV) + chunk = loadfile(filename) assert(chunk ~= nil) chunk() "#, From 14c941dc68bc842ebc18d04e0fdd680aef4c4cb5 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sun, 16 Jan 2022 11:31:44 +0000 Subject: [PATCH 09/20] Fixes for the load/loadfile wrappers in Lua 5.1. Also fix an incorrect error return in dostring(). --- crates/rlua-lua51-sys/src/lib.rs | 6 +++--- src/lua.rs | 10 +++++++-- src/util.rs | 8 ++++---- tests/tests.rs | 35 ++++++++++++++++++++++++++------ 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/crates/rlua-lua51-sys/src/lib.rs b/crates/rlua-lua51-sys/src/lib.rs index 4fe7f43e..db811f47 100644 --- a/crates/rlua-lua51-sys/src/lib.rs +++ b/crates/rlua-lua51-sys/src/lib.rs @@ -68,7 +68,7 @@ pub use { pub use { bindings::lua_Alloc, bindings::lua_CFunction, bindings::lua_Debug, bindings::lua_Integer, - bindings::lua_Number, bindings::lua_State, + bindings::lua_Number, bindings::lua_State, bindings::lua_Writer, }; /* @@ -143,8 +143,8 @@ pub use bindings::lua_gc; ** miscellaneous functions */ pub use { - bindings::lua_concat, bindings::lua_error, bindings::lua_getallocf, bindings::lua_next, - bindings::lua_setallocf, + bindings::lua_concat, bindings::lua_dump, bindings::lua_error, bindings::lua_getallocf, + bindings::lua_next, bindings::lua_setallocf, }; /* diff --git a/src/lua.rs b/src/lua.rs index 369140ae..8b986913 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -625,7 +625,7 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { do -- load(chunk [, chunkname]) local real_load = load - -- safe type() in case user code replaces it + -- save type() in case user code replaces it local real_type = type local real_error = error load = function(func, chunkname) @@ -646,7 +646,7 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { return data end end - return real_load(wrapfunc, chunkname) + return real_load(wrap_func, chunkname) end -- loadstring(string [, chunkname]) @@ -664,6 +664,7 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { -- loadfile ([filename]) local real_loadfile = loadfile + local real_io_open = io.open loadfile = function(filename) local f, err = real_io_open(filename, "rb") if not f then @@ -690,6 +691,11 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { "#; let result = dostring(state, wrapload); + if result != 0 { + use std::ffi::CStr; + let errmsg = ffi::lua_tostring(state, -1); + eprintln!("Internal error running setup code: {:?}", CStr::from_ptr(errmsg)); + } assert_eq!(result, 0); } diff --git a/src/util.rs b/src/util.rs index 1b0ff671..9a8deb9a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -697,17 +697,17 @@ pub unsafe fn loadbufferx( } pub unsafe fn dostring(state: *mut ffi::lua_State, s: &str) -> c_int { - if loadbufferx( + let load_result = loadbufferx( state, s.as_ptr() as *const c_char, s.len(), cstr!(""), cstr!("t"), - ) == 0 - { + ); + if load_result == ffi::LUA_OK { ffi::lua_pcall(state, 0, ffi::LUA_MULTRET, 0) } else { - 0 + load_result } } diff --git a/tests/tests.rs b/tests/tests.rs index 715c5650..020c4e66 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -435,7 +435,13 @@ fn test_load_wrappers() { lua.load( r#" assert(type(binchunk) == "string") - chunk = load(binchunk, "bad", "bt") + local binchunk_copy = binchunk + chunk = load(function () + local result = binchunk_copy + binchunk_copy = nil + return result + end, "bad", "bt") + print(chunk) assert(chunk == nil) "#, ) @@ -444,7 +450,13 @@ fn test_load_wrappers() { assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); lua.load( r#" - chunk = load("x = x + 4") + local s = "x = x + 4" + local function loader() + local result = s + s = nil + return result + end + chunk = load(loader) assert(chunk ~= nil) chunk() "#, @@ -488,12 +500,23 @@ fn test_no_load_wrappers() { lua.load( r#" assert(type(binchunk) == "string") - chunk = load(binchunk, "fail", "t") - assert(chunk == nil) - chunk = load(binchunk, "good", "bt") + assert(binchunk:byte(1) == 27) + local stringsource = binchunk + local loader = function () + local result = stringsource + stringsource = nil + return result + end + if _VERSION ~= "Lua 5.1" then + -- Lua 5.1 doesn't support the mode parameter. + chunk = load(binchunk, "fail", "t") + assert(chunk == nil) + end + chunk = load(loader, "good", "bt") assert(chunk ~= nil) chunk() - chunk = load("x = x + 3") + stringsource = "x = x + 3" + chunk = load(loader) chunk() "#, ) From 98e65e3723dd03d385256f813ebc13c655ae3990 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sun, 16 Jan 2022 11:38:37 +0000 Subject: [PATCH 10/20] Fix the loadfile test for Lua 5.1. --- tests/tests.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index 020c4e66..aa19083e 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -625,8 +625,12 @@ fn test_no_loadfile_wrappers() { std::fs::write(&tmppath2, "x = x + 3").unwrap(); lua.load( r#" - chunk = loadfile(filename, "t") - assert(chunk == nil) + if _VERSION ~= "Lua 5.1" then + -- Lua 5.1 doesn't have the mode argument, so is + -- effectively always "bt". + chunk = loadfile(filename, "t") + assert(chunk == nil) + end chunk = loadfile(filename, "bt") assert(chunk ~= nil) chunk() From 773c049a4e81481f3556e854c11dbc2c6a3d4043 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sun, 16 Jan 2022 11:38:55 +0000 Subject: [PATCH 11/20] Cargo fmt. --- src/lua.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lua.rs b/src/lua.rs index 8b986913..d79705ee 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -694,7 +694,10 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { if result != 0 { use std::ffi::CStr; let errmsg = ffi::lua_tostring(state, -1); - eprintln!("Internal error running setup code: {:?}", CStr::from_ptr(errmsg)); + eprintln!( + "Internal error running setup code: {:?}", + CStr::from_ptr(errmsg) + ); } assert_eq!(result, 0); } From c9861cc6c19982664cb1216cbde69cbc51cd0d69 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sun, 16 Jan 2022 15:00:26 +0000 Subject: [PATCH 12/20] Test dofile. Fix dofile: raise an error instead of returning nil. --- src/lua.rs | 2 + tests/tests.rs | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/src/lua.rs b/src/lua.rs index d79705ee..c6bc70d4 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -685,6 +685,8 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { local chunk = loadfile(filename) if chunk then return chunk() + else + real_error("rlua dofile: attempt to load bytecode") end end end diff --git a/tests/tests.rs b/tests/tests.rs index aa19083e..14df5b7f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -645,6 +645,119 @@ fn test_no_loadfile_wrappers() { }; } +#[test] +fn test_dofile_wrappers() { + let mut tmppath = std::env::temp_dir(); + tmppath.push("test_dofile_wrappers.lua"); + + Lua::new().context(|lua| { + let globals = lua.globals(); + globals.set("filename", tmppath.to_str().unwrap()).unwrap(); + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + let binchunk = globals.get::<_, BString>("binchunk").unwrap(); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + std::fs::write(&tmppath, binchunk).unwrap(); + lua.load( + r#" + ok, err = pcall(dofile, filename) + assert(not ok) + assert(err:match("rlua dofile: attempt to load bytecode")) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + std::fs::write(&tmppath, "x = x + 4").unwrap(); + lua.load( + r#" + ok, ret = pcall(dofile, filename) + assert(ok) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + }); +} + +#[test] +fn test_no_dofile_wrappers() { + let mut tmppath = std::env::temp_dir(); + tmppath.push("test_dofile_wrappers.lua"); + + unsafe { + Lua::unsafe_new_with_flags( + StdLib::ALL_NO_DEBUG, + InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS, + ) + .context(|lua| { + let globals = lua.globals(); + globals.set("filename", tmppath.to_str().unwrap()).unwrap(); + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + let binchunk = globals.get::<_, BString>("binchunk").unwrap(); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + std::fs::write(&tmppath, binchunk).unwrap(); + lua.load( + r#" + ok, ret = pcall(dofile, filename) + assert(ok) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 2); + std::fs::write(&tmppath, "x = x + 4").unwrap(); + lua.load( + r#" + ok, ret = pcall(dofile, filename) + assert(ok) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 6); + }); + } +} + #[test] fn test_result_conversions() { Lua::new().context(|lua| { From 9bff2138d7265c22575bf4c8320793cc6bad5305 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sun, 16 Jan 2022 16:12:33 +0000 Subject: [PATCH 13/20] Fix dofile error handling for Lua 5.3/5.4. --- src/lua.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lua.rs b/src/lua.rs index c6bc70d4..c2e121ae 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -602,6 +602,7 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { -- loadfile ([filename [, mode [, env]]]) local real_loadfile = loadfile + local real_error = error loadfile = function(...) local args = table.pack(...) args[2] = "t" @@ -616,6 +617,8 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { local chunk = loadfile(filename) if chunk then return chunk() + else + real_error("rlua dofile: attempt to load bytecode") end end end From ba89fa689a119d192552a8df436b422028ea915a Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sun, 16 Jan 2022 16:30:37 +0000 Subject: [PATCH 14/20] Fix non-determinism in tests. We were using the same temp file for two tests, which meant there were race conditions. Should really use non-hardcoded filenames. --- tests/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tests.rs b/tests/tests.rs index 14df5b7f..dac97477 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -702,7 +702,7 @@ fn test_dofile_wrappers() { #[test] fn test_no_dofile_wrappers() { let mut tmppath = std::env::temp_dir(); - tmppath.push("test_dofile_wrappers.lua"); + tmppath.push("test_no_dofile_wrappers.lua"); unsafe { Lua::unsafe_new_with_flags( From 8fb784a2f011f5bf0b2715631237049be3f8c116 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Mon, 17 Jan 2022 22:30:45 +0000 Subject: [PATCH 15/20] Add test for loadfile. --- src/lua.rs | 2 +- tests/tests.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) diff --git a/src/lua.rs b/src/lua.rs index c2e121ae..58d7380a 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -659,7 +659,7 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { real_error("rlua loadstring: string expected.") elseif s:byte(1) == 27 then -- This is a binary chunk, so disallow - real_error("rlua loadstring: loading binary chunks is not allowed") + return nil, "rlua loadstring: loading binary chunks is not allowed" else return real_loadstring(s, chunkname) end diff --git a/tests/tests.rs b/tests/tests.rs index dac97477..4e28963b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -758,6 +758,113 @@ fn test_no_dofile_wrappers() { } } +#[test] +fn test_loadstring_wrappers() { + Lua::new().context(|lua| { + let globals = lua.globals(); + if globals.get::<_, Function>("loadstring").is_err() { + // Loadstring is not present in Lua 5.4, and only with a + // compatibility mode in Lua 5.3. + return; + } + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + lua.load( + r#" + assert(type(binchunk) == "string") + chunk = loadstring(binchunk) + assert(chunk == nil) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + lua.load( + r#" + local s = "x = x + 4" + chunk = loadstring(s) + assert(chunk ~= nil) + chunk() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + }); +} + +#[test] +fn test_no_loadstring_wrappers() { + unsafe { + Lua::unsafe_new_with_flags( + StdLib::ALL_NO_DEBUG, + InitFlags::DEFAULT - InitFlags::LOAD_WRAPPERS, + ) + .context(|lua| { + let globals = lua.globals(); + if globals.get::<_, Function>("loadstring").is_err() { + // Loadstring is not present in Lua 5.4, and only with a + // compatibility mode in Lua 5.3. + return; + } + lua.load( + r#" + x = 0 + function incx() + x = x + 1 + end + binchunk = string.dump(incx) + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 0); + + lua.load( + r#" + incx() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 1); + lua.load( + r#" + assert(type(binchunk) == "string") + assert(binchunk:byte(1) == 27) + chunk = loadstring(binchunk) + assert(chunk ~= nil) + chunk() + chunk = loadstring("x = x + 3") + chunk() + "#, + ) + .exec() + .unwrap(); + assert_eq!(globals.get::<_, u32>("x").unwrap(), 5); + }) + }; +} + #[test] fn test_result_conversions() { Lua::new().context(|lua| { From eff6101d69bcd3002dca0a04bbc96aae7890fe7e Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Wed, 19 Jan 2022 19:45:10 +0000 Subject: [PATCH 16/20] Fix the LOAD_WRAPPERS flag. --- src/lua.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua.rs b/src/lua.rs index 58d7380a..adf35d83 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -64,7 +64,7 @@ bitflags! { /// Flags describing the set of lua modules to load. pub struct InitFlags: u32 { const PCALL_WRAPPERS = 0x1; - const LOAD_WRAPPERS = 0x1; + const LOAD_WRAPPERS = 0x2; const DEFAULT = InitFlags::PCALL_WRAPPERS.bits | InitFlags::LOAD_WRAPPERS.bits; From 35a2c52d2009f8512518b42d5dff1f7d89afb635 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Wed, 19 Jan 2022 20:17:10 +0000 Subject: [PATCH 17/20] Optionally (but by default) remove package.loadlib. --- src/lua.rs | 19 ++++++++++++++++++- tests/tests.rs | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/lua.rs b/src/lua.rs index adf35d83..8becd562 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -65,9 +65,11 @@ bitflags! { pub struct InitFlags: u32 { const PCALL_WRAPPERS = 0x1; const LOAD_WRAPPERS = 0x2; + const REMOVE_LOADLIB = 0x4; const DEFAULT = InitFlags::PCALL_WRAPPERS.bits | - InitFlags::LOAD_WRAPPERS.bits; + InitFlags::LOAD_WRAPPERS.bits | + InitFlags::REMOVE_LOADLIB.bits; const NONE = 0; } } @@ -707,6 +709,21 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { assert_eq!(result, 0); } + if init_flags.contains(InitFlags::REMOVE_LOADLIB) { + ffi::lua_getglobal(state, cstr!("package")); + let t = ffi::lua_type(state, -1); + if t == ffi::LUA_TTABLE { + // Package is loaded + ffi::lua_pushnil(state); + ffi::lua_setfield(state, -2, cstr!("loadlib")); + } else { + // Assume it's not present otherwise. + assert_eq!(t, ffi::LUA_TNIL); + } + // Pop the package (or nil) off the stack. + ffi::lua_pop(state, 1); + } + // Create ref stack thread and place it in the registry to prevent it from being garbage // collected. diff --git a/tests/tests.rs b/tests/tests.rs index 4e28963b..69c4e6fe 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -865,6 +865,31 @@ fn test_no_loadstring_wrappers() { }; } +#[test] +fn test_default_loadlib() { + Lua::new().context(|lua| { + let globals = lua.globals(); + let package = globals.get::<_, Table>("package").unwrap(); + let loadlib = package.get::<_, Function>("loadlib"); + assert!(loadlib.is_err()); + }); +} + +#[test] +fn test_no_remove_loadlib() { + unsafe { + Lua::unsafe_new_with_flags( + StdLib::ALL_NO_DEBUG, + InitFlags::DEFAULT - InitFlags::REMOVE_LOADLIB, + ) + .context(|lua| { + let globals = lua.globals(); + let package = globals.get::<_, Table>("package").unwrap(); + let _loadlib = package.get::<_, Function>("loadlib").unwrap(); + }); + } +} + #[test] fn test_result_conversions() { Lua::new().context(|lua| { From 33ad9688f2f711196304254ffe0ab4c829d35ce9 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Thu, 20 Jan 2022 22:05:52 +0000 Subject: [PATCH 18/20] Also remove the C searchers/loaders when removing loadlib. --- src/lua.rs | 22 +++++++++++++++++++--- tests/tests.rs | 16 ++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 8becd562..5b01675d 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -19,8 +19,8 @@ use crate::hook::{hook_proc, Debug, HookTriggers}; use crate::markers::NoRefUnwindSafe; use crate::types::Callback; use crate::util::{ - assert_stack, dostring, init_error_registry, protect_lua_closure, push_globaltable, requiref, - safe_pcall, safe_xpcall, userdata_destructor, + assert_stack, dostring, init_error_registry, protect_lua_closure, push_globaltable, rawlen, + requiref, safe_pcall, safe_xpcall, userdata_destructor, }; bitflags! { @@ -713,9 +713,25 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { ffi::lua_getglobal(state, cstr!("package")); let t = ffi::lua_type(state, -1); if t == ffi::LUA_TTABLE { - // Package is loaded + // Package is loaded. Remove loadlib. ffi::lua_pushnil(state); ffi::lua_setfield(state, -2, cstr!("loadlib")); + + #[cfg(rlua_lua51)] + let searchers_name = cstr!("loaders"); + #[cfg(any(rlua_lua53, rlua_lua54))] + let searchers_name = cstr!("searchers"); + + ffi::lua_getfield(state, -1, searchers_name); + debug_assert_eq!(ffi::lua_type(state, -1), ffi::LUA_TTABLE); + debug_assert_eq!(rawlen(state, -1), 4); + // Remove the searchers/loaders which will load C libraries. + ffi::lua_pushnil(state); + ffi::lua_seti(state, -2, 4); + ffi::lua_pushnil(state); + ffi::lua_seti(state, -2, 3); + + ffi::lua_pop(state, 1); } else { // Assume it's not present otherwise. assert_eq!(t, ffi::LUA_TNIL); diff --git a/tests/tests.rs b/tests/tests.rs index 69c4e6fe..264f68ac 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -872,6 +872,14 @@ fn test_default_loadlib() { let package = globals.get::<_, Table>("package").unwrap(); let loadlib = package.get::<_, Function>("loadlib"); assert!(loadlib.is_err()); + + lua.load( + r#" + assert(#(package.loaders or package.searchers) == 2) + "#, + ) + .exec() + .unwrap(); }); } @@ -886,6 +894,14 @@ fn test_no_remove_loadlib() { let globals = lua.globals(); let package = globals.get::<_, Table>("package").unwrap(); let _loadlib = package.get::<_, Function>("loadlib").unwrap(); + + lua.load( + r#" + assert(#(package.loaders or package.searchers) == 4) + "#, + ) + .exec() + .unwrap(); }); } } From 261db4a03ddf1ea93800e146905ee4bed7968a21 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Fri, 21 Jan 2022 08:17:28 +0000 Subject: [PATCH 19/20] Switch from lua_seti to lua_rawseti for Lua 5.1. --- src/lua.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 5b01675d..0077bc3d 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -727,9 +727,9 @@ unsafe fn create_lua(lua_mod_to_load: StdLib, init_flags: InitFlags) -> Lua { debug_assert_eq!(rawlen(state, -1), 4); // Remove the searchers/loaders which will load C libraries. ffi::lua_pushnil(state); - ffi::lua_seti(state, -2, 4); + ffi::lua_rawseti(state, -2, 4); ffi::lua_pushnil(state); - ffi::lua_seti(state, -2, 3); + ffi::lua_rawseti(state, -2, 3); ffi::lua_pop(state, 1); } else { From 1a56538a0dd89a20acdc2ceb2a0b43b62dfbd1c7 Mon Sep 17 00:00:00 2001 From: Chris Emerson Date: Sat, 22 Jan 2022 21:52:59 +0000 Subject: [PATCH 20/20] Fix a documentation link. --- src/userdata.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/userdata.rs b/src/userdata.rs index ac29c684..980ab769 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -342,7 +342,7 @@ impl<'lua> AnyUserData<'lua> { /// Sets an associated value to this `AnyUserData`. /// - /// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`]. + /// The value may be any Lua value whatsoever, and can be retrieved with [`AnyUserData::get_user_value`]. /// /// Equivalent to set_i_user_value(v, 1) ///