Git Product home page Git Product logo

rlua's Introduction

rlua -- High level bindings between Rust and Lua

rlua is now deprecated in favour of mlua: see below for migration information

rlua is now a thin transitional wrapper around mlua; it is recommended to use mlua directly for new projects and to migrate to it when convenient. mlua was a fork of rlua which has recently seen more development activity and new features.

Migration

rlua 0.20 includes some utilities to help transition to mlua, but is otherwise just re-exporting mlua directly.

The main changes are:

  • In mlua, Lua::context() is no longer necessary. The methods previously on Context can now be called directly on the Lua object. rlua 0.20 includes an RluaCompat extension trait which adds a context() method which can be used to avoid having to update code all at once.

  • The ToLua trait has been renamed to IntoLua, and its conversion method to_lua is now into_lua. rlua 0.20 includes ToLua as an alias for IntoLua and an extension ToLuaCompat which adds a to_lua method as a temporary convenience.

A few other changes which should be less disruptive:

  • mlua has different defaults and options for blocking loading C libraries or compiled modules from Lua code or catching Rust panics. Check the Lua::new_with and unsafe variants for the new options.

rlua's People

Contributors

0x2ec avatar a-rodin avatar a2aaron avatar acrisci avatar alexandraalter avatar ambeeeeee avatar azdle avatar freemasen avatar jhasse avatar jonas-schievink avatar jugglerchris avatar kyren avatar lpghatguy avatar paulfrische avatar poga avatar pollend avatar sakridge avatar salexan2001 avatar shiroy avatar thelearnerofcode avatar timidger avatar w-ensink avatar yasumutte avatar yjhmelody avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rlua's Issues

I'm pretty sure you can cause UB in safe code with Callbacks

Callbacks are not currently placed inside a RefCell, and are also FnMuts (not Fns), so if you call a callback inside itself, you've got mutability and aliasing.

I think all userdatas should be inside RefCell to ensure this problem is avoided in the future, we could also provide the ability to add Fn (not FnMut) callbacks, but it kind of doubles the API surface area, unless we do some trait trickery. I think just using RefCell is fine for the moment.

Segfault on dropping LuaFunction?

Hello, and thanks for this library, it looks great.

I'm trying to write a configurable/scriptable program where one can register functions from a Lua script to be used later from a GUI Rust program. Running the following in release mode (Linux 64 bit, Rust 1.27.2) creates a segfault when program is dropped. Am I doing something wrong or strange?

extern crate rlua;
use rlua::prelude::*;

#[derive(Debug)]
struct Program<'a> {
    funcs: Vec<(String,LuaFunction<'a>)>,
}

fn mk_program<'a>(lua :&'a Lua, script :&str)  -> LuaResult<Program<'a>> {
    let mut program = Program { funcs: Vec::new()};
    lua.scope(|scope| {
        lua.globals().set("register", scope.create_function_mut(|_, tbl:LuaTable| {
            let name :String = tbl.get("name")?;
            let func :LuaFunction = tbl.get("func")?;
            program.funcs.push((name,func));
            Ok(())
        })?)?;
        lua.eval::<()>(script, None)?;
        Ok(())
    })?;
    Ok(program)
}

fn main(){
    let lua = Lua::new();
    let program = mk_program(&lua, r#"register { name = "test", func = function () end }"#).unwrap();
    println!("{:?}", program);
}

GDB output:

Program { funcs: [("test", Function(Ref(4)))] }

Program received signal SIGSEGV, Segmentation fault.
rlua::lua::Lua::drop_ref::h44c6ee14693a031e (self=0x7fffffffd278, lref=0x7ffff6a50018)
    at /home/bjlut/.cargo/registry/src/github.com-1ecc6299db9ec823/rlua-0.14.0/src/lua.rs:763
763	            ffi::lua_pushnil((*extra).ref_thread);
(gdb) bt
#0  rlua::lua::Lua::drop_ref::h44c6ee14693a031e (self=0x7fffffffd278, lref=0x7ffff6a50018)
    at /home/bjlut/.cargo/registry/src/github.com-1ecc6299db9ec823/rlua-0.14.0/src/lua.rs:763
#1  _$LT$rlua..types..LuaRef$LT$$u27$lua$GT$$u20$as$u20$core..ops..drop..Drop$GT$::drop::h5c2fb308405493c3 (self=0x7ffff6a50018)
    at /home/bjlut/.cargo/registry/src/github.com-1ecc6299db9ec823/rlua-0.14.0/src/types.rs:92
#2  0x0000555555562ad5 in core::ptr::drop_in_place::h4eff9b84948e99de () at /checkout/src/libcore/ptr.rs:59
#3  core::ptr::drop_in_place::hc0a0e904c5e46691 () at /checkout/src/libcore/ptr.rs:59
#4  core::ptr::drop_in_place::hf24a5bf0280dbaeb () at /checkout/src/libcore/ptr.rs:59
#5  core::ptr::drop_in_place::h4dff6797faf34f16 () at /checkout/src/libcore/ptr.rs:59
#6  _$LT$alloc..vec..Vec$LT$T$GT$$u20$as$u20$core..ops..drop..Drop$GT$::drop::h6e6cc1341dca5ef0 (self=<optimized out>)
    at /checkout/src/liballoc/vec.rs:2166
#7  core::ptr::drop_in_place::hdeb56b48f40b6a81 () at /checkout/src/libcore/ptr.rs:59
#8  core::ptr::drop_in_place::heebf2eae8bc36947 () at /checkout/src/libcore/ptr.rs:59
#9  luasegfault::main::h91edc85fef633fb6 () at src/main.rs:28
#10 0x0000555555560763 in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hf36a085e69e29833 () at /checkout/src/libstd/rt.rs:74
#11 0x00005555555ae183 in std::rt::lang_start_internal::_$u7b$$u7b$closure$u7d$$u7d$::h86ba874310c8f41e () at libstd/rt.rs:59
#12 std::panicking::try::do_call::h64129d2b0e54f3b8 () at libstd/panicking.rs:310
#13 0x00005555555c4dca in __rust_maybe_catch_panic () at libpanic_unwind/lib.rs:105
#14 0x00005555555b2156 in std::panicking::try::hceef11cfeb87cfe0 () at libstd/panicking.rs:289
#15 std::panic::catch_unwind::h331e7e117781c30b () at libstd/panic.rs:374
#16 std::rt::lang_start_internal::h6264a91317866dd6 () at libstd/rt.rs:58
#17 0x0000555555562d5a in main ()

Lua::from/to have confusing names

In Rust, T::from* and T::to* imply creating a T from another type or converting the T to another type (doesn't have to consume T).

Lua::from doesn't create a Lua object, it merely uses it to perform the conversion, producing a Value. Similarly, Lua::to doesn't convert the Lua object, it converts its argument (a Value).

I think the best way to clear this up is to remove both methods - they are merely wrappers around t.to_lua and T::from_lua respectively, which is perfectly clear.

Scoped borrow of UserData

It seems like several people have struggled with putting Rust values into Lua as UserData and then later extracting them from Lua again, hoping for everything to be nice and safe when the value comes back out.

Wrapping a value in something like Rc<RefCell<Option<_>>> works, but is slightly cumbersome to work with. It also requires you to use an owned value. If you only have a mutable reference on hand, it seems like you cannot use it as a UserData. Scopes can create functions which are not 'static so you can use borrowed values there, but this is also somewhat cumbersome.

I have tried to come up with a workaround for this, allowing Scope to create UserData from mut-borrowed values. Hoping to get opinions on whether this is implementation will work. Apologies if I have missed something obvious or something that has already been discussed. I have looked at #20, especially #20 (comment) .

The idea is to give a &'scope mut T reference to the Scope and store it as a raw pointer *mut T, which is plain old data so it can have 'static lifetime. Because we are in a Scope, we know that the pointer will be deleted when the Scope is destructed. The UserData trait can be implemented for BorrowedUserData<T> by running T::add_methods with a modified UserDataMethods implementation which extracts BorrowedUserData<T> and dereferences the pointer whenever the T is needed. Checking for multiple borrows is handled with in the same way as owned values, with RefCell.

Code diff here: luteberget@8d32cca

I am certainly no expert in unsafe Rust, so if I've misunderstood any of the following then this might not work:

  1. Giving out a &'scope mut T reference ensures that the corresponding *mut T points to the T for the duration of the borrow.
  2. The pointer is only accessed and dereferenced when borrowed through the RefCell, so the dereferenced pointer can be used normally as a &T or &mut T in Rust code for the lifetime of the Ref/RefMut.
  3. I'm not sure a raw pointer is actually needed, maybe it would work with mem::transmute into static lifetime instead, or something else.

Also, the BorrowedUserData<T> struct is internal to the crate, so users of the library cannot use AnyUserData::borrow or similar. I think this could be fixed by checking for both T and BorrowedUserData<T> when borrowing from AnyUserData.

Using this feature looks like this:

extern crate rlua;
use rlua::prelude::*;

#[derive(Debug)]
struct MyData {
    val: u64,
}

impl LuaUserData for MyData {
    fn add_methods(methods: &mut LuaUserDataMethods<Self>) {
        methods.add_method_mut("increment", |_,this,()| {
            this.val += 1;
            Ok(())
        });
    }
}

fn main() -> LuaResult<()> {
    let lua = Lua::new();
    let mut x = MyData { val: 0 };
    lua.scope(|s| {
        lua.globals().set("x", s.borrow_userdata(&mut x)?)?;
        // mutate x from Lua
        lua.eval::<()>("x:increment()", None)?;
        Ok(())
    })?;
    x.val += 1;
    // x.val is now 2
    Ok(())
}

How to set methods after creating userdata?

I have a map of methods that I want to add to my userdata. However, I can't pass anything to UserData::add_methods. Is there a way to set methods after I created the user data?

Releases aren't tagged

rlua doesn't tag its releases, which can be annoying when you want to check the source of a particular version.

Why does create_function specify 'static?

When playing around trying to make a prototype for a system I'm developing, I found that I bumped into a wall when using Lua.create_function. It has a lifetime bound specified as 'static which caused trouble, but when I tried changing it to 'lua it worked and it doesn't seem to have caused trouble elsewhere. So my question is why the boundary is 'static instead of 'lua. I'm pretty new to Rust so I may have missed something very obvious here, but I thought I'd ask anyway.

Thanks!

Consider removing the `Lua` prefix from types/traits

The rlua crate is already a namespace for the contents, so they don't need a prefix for disambiguation. Removing the prefix would allow writing shorter and slightly clearer code in my opinion.

Downside: use rlua::*; imports too much (including a Result type alias shadowing Rust's own result type). This can be mitigated somewhat by introducing a prelude module like many libs do. It's also not clear to me if renaming the LuaString type to String is a good idea.

(also, LightUserData is the only type that's not prefixed)

Functions are !Send

Function is basically a wrapper of Lua and since Lua itself is Send, is it possible to implement Send for Function?

xpcall returning three values

I found a strange issue with using xpcall that I can't reproduce in other Lua 5.3 interpreters. I tested this using the system lua (e.g I compiled it with --no-default-features on the repl and I found it through a very hard to follow bug while working on Way Cooler).

Here's the basic steps to reproduce:

Run xpcall(function(foo) return foo end, print, "foo") in both interpreters (note it doesn't really matter what the functions are, the first function has to return something though)

On my system lua (Lua 5.3.4) it returns true and "foo" (as expected)

On rlua it returns true function "foo".

The second function it returns is the error function, you can see that by running this in the rlua interpreter:

(function(a, b, c) b("a") end )(xpcall(function(foo) return foo end, print, "foo")) => prints "a"

Don't let Lua code cause panics or aborts

This is a tracking issue for the remaining cases where Lua code can cause a Rust panic or a direct abort. This is generally unwanted, because untrusted Lua code should not be able to abort the Rust program. Although panics can be caught on the Rust-side, it is intended that all errors caused by Lua are propagated using LuaResult.

Note that fixing these issues will still not make it advisable to run arbitrary untrusted Lua code. Proper sandboxing support and execution limits need to be exposed first.

  • Accessing resurrected UserData directly causes a panic. Instead, a Lua error should be raised. This error can be caught by Lua (this isn't very useful, but also not a big problem) or will propagate to the Rust code that caused the Lua code to run.
  • Exceeding the stack size with Rust callbacks will lead either to an assertion failure (which is an abort) in Lua or to a panic. rlua should automatically provide and enforce a recursion limit, raising a Lua error when hit.
  • According to the readme: There are currently no checks on argument sizes. An abort can be caused by passing too large Variadics or MultiValues. When Rust calls a function with too many arguments, an Error should be directly returned. When Lua calls a Rust callback with too many arguments, the Lua runtime should already handle this. However, a Rust callback can also return too many values (Lua "only" guarantees that at least 1000 values can be returned). This can be handled by raising a Lua error. Alternatively, we can define this as a bug on the Rust side and cause a panic. This can lead to subtle bugs, however, when the number of values depends on the inputs from Lua or is otherwise unclear.
  • Errors in __gc cause an abort. Raising a Lua error from here is unsafe, so it is caught and the program is aborted. This might be a reasonable default behaviour in some cases, but it would be nice if a user-defined callback could be provided.
  • Allocation failure causes an abort. While this matches Rust's behaviour, a way to limit the amount of memory used by Lua code is essential to be able to run untrusted code.

I hope these are all cases. If not, feel free to comment/edit, of course.

Why interior mutability?

I'm curious about the rationale behind allowing mutations in rlua types without mut? I get that it may be more convenient, but is there some justification as to why it's not just undermining the intent of Rust's type system?

Support passing Results to Lua

We already have a ToLua impl for Option, which will push the contained value if it exists, and nil otherwise. This maps pretty well into Lua's world.

Another common Lua pattern occurs when dealing with fallible operations like io.open, which either returns the opened file on success, or nil followed by an error message on failure. It would be nice if I could map Rust's Result to this idiom. This would require a ToLuaMulti impl instead of a ToLua impl, though (since the operation can push nil and the error message).

LuaErrorKind::IncompleteStatement should be removed.

As seen here, rlua checks for "<eof>" at the end of error messages and returns LuaErrorKind::IncompleteStatement if it finds it.

One issue I have with this is that it causes tests::test_eval to fail when using LuaJIT, because the error message ends with "'<eof>'" (notice the single quotes). I realize that might not be a good enough reason to change it.

But my OTHER issue is that it just seems pointless to have it at all. Why not just return a LuaErrorKind::ScriptError in both cases? (the comment there even suggests that it would be better to remove it)

Loading selected Lua libs

As of right now, when creating a new instance of the Lua structure, all the lua libraries are automatically loaded. This is, as written in the README, undesirable.
Is there any reason this is currently like this? What do I need to know before trying to change the API to make libraries loading into a separate functions?

Array methods for LuaTable have unclear behaviour

I've noticed that LuaTable::for_each_array_value and LuaTable::array_values have somewhat surprising behaviour. First of all, they don't really match anything in the Lua API (the manual doesn't define what an array is, only sequences are defined). Second, they require the table to exclusively hold natural numbers as keys, something unseen in the Lua API or standard library (which just ignore the non-sequence part of a table).

Another unexpected behaviour occurs when iterating over a table with "holes", like { [1] = "bla", [2] = "qux", [123] = "oops" }, then the missing elements are still taken into account and the closure passed to for_each_array_value is called 121 times with a nil value. While this is pretty much the only usable behaviour since the key isn't passed to the closure, this isn't quite what I expected (eg. ipairs would ignore the 123 key and only iterate over 1 and 2).

My initial solution for this would've been to change the methods to act on the sequence part of the table instead (and rename them accordingly), however, the current behaviour is used by the FromLua impls of HashSet, BTreeSet and Vec, where it does make more sense (we return an error instead of ignoring the non-sequence part of the table). Not sure what's the best way forward for this API.

Coroutines show up as "Dead" when running

When a coroutine checks its own status, ThreadStatus::Dead is returned.

I'm not sure if this is intentional, Active would make a bit more sense, but that would mean that we can't know in advance whether calling resume will work. Maybe Active should be split into Running and Ready/Resumable/Waiting or something else?

Reproduction:

extern crate rlua;

use rlua::*;

fn main() {
    let lua = Lua::new();
    let f = lua.create_function(|lua, args| {
        let thrd: Thread = lua.unpack(args)?;
        println!("{:?}", thrd.status());
        lua.pack(())
    });
    let thrd = lua.create_thread(f);
    thrd.resume::<_, ()>(thrd.clone()).unwrap();
}

How to access data outside of a function? (Lifetime complications)

Lua::create_function accepts callables of 'static lifetime. This obviously disallows passing a closure with an environment, since they are not 'static. This is raises a question: How can one acces data outside of LuaFunction?

How can one achieve something like the following?

let house = House::new(); // Interior mutability type
globals.set("foo", lua.create_function(|_, _| house.set_on_fire()));

What's the usual pattern to solve this issue?

lua_pcall/lua_error invocations might be unsafe (all of them)

Currently, rlua makes sure to only call Lua API functions that can cause an error from within error_guard, which uses lua_pcall to run a Rust closure in a protected environment. This causes all Lua errors to be caught by lua_pcall.

Since Lua's error handling uses setjmp and longjmp to do non-local control flow, an error will longjmp to the lua_pcall, skipping across the Rust closure passed to error_guard. According to IRC this is undefined behaviour in Rust. EDIT: People are also claiming that it's unsafe in this user forum thread.

Fixing this is hard. It would require that all uses of lua_pcall only call C functions. And calling lua_error from Rust would never be a safe thing to do, since it must skip across a Rust stack frame.

If this is really UB, the only way to write a safe Lua wrapper would involve writing C code. Not sure if that's really the case, though...

Serde Serializer/Deserializer for Lua Value

Currently conversion of Rust types to Lua Values is uses FromLua and ToLua traits. However, it might be cumbersome to implement these traits manually. One solution is to implement derive procedural macro. However, it is quite a lot of work and introduces extra dependency for end user.

On the other hand, a very similar data type is used in serde_json - Value. With Serializer/Deserializer traits implemented, basically any data type with Serialize/Deserialize trait can be converted to/from it.

I have test implementation of Serializer in zrkn/rlua (mostly copied from serde_json). In my project I only need to convert Rust values to Lua, so it works for me. However if you would consider merging this feature upstream I can implement Deserializer too and make pull request.

Collect common problems into FAQ

While using rlua, I encountered a lot of lifetime problem which are already described in previous issues (e.g. #20 (comment), #41, #74...). After reading those issues, using rlua becomes much easier.

It would be great if we can collect these design rationale into a FAQ document for beginners to lookup. I can help if you're ok about the idea.

Here's a list of issues/comment I think are really helpful:

Why 'static lifetime is required when passing data to Lua?

The reason I mention the GC is because that is the reason WHY userdata must be 'static: since we don't know when Lua will get around to garbage collecting our userdata, we can't make any assumptions about the lifetimes of any references in the userdata. Lua::scope forces the userdatas to be destructed at the exit of the scope.

How do I store Table, Function, or other rlua reference types outside of Lua?

There are two rules about rlua reference types that you're butting up against:

rlua reference types (Table, Function, etc) hold references to Lua, and are not really designed to be stored along-side Lua, because that would require self borrows. You CAN make structs in Rust that self borrow, but you really don't want to go down that road, it's very complex and you 99.999% of the time don't need it.

rlua reference types are also not designed to be stored inside userdata inside Lua. When I say userdata here, I mean both actual UserData types and also things like Rust callbacks. For one, there are some actual safety issues if you were able to do that, but more importantly Lua itself is not really designed for this either. Lua's C API does not provide a way of telling Lua that a userdata contains a registry reference, so you would end up potentially making something that cannot be garbage collected. The actual issue is very complicated but you can read about it some here.

Serializing the Lua state

At the moment I'm using specs and rlua to write a simple RTS for research-level AI. I won't go into the details, but for various reasons (including tree search, game replays, speculative simulation, and so on) I really need to serialize the entire game state from any frame so that it's arbitrarily rewindable and deterministic on rewind.

At the moment, the lack of serialization is constraining. I basically have to assume the Lua stores no data, and likewise the Rust side must immediately convert any returned Lua data into native Rust types and write it elsewhere. This limits my scripting potential heavily without mucking with the core engine, because I need to essentially implement piecemeal triggers in Rust and use Lua as a simple configuration language (basically just return a table of prebaked entities, triggers and parameters which Rust then executes).

Currently, my strategy is just to serialize the file path of the script, and on startup load it (on rewind I just assume the file is constant).

I'm not asking for full Serde here since I doubt it's even possible (esp. once references get involved), but it would be nice if there was some way to serialize certain data and (pipe dream) Function types, and reattach them to a new context, even if there end up being some constraints. I understand this is likely a Hard Problemโ„ข but I figured I'd file an issue just to see if any partial version of this is even possible.

xpcall is broken

rlua REPL session:

> xpcall(error, function(...) print("caught error: ", ...) return "errรถr" end, "testerror")
caught error: 	bad argument #2 to 'error' (number expected, got string)
Boolean(false)	String(String(LuaRef(4)))
> xpcall()
repl: lua/lapi.c:227: lua_copy: Assertion `(((to) != (&luaO_nilobject_))) && "invalid index"' failed.

The first one: error is not called with the right arguments, it should only get "testerror".
The second one is just a missing check for enough arguments.

pcall also has problems:

> print(pcall())
false	attempt to call a string value

Difference to hlua [Readme]

You write:

This crate has the following differences with hlua:
Handles to Lua values use the Lua registry, not the stack
Handles to Lua values are all internally mutable
Handles to Lua values have non-mutable borrows to the main Lua object, so there can be multiple handles or long lived handles
Targets Lua 5.3

What is what?
What is hlua and which one your project?

Segfault in LuaTable::set

After a long debugging session, I've noticed that cargo test --release fails with a segfault on the current nightly.

This might as well be a regression of the compiler, I'm not yet sure.

Soundness issue when borrowing Lua in a callback

Currently this compiles, and that is a problem because lua does not have a stable address. Closures should go back to having a 'static lifetime.

    let lua = Lua::new();
    let globals = lua.globals();

    globals.set("boom", lua.create_function(|_, _| {
        lua.pack(lua.eval::<i32>("1 + 1", None)?)
    })).unwrap();

Panic if AnyUserData.get_user_value fails

extern crate rlua;

struct Unconvertible;

impl <'lua> rlua::FromLua<'lua> for Unconvertible {
    fn from_lua(_: rlua::Value<'lua>, _: &'lua rlua::Lua) -> rlua::Result<Self> {
        Err(rlua::Error::ExpiredUserData)
    }
}

struct MyData;

impl rlua::UserData for MyData {
}

fn main() {
    let lua = rlua::Lua::new();
    let data = lua.create_userdata(MyData).unwrap();
    data.get_user_value::<Unconvertible>().unwrap();
}

Output: (with RUST_BACKTRACE=1):

thread 'main' panicked at 'rlua internal error: expected stack to be 0, got 1', /home/psychon/.cargo/registry/src/github.com-1ecc6299db9ec823/rlua-0.12.0/src/util.rs:47:4
stack backtrace:
   0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: std::panicking::rust_panic_with_hook
   5: std::panicking::begin_panic
   6: std::panicking::begin_panic_fmt
   7: rlua::util::stack_guard
             at ./<panic macros>:7
   8: rlua::userdata::AnyUserData::get_user_value
             at /home/psychon/.cargo/registry/src/github.com-1ecc6299db9ec823/rlua-0.12.0/src/userdata.rs:426
   9: rlua_test::main
             at src/main.rs:19
  10: __rust_maybe_catch_panic
  11: std::rt::lang_start
  12: main
  13: __libc_start_main
  14: _start

What I think happens:
This is the code for get_user_value:

    pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
        let lua = self.0.lua;
        unsafe {
            stack_guard(lua.state, 0, || {
                check_stack(lua.state, 2);
                lua.push_ref(lua.state, &self.0);
                ffi::lua_getuservalue(lua.state, -1);
                let res = V::from_lua(lua.pop_value(lua.state), lua)?;
                ffi::lua_pop(lua.state, 1);
                Ok(res)
            })
        }
    }

In the above, if from_lua fails, then the lua_pop is not executed and the stack_guard notices that something is wrong.

I have not really looked at the remaining code in rlua, but I guess that this problems also exists in other places. For example, I bet I could trigger a panic with set_user_value by making to_lua fail.

CC @Timidger

Why is Lua not Send?

I believe wrapping Lua in Arc<Mutex<...>> should allow passing it to other threads and synchronized access?

Right now it's this:

error[E0277]: the trait bound `*mut rlua::ffi::lua_State: std::marker::Send` is not satisfied in `rlua::Lua`
  --> src/main.rs:29:29
   |
29 |     let event_loop_thread = thread::spawn({
   |                             ^^^^^^^^^^^^^ `*mut rlua::ffi::lua_State` cannot be sent between threads safely
   |
   = help: within `rlua::Lua`, the trait `std::marker::Send` is not implemented for `*mut rlua::ffi::lua_State`
   = note: required because it appears within the type `rlua::Lua`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<rlua::Lua>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::Mutex<rlua::Lua>>`
   = note: required because it appears within the type `[closure@src/main.rs:32:9: 55:10 lua:std::sync::Arc<std::sync::Mutex<rlua::Lua>>, barrier:std::sync::Arc<std::sync::Barrier>]`
   = note: required by `std::thread::spawn`

[Q] Catch lua error

Can I catch and Faulty lua code, and get some stack trace without panicking the main Rust code?

Consider hiding `Error` internals

Currently, adding a variant to Error (or changing an existing one) is a breaking change. This happens pretty frequently during development. We could prevent that by making Error a struct with a few helper methods to create a specific error, and make the internal enum private so it's not part of the API.

This is probably not that important right now, but when rlua gets closer to a 1.0 release it could allow more freedom in expanding rlua's internals (better error messages, more distinguished error cases without a breaking change). I'm just opening this issue to get a bit of feedback.

Storing Table references alongside Lua.

I'm having a bit of difficulty architecting a scripting extension to my entity component system using rlua to support user-scripted behaviours. The idea is to have the user define a table in a Lua script resembling the following:

local Test = { }

function Test:Init() 
  -- Do some stuff
end

function Test:Update(delta) 
  -- Do some stuff
end

function Test:Destroy() 
  -- Do some stuff
end

return Test

Now, at a basic level, I'd like to be able to associate these table definitions with an internal entity ID. So, I'd like somewhere to store a HashMap<Entity, Table>. On an update event, I'd like to loop through the tables and execute the appropriate function in the Lua context. The issue I'm running into currently is lifetimes. With the following astoundingly naive solution:

pub struct ScriptSystem<'a> {
    host: Lua,
    initialized: BitSet,
    behaviours: HashMap<Entity, Table<'a>>,
}

impl<'a> ScriptSystem<'a> {
    pub fn new() -> Self {
        ScriptSystem {
            host: Lua::new(),
            initialized: BitSet::new(),
            behaviours: HashMap::new(),
        }
    }
}

impl<'a, 'b> System<'a> for ScriptSystem<'b> {
    type SystemData = (
        Entities<'a>,
        ReadStorage<'a, Script>,
        Fetch<'a, AssetStorage<ScriptAsset>>,
        Fetch<'a, Time>,
    );

    fn run(&mut self, (entities, script, storage, time): Self::SystemData) {
        let behaviour = self.host.eval::<Table>(asset.source.as_str(), Some(asset.name.as_str()));
        self.behaviours.insert(entity, behaviour);
    }
}

I run into predictable lifetime errors, because the lifetime of the Table value returned from the eval() call would outlive the borrowed content self.host but must also adhere to the lifetime parameter 'b and so survive with the struct. I have tried every workaround I know of to fix this issue, including wrapping both the Lua instance and HashMap in a common struct with a shared lifetime. I think it's time to face the fact that I'm probably just doing something plain wrong, and was curious if there is a solution to this sort of use case using rlua.

Unable to pass functions as arguments to `create_function`

let on_function = lua.create_function(|_, (name, handler): (String, fn(String))| {
	handler(name);
	Ok(())
})?;

I've also attempted with rlua::Function, but that seems to not work either.

Error:

error[E0277]: the trait bound `(std::string::String, fn(std::string::String)): rlua::FromLuaMulti<'_>` is not satisfied
  --> src/modules/api/event_handler.rs:17:25
   |
17 |         let on_function = lua.create_function(|_, (name, handler): (String, fn(String))| {
   |                               ^^^^^^^^^^^^^^^ the trait `rlua::FromLuaMulti<'_>` is not implemented for `(std::string::String, fn(std::string::String))`
   |
   = help: the following implementations were found:
             <(B, A) as rlua::FromLuaMulti<'lua>>

Is there someway to pass a function that I'm missing?

Support for lua_sethook

This might be a safety nightmare, so I wanted to get your input before looking into implementation. Here are the production use-cases:

  • running untrusted code submitted from users (stop infinite loops)
  • pre-empting a CPU-heavy lua script in an async event loop

And then there's the typical debugging tasks, but I think those can be done within lua with the current debug library.

From #52, it looks like the debug library is considered "unsafe", and I'm not sure if this can be done safely. In any case, I'd really like be able to handle the above use cases.

I created a similar issue on hlua (#194), just in case discussion in the other project is useful here.

I'm willing to do work on this, but I'd like to know your thoughts on feasibility first.

EDIT: This is essentially the first bullet point in the README (and perhaps the second).

Cannot reimplement luaL_tolstring / No accessible way for lua_topointer

Hi,

Lua's default tostring implementation just calls luaL_tolstring. For userdata, threads, ..., this creates a string of the form [typename] [pointer] where the pointer comes from lua_topointer().

The current plan in way-cooler is to reimplement parts of awesome. Awesome implements a similar __tostring metamethod: This produces the name of the type (e.g. button) followed by the raw pointer. Right now I see no way to implement this with rlua. Taking the address of an AnyUserData (via &foo as *const _) does not work, since this just produces a "random pointer" to the stack.
(As I just learned when looking up the code, I could just add a __name field to the meta-table... interesting; thanks for making me solve my own problems! Still, I'll leave this paragraph in since it still might be nice to be able to implement this.)

The above might not be all that convincing (you could just tell me to "fake" a pointer, I guess), so here is another use case:
Awesome has objects called "tags". These are accessible outside of Lua (via X11). To make it possible for Lua to create (easy) and destroy (hard!) tags, tag objects have an activated property that can be set to true/false. When true, awesome uses luaL_ref to create a reference to the tag, saves it in a global list and makes it accessible externally.
I have problems reimplementing this with rlua. My idea was to save a Table in the registry. When a tag becomes activated, I append the tag to the table (table.set(table.len() + 1, object)). However, when deactivating a tag, I have problems finding the right entry in the table. I do not want to use something like PartialEq, since that compares for equality, not object identity.
I do not have a really good reason for why I don't just do table.set(object, true) (i.e. using the object as key and let Lua figure out object identity) except for compatibility with awesome.

I think both of these problems could be fixed by providing wrappers for lua_topointer on AnyUserData, although I have to admit that this feels a bit hackish. Of course, I am always open for other suggestions as well. :-)

Edit: Well... okay, __name is not accessible through UserDataMethods, so that one does not help me either.

Current __gc logic for userdata is unsound

The manual states:

Moreover, if the finalizer marks a finalizing object for finalization again, its finalizer will be called again in the next cycle where the object is unreachable.

This means that a destructor assigned to the __gc metamethod might be run multiple times on the same userdata. Inside fn destructor, rlua currently just does this:

        let obj = &mut *(ffi::lua_touserdata(state, 1) as *mut T);
        mem::replace(obj, mem::uninitialized());
        0

If one figures out a way to resurrect the userdata object from within drop, this leads to a double free. However, I am not sure if this is possible, since all userdata must outlive 'static.

If this is by design, it would be helpful to document that. In general, rluas internals are barely documented which makes it hard to understand and debug stuff like #18.

EDIT: See comment below for use-after-free demonstration

Why type_name() is private?

Hi.

When implementing ToLua/FromLua for custom types it would've been handy to have access to type name for returning it in ConversionError. Maybe make it public? Or add utility method for creating ConversionErrors with apropriate to/from values?

Creating a coroutine from a Rust closure hits an assertion

Reproduction:

extern crate rlua;

use rlua::*;

fn main() {
    let lua = Lua::new();
    let thrd_main = lua.create_function(|lua, _| {
        lua.pack(())
    });
    lua.globals().set("main", thrd_main).unwrap();
    let thrd: Thread = lua.eval("coroutine.create(main)", None).unwrap();
    thrd.resume::<_, ()>(()).unwrap();
}

This hits an assertion:

bug: lua/lapi.c:182: lua_settop: Assertion `(-(idx+1) <= (L->top - (func + 1))) && "invalid new top"' failed.

Notably, when replacing the Lua code with coroutine.create(function() end) it works.

"not enough elements in stack" assertion when playing with coroutines

Hit this while writing docs and an example for LuaThread::resume. Take this code:

extern crate rlua;
use rlua::*;
                                                             
fn main() {
let lua = Lua::new();
let thread: LuaThread = lua.eval(r#"
    coroutine.create(function(arg)
        assert(arg == 42)
        local yieldarg = coroutine.yield(123)
        assert(yieldarg == 43)
        return 987
    end)
"#).unwrap();
                                                             
assert_eq!(thread.resume::<_, u32>(42).unwrap(), Some(123));
assert_eq!(thread.resume::<_, u32>(43).unwrap(), Some(987));
assert_eq!(thread.resume::<_, u32>(()).unwrap(), None);
}

Executing it hits this assertion inside Lua:

rust_out: lua/ldo.c:663: lua_resume: Assertion `(((L->status == 0) ? nargs + 1 : nargs) < (L->top - L->ci->func)) && "not enough elements in the stack"' failed.

LuaJIT support

Hi, found this note in lua-jit-sys crate:

Compatability with rlua and hlua

I will try to get rlua and hlua working

I wonder how much effort it is to add LuaJIT support to rlua? I found rlua has the most idiomatic and safe API to Lua out there, and it fits my needs almost perfectly - just need the LuaJIT there.

[Q] return map or vec from eval

 "map" => {
      let eval_res = lua.eval::<HashMap>(code, None);
      println!("val = {:?}", eval_res);
    }
    "string" => {
      let eval_res = lua.eval::<Vec>(code, None);
      println!("val = {:?}", eval_res);
    }

Something like this?

debug library not included

Hey there,

The awesome lua library (which I'm trying to run with these bindings) makes great use of the debug functionality of Lua (specifically debug.traceback), but it's not included in these bindings. I could make it myself, but I just wanted to double check to see why it's not included.

How to store a Lua function in UserData

I need to store a user-provided Lua function in a struct that implements UserData. I tried using UserDataMethods to add a method that takes in a function and stores it but I keep running into lifetime errors.

rlua::Lua is expected to live for the entirety of the program (and in fact I put it in a lazy_static! so that its address is always stable), but I can't find a way to indicate in the custom user data method that rlua::Lua will be 'static and there's no way for me to convince it that the anonymous 'lua lifetime for the passed in rlua::Lua reference will live longer than the reference I store in the UserData.

Here's a minimized example of the issue I'm running into:

use rlua;

// How long the function lives, should be == 'lua
struct MyUserData<'function> { 
    function: Option<rlua::Function<'function>>
}

// HAS to be static, due to `UserData` requirements...
impl UserData for MyUserData<'static> 
{
    fn add_methods(methods: &mut rlua::UserDataMethods<Self>) {
        methods.add_method_mut("test", test_function);
    }
}

// lifetime issue occurs in this function
// is there some way for me to prove that `this` lives as long as `lua`??
fn test_function(lua: &rlua::Lua, this: &mut MyUserData, func: rlua::Function)
                        -> rlua::Result<()> {
    this.function = Some(func);
}

fn main() {}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.