mlua/src/lua.rs

1209 lines
40 KiB
Rust
Raw Normal View History

use std::{ptr, str};
2017-05-21 18:50:59 -05:00
use std::ops::{Deref, DerefMut};
use std::iter::FromIterator;
use std::cell::RefCell;
use std::ffi::CString;
2017-05-21 18:50:59 -05:00
use std::any::TypeId;
use std::marker::PhantomData;
use std::collections::{HashMap, VecDeque};
use std::os::raw::{c_char, c_int, c_void};
use std::process;
2017-05-21 18:50:59 -05:00
use libc;
2017-05-21 18:50:59 -05:00
use ffi;
use error::*;
use util::*;
2017-10-23 15:42:20 -05:00
use types::{Callback, Integer, LightUserData, LuaRef, Number};
use string::String;
use table::Table;
2017-10-23 15:42:20 -05:00
use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
2017-05-21 18:50:59 -05:00
2017-06-17 20:23:17 -05:00
/// A dynamically typed Lua value.
2017-05-21 18:50:59 -05:00
#[derive(Debug, Clone)]
pub enum Value<'lua> {
2017-06-17 20:23:17 -05:00
/// The Lua value `nil`.
2017-05-21 18:50:59 -05:00
Nil,
2017-06-17 20:23:17 -05:00
/// The Lua value `true` or `false`.
2017-05-21 18:50:59 -05:00
Boolean(bool),
2017-06-17 20:23:17 -05:00
/// A "light userdata" object, equivalent to a raw pointer.
LightUserData(LightUserData),
2017-06-17 20:23:17 -05:00
/// An integer number.
///
/// Any Lua number convertible to a `Integer` will be represented as this variant.
Integer(Integer),
2017-06-17 20:23:17 -05:00
/// A floating point number.
Number(Number),
2017-06-17 20:23:17 -05:00
/// An interned string, managed by Lua.
///
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
String(String<'lua>),
2017-06-17 20:23:17 -05:00
/// Reference to a Lua table.
Table(Table<'lua>),
2017-06-17 20:23:17 -05:00
/// Reference to a Lua function (or closure).
Function(Function<'lua>),
2017-06-17 20:23:17 -05:00
/// Reference to a Lua thread (or coroutine).
Thread(Thread<'lua>),
/// Reference to a userdata object that holds a custom type which implements `UserData`.
/// Special builtin userdata types will be represented as other `Value` variants.
UserData(AnyUserData<'lua>),
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
Error(Error),
2017-05-21 18:50:59 -05:00
}
2017-07-24 09:40:00 -05:00
pub use self::Value::Nil;
2017-05-21 18:50:59 -05:00
2017-08-01 12:36:26 -05:00
impl<'lua> Value<'lua> {
pub(crate) fn type_name(&self) -> &'static str {
match *self {
Value::Nil => "nil",
Value::Boolean(_) => "boolean",
Value::LightUserData(_) => "light userdata",
Value::Integer(_) => "integer",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Table(_) => "table",
Value::Function(_) => "function",
Value::Thread(_) => "thread",
Value::UserData(_) => "userdata",
Value::Error(_) => "userdata",
}
}
}
/// Trait for types convertible to `Value`.
pub trait ToLua<'lua> {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>>;
2017-05-21 18:50:59 -05:00
}
/// Trait for types convertible from `Value`.
pub trait FromLua<'lua>: Sized {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
fn from_lua(lua_value: Value<'lua>, lua: &'lua Lua) -> Result<Self>;
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Multiple Lua values used for both argument passing and also for multiple return values.
2017-05-21 18:50:59 -05:00
#[derive(Debug, Clone)]
pub struct MultiValue<'lua>(VecDeque<Value<'lua>>);
2017-05-21 18:50:59 -05:00
impl<'lua> MultiValue<'lua> {
2017-09-14 15:59:59 -05:00
/// Creates an empty `MultiValue` containing no values.
pub fn new() -> MultiValue<'lua> {
MultiValue(VecDeque::new())
2017-05-21 18:50:59 -05:00
}
}
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue(VecDeque::from_iter(iter))
2017-05-21 18:50:59 -05:00
}
}
impl<'lua> IntoIterator for MultiValue<'lua> {
type Item = Value<'lua>;
type IntoIter = <VecDeque<Value<'lua>> as IntoIterator>::IntoIter;
2017-05-21 18:50:59 -05:00
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'lua> Deref for MultiValue<'lua> {
type Target = VecDeque<Value<'lua>>;
2017-05-21 18:50:59 -05:00
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'lua> DerefMut for MultiValue<'lua> {
2017-05-21 18:50:59 -05:00
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
2017-06-17 20:23:17 -05:00
/// Trait for types convertible to any number of Lua values.
///
/// This is a generalization of `ToLua`, allowing any number of resulting Lua values instead of just
/// one. Any type that implements `ToLua` will automatically implement this trait.
pub trait ToLuaMulti<'lua> {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>>;
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Trait for types that can be created from an arbitrary number of Lua values.
///
/// This is a generalization of `FromLua`, allowing an arbitrary number of Lua values to participate
/// in the conversion. Any type that implements `FromLua` will automatically implement this trait.
pub trait FromLuaMulti<'lua>: Sized {
2017-06-17 20:23:17 -05:00
/// Performs the conversion.
///
/// In case `values` contains more values than needed to perform the conversion, the excess
/// values should be ignored. This reflects the semantics of Lua when calling a function or
/// assigning values. Similarly, if not enough values are given, conversions should assume that
/// any missing values are nil.
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Handle to an internal Lua function.
2017-05-21 18:50:59 -05:00
#[derive(Clone, Debug)]
pub struct Function<'lua>(LuaRef<'lua>);
2017-05-21 18:50:59 -05:00
impl<'lua> Function<'lua> {
2017-06-17 20:23:17 -05:00
/// Calls the function, passing `args` as function arguments.
///
/// The function's return values are converted to the generic type `R`.
2017-07-24 17:16:43 -05:00
///
/// # Examples
///
/// Call Lua's built-in `tostring` function:
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Function, Result};
/// # fn try_main() -> Result<()> {
/// let lua = Lua::new();
/// let globals = lua.globals();
///
/// let tostring: Function = globals.get("tostring")?;
///
/// assert_eq!(tostring.call::<_, String>(123)?, "123");
///
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// Call a function with multiple arguments:
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Function, Result};
/// # fn try_main() -> Result<()> {
/// let lua = Lua::new();
///
/// let sum: Function = lua.eval(r#"
/// function(a, b)
/// return a + b
/// end
/// "#, None)?;
///
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
2017-07-24 17:16:43 -05:00
///
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
2017-05-21 18:50:59 -05:00
let lua = self.0.lua;
unsafe {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
stack_err_guard(lua.state, 0, || {
2017-05-21 18:50:59 -05:00
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(lua.state, nargs + 3);
2017-05-21 18:50:59 -05:00
let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(lua.state, &self.0);
2017-05-21 18:50:59 -05:00
for arg in args {
lua.push_value(lua.state, arg);
2017-05-21 18:50:59 -05:00
}
2017-06-15 09:26:39 -05:00
handle_error(
lua.state,
pcall_with_traceback(lua.state, nargs, ffi::LUA_MULTRET),
)?;
2017-05-21 18:50:59 -05:00
let nresults = ffi::lua_gettop(lua.state) - stack_start;
let mut results = MultiValue::new();
2017-05-21 18:50:59 -05:00
for _ in 0..nresults {
results.push_front(lua.pop_value(lua.state));
2017-05-21 18:50:59 -05:00
}
R::from_lua_multi(results, lua)
})
}
}
2017-07-24 17:05:36 -05:00
/// Returns a function that, when called, calls `self`, passing `args` as the first set of
2017-06-17 20:23:17 -05:00
/// arguments.
///
2017-07-24 17:05:36 -05:00
/// If any arguments are passed to the returned function, they will be passed after `args`.
2017-06-17 20:23:17 -05:00
///
2017-07-24 17:16:43 -05:00
/// # Examples
2017-06-21 17:38:08 -05:00
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Function, Result};
/// # fn try_main() -> Result<()> {
2017-06-21 17:38:08 -05:00
/// let lua = Lua::new();
///
2017-07-24 17:05:36 -05:00
/// let sum: Function = lua.eval(r#"
/// function(a, b)
/// return a + b
/// end
/// "#, None)?;
///
/// let bound_a = sum.bind(1)?;
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
2017-06-21 17:38:08 -05:00
///
2017-07-24 17:05:36 -05:00
/// let bound_a_and_b = sum.bind(13)?.bind(57)?;
/// assert_eq!(bound_a_and_b.call::<_, u32>(())?, 13 + 57);
2017-06-21 17:38:08 -05:00
///
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
2017-06-21 17:38:08 -05:00
/// # }
/// ```
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
2017-05-21 18:50:59 -05:00
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
2017-10-23 13:50:47 -05:00
check_stack(state, nbinds + 2);
ffi::lua_settop(state, nargs + nbinds + 1);
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
2017-05-21 18:50:59 -05:00
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
2017-10-23 13:50:47 -05:00
ffi::lua_replace(state, 1);
2017-05-21 18:50:59 -05:00
for i in 0..nbinds {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
2017-10-23 13:50:47 -05:00
ffi::lua_replace(state, i + 2);
2017-05-21 18:50:59 -05:00
}
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
ffi::lua_gettop(state)
}
let lua = self.0.lua;
unsafe {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
stack_err_guard(lua.state, 0, || {
2017-05-21 18:50:59 -05:00
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(lua.state, nargs + 2);
lua.push_ref(lua.state, &self.0);
2017-05-21 18:50:59 -05:00
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(lua.state, arg);
2017-05-21 18:50:59 -05:00
}
ffi::lua_pushcclosure(lua.state, bind_call_impl, nargs + 2);
Ok(Function(lua.pop_ref(lua.state)))
2017-05-21 18:50:59 -05:00
})
}
}
}
2017-06-17 20:23:17 -05:00
/// Status of a Lua thread (or coroutine).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ThreadStatus {
/// The thread was just created, or is suspended because it has called `coroutine.yield`.
///
/// If a thread is in this state, it can be resumed by calling [`Thread::resume`].
///
/// [`Thread::resume`]: struct.Thread.html#method.resume
Resumable,
/// Either the thread has finished executing, or the thread is currently running.
Unresumable,
/// The thread has raised a Lua error during execution.
Error,
}
2017-06-17 20:23:17 -05:00
/// Handle to an internal Lua thread (or coroutine).
#[derive(Clone, Debug)]
pub struct Thread<'lua>(LuaRef<'lua>);
impl<'lua> Thread<'lua> {
2017-06-17 20:23:17 -05:00
/// Resumes execution of this thread.
///
/// Equivalent to `coroutine.resume`.
///
/// Passes `args` as arguments to the thread. If the coroutine has called `coroutine.yield`, it
/// will return these arguments. Otherwise, the coroutine wasn't yet started, so the arguments
/// are passed to its main function.
///
/// If the thread is no longer in `Active` state (meaning it has finished execution or
2017-07-24 17:16:43 -05:00
/// encountered an error), this will return `Err(CoroutineInactive)`, otherwise will return `Ok`
/// as follows:
2017-06-17 20:23:17 -05:00
///
/// If the thread calls `coroutine.yield`, returns the values passed to `yield`. If the thread
/// `return`s values from its main function, returns those.
///
2017-07-24 17:16:43 -05:00
/// # Examples
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Thread, Error, Result};
/// # fn try_main() -> Result<()> {
/// let lua = Lua::new();
/// let thread: Thread = lua.eval(r#"
/// coroutine.create(function(arg)
/// assert(arg == 42)
/// local yieldarg = coroutine.yield(123)
/// assert(yieldarg == 43)
/// return 987
/// end)
/// "#, None).unwrap();
///
/// assert_eq!(thread.resume::<_, u32>(42).unwrap(), 123);
/// assert_eq!(thread.resume::<_, u32>(43).unwrap(), 987);
///
/// // The coroutine has now returned, so `resume` will fail
/// match thread.resume::<_, u32>(()) {
/// Err(Error::CoroutineInactive) => {},
/// unexpected => panic!("unexpected result {:?}", unexpected),
/// }
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn resume<A, R>(&self, args: A) -> Result<R>
2017-06-17 22:50:40 -05:00
where
A: ToLuaMulti<'lua>,
R: FromLuaMulti<'lua>,
{
let lua = self.0.lua;
unsafe {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
let status = ffi::lua_status(thread_state);
2017-06-17 22:50:40 -05:00
if status != ffi::LUA_YIELD && ffi::lua_gettop(thread_state) == 0 {
return Err(Error::CoroutineInactive);
2017-06-17 22:50:40 -05:00
}
2017-06-17 22:50:40 -05:00
ffi::lua_pop(lua.state, 1);
2017-06-17 22:50:40 -05:00
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(thread_state, nargs);
2017-06-17 22:50:40 -05:00
for arg in args {
lua.push_value(thread_state, arg);
2017-06-17 22:50:40 -05:00
}
2017-06-17 22:50:40 -05:00
handle_error(
thread_state,
2017-06-17 22:50:40 -05:00
resume_with_traceback(thread_state, lua.state, nargs),
)?;
let nresults = ffi::lua_gettop(thread_state);
let mut results = MultiValue::new();
2017-06-17 22:50:40 -05:00
for _ in 0..nresults {
results.push_front(lua.pop_value(thread_state));
}
2017-06-17 22:50:40 -05:00
R::from_lua_multi(results, lua)
})
}
}
2017-06-17 20:23:17 -05:00
/// Gets the status of the thread.
pub fn status(&self) -> ThreadStatus {
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
ffi::lua_pop(lua.state, 1);
let status = ffi::lua_status(thread_state);
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
ThreadStatus::Error
} else if status == ffi::LUA_YIELD || ffi::lua_gettop(thread_state) > 0 {
ThreadStatus::Resumable
} else {
ThreadStatus::Unresumable
}
})
}
}
}
2017-06-17 20:23:17 -05:00
/// Top level Lua struct which holds the Lua state itself.
2017-05-21 18:50:59 -05:00
pub struct Lua {
pub(crate) state: *mut ffi::lua_State,
main_state: *mut ffi::lua_State,
2017-05-21 18:50:59 -05:00
ephemeral: bool,
}
impl Drop for Lua {
fn drop(&mut self) {
unsafe {
if !self.ephemeral {
ffi::lua_close(self.state);
2017-05-21 18:50:59 -05:00
}
}
}
}
impl Lua {
2017-06-17 20:23:17 -05:00
/// Creates a new Lua state.
///
/// Also loads the standard library.
2017-05-21 18:50:59 -05:00
pub fn new() -> Lua {
unsafe extern "C" fn allocator(
_: *mut c_void,
ptr: *mut c_void,
_: usize,
nsize: usize,
) -> *mut c_void {
if nsize == 0 {
libc::free(ptr as *mut libc::c_void);
ptr::null_mut()
} else {
let p = libc::realloc(ptr as *mut libc::c_void, nsize);
if p.is_null() {
// We must abort on OOM, because otherwise this will result in an unsafe
// longjmp.
eprintln!("Out of memory in Lua allocation, aborting!");
process::abort()
} else {
p as *mut c_void
}
}
}
2017-05-21 18:50:59 -05:00
unsafe {
let state = ffi::lua_newstate(allocator, ptr::null_mut());
2017-05-21 18:50:59 -05:00
stack_guard(state, 0, || {
// Do not open the debug library, currently it can be used to cause unsafety.
ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1);
ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1);
ffi::luaL_requiref(state, cstr!("table"), ffi::luaopen_table, 1);
ffi::luaL_requiref(state, cstr!("io"), ffi::luaopen_io, 1);
ffi::luaL_requiref(state, cstr!("os"), ffi::luaopen_os, 1);
ffi::luaL_requiref(state, cstr!("string"), ffi::luaopen_string, 1);
ffi::luaL_requiref(state, cstr!("utf8"), ffi::luaopen_utf8, 1);
ffi::luaL_requiref(state, cstr!("math"), ffi::luaopen_math, 1);
ffi::luaL_requiref(state, cstr!("package"), ffi::luaopen_package, 1);
ffi::lua_pop(state, 9);
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
// Create the userdata registry table
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
state,
&LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void,
);
push_userdata::<HashMap<TypeId, c_int>>(state, HashMap::new());
2017-05-21 18:50:59 -05:00
ffi::lua_newtable(state);
push_string(state, "__gc");
ffi::lua_pushcfunction(state, userdata_destructor::<HashMap<TypeId, c_int>>);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
ffi::lua_setmetatable(state, -2);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
2017-05-21 18:50:59 -05:00
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
// Create the function metatable
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
2017-05-21 18:50:59 -05:00
ffi::lua_newtable(state);
push_string(state, "__gc");
ffi::lua_pushcfunction(state, userdata_destructor::<RefCell<Callback>>);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
push_string(state, "__metatable");
ffi::lua_pushboolean(state, 0);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
2017-05-21 18:50:59 -05:00
// Override pcall, xpcall, and setmetatable with versions that cannot be used to
// cause unsafety.
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
2017-05-21 18:50:59 -05:00
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
push_string(state, "pcall");
ffi::lua_pushcfunction(state, safe_pcall);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
push_string(state, "xpcall");
ffi::lua_pushcfunction(state, safe_xpcall);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
push_string(state, "setmetatable");
ffi::lua_pushcfunction(state, safe_setmetatable);
ffi::lua_rawset(state, -3);
2017-05-21 18:50:59 -05:00
ffi::lua_pop(state, 1);
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
});
2017-05-21 18:50:59 -05:00
Lua {
state,
main_state: state,
2017-05-21 18:50:59 -05:00
ephemeral: false,
}
}
}
/// Loads a chunk of Lua code and returns it as a function.
2017-06-17 20:23:17 -05:00
///
/// The source can be named by setting the `name` parameter. This is generally recommended as it
/// results in better error traces.
2017-06-17 20:23:17 -05:00
///
/// Equivalent to Lua's `load` function.
pub fn load(&self, source: &str, name: Option<&str>) -> Result<Function> {
2017-05-21 18:50:59 -05:00
unsafe {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
stack_err_guard(self.state, 0, || {
2017-06-15 09:26:39 -05:00
handle_error(
self.state,
if let Some(name) = name {
let name = CString::new(name.to_owned()).map_err(|e| {
2017-08-01 12:36:26 -05:00
Error::ToLuaConversionError {
from: "&str",
to: "string",
message: Some(e.to_string()),
}
})?;
2017-06-15 09:26:39 -05:00
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
name.as_ptr(),
)
} else {
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
)
},
)?;
2017-05-21 18:50:59 -05:00
Ok(Function(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
})
}
}
/// Execute a chunk of Lua code.
///
/// This is equivalent to simply loading the source with `load` and then calling the resulting
/// function with no arguments.
///
/// Returns the values returned by the chunk.
pub fn exec<'lua, R: FromLuaMulti<'lua>>(
&'lua self,
source: &str,
name: Option<&str>,
) -> Result<R> {
self.load(source, name)?.call(())
}
2017-06-17 20:23:17 -05:00
/// Evaluate the given expression or chunk inside this Lua state.
///
/// If `source` is an expression, returns the value it evaluates to. Otherwise, returns the
/// values returned by the chunk (if any).
pub fn eval<'lua, R: FromLuaMulti<'lua>>(
&'lua self,
source: &str,
name: Option<&str>,
) -> Result<R> {
// First, try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the
// actual lua repl does.
self.load(&format!("return {}", source), name)
.or_else(|_| self.load(source, name))?
.call(())
}
2017-07-24 19:13:11 -05:00
/// Pass a `&str` slice to Lua, creating and returning an interned Lua string.
pub fn create_string(&self, s: &str) -> String {
2017-05-21 18:50:59 -05:00
unsafe {
stack_guard(self.state, 0, || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 1);
2017-05-21 18:50:59 -05:00
ffi::lua_pushlstring(self.state, s.as_ptr() as *const c_char, s.len());
String(self.pop_ref(self.state))
2017-05-21 18:50:59 -05:00
})
}
}
2017-06-17 20:23:17 -05:00
/// Creates and returns a new table.
pub fn create_table(&self) -> Table {
2017-05-21 18:50:59 -05:00
unsafe {
stack_guard(self.state, 0, || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 1);
2017-05-21 18:50:59 -05:00
ffi::lua_newtable(self.state);
Table(self.pop_ref(self.state))
2017-05-21 18:50:59 -05:00
})
}
}
2017-06-17 20:23:17 -05:00
/// Creates a table and fills it with values from an iterator.
pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> Result<Table<'lua>>
2017-06-15 09:26:39 -05:00
where
K: ToLua<'lua>,
V: ToLua<'lua>,
I: IntoIterator<Item = (K, V)>,
2017-05-21 18:50:59 -05:00
{
unsafe {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
stack_err_guard(self.state, 0, || {
check_stack(self.state, 3);
ffi::lua_newtable(self.state);
for (k, v) in cont {
self.push_value(self.state, k.to_lua(self)?);
self.push_value(self.state, v.to_lua(self)?);
ffi::lua_rawset(self.state, -3);
}
Ok(Table(self.pop_ref(self.state)))
})
2017-05-21 18:50:59 -05:00
}
}
2017-06-17 20:23:17 -05:00
/// Creates a table from an iterator of values, using `1..` as the keys.
pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> Result<Table<'lua>>
2017-06-15 09:26:39 -05:00
where
T: ToLua<'lua>,
I: IntoIterator<Item = T>,
2017-05-21 18:50:59 -05:00
{
self.create_table_from(cont.into_iter().enumerate().map(|(k, v)| (k + 1, v)))
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
2017-07-25 18:54:40 -05:00
///
/// # Examples
///
/// Create a function which prints its argument:
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> {
/// let lua = Lua::new();
///
/// let greet = lua.create_function(|_, name: String| {
2017-07-25 18:54:40 -05:00
/// println!("Hello, {}!", name);
/// Ok(())
2017-07-25 18:54:40 -05:00
/// });
/// # let _ = greet; // used
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// Use tuples to accept multiple arguments:
2017-07-25 18:54:40 -05:00
///
/// ```
/// # extern crate rlua;
/// # use rlua::{Lua, Result};
/// # fn try_main() -> Result<()> {
/// let lua = Lua::new();
///
/// let print_person = lua.create_function(|_, (name, age): (String, u8)| {
2017-07-25 18:54:40 -05:00
/// println!("{} is {} years old!", name, age);
/// Ok(())
2017-07-25 18:54:40 -05:00
/// });
/// # let _ = print_person; // used
/// # Ok(())
/// # }
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn create_function<'lua, A, R, F>(&'lua self, mut func: F) -> Function<'lua>
2017-06-15 09:26:39 -05:00
where
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + FnMut(&'lua Lua, A) -> Result<R>,
2017-05-21 18:50:59 -05:00
{
self.create_callback_function(Box::new(move |lua, args| {
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}))
2017-05-21 18:50:59 -05:00
}
2017-06-17 20:23:17 -05:00
/// Wraps a Lua function into a new thread (or coroutine).
///
/// Equivalent to `coroutine.create`.
pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Thread<'lua> {
unsafe {
stack_guard(self.state, 0, move || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 1);
let thread_state = ffi::lua_newthread(self.state);
self.push_ref(thread_state, &func.0);
Thread(self.pop_ref(self.state))
})
}
}
2017-06-17 20:23:17 -05:00
/// Create a Lua userdata object from a custom userdata type.
pub fn create_userdata<T>(&self, data: T) -> AnyUserData
2017-06-15 09:26:39 -05:00
where
T: UserData,
2017-05-21 18:50:59 -05:00
{
unsafe {
stack_guard(self.state, 0, move || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 2);
2017-05-21 18:50:59 -05:00
push_userdata::<RefCell<T>>(self.state, RefCell::new(data));
2017-05-21 18:50:59 -05:00
2017-06-15 09:26:39 -05:00
ffi::lua_rawgeti(
self.state,
ffi::LUA_REGISTRYINDEX,
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
self.userdata_metatable::<T>() as ffi::lua_Integer,
2017-06-15 09:26:39 -05:00
);
2017-05-21 18:50:59 -05:00
ffi::lua_setmetatable(self.state, -2);
AnyUserData(self.pop_ref(self.state))
2017-05-21 18:50:59 -05:00
})
}
}
2017-06-17 20:23:17 -05:00
/// Returns a handle to the global environment.
pub fn globals(&self) -> Table {
2017-05-21 18:50:59 -05:00
unsafe {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
stack_guard(self.state, 0, move || {
check_stack(self.state, 1);
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
Table(self.pop_ref(self.state))
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
})
2017-05-21 18:50:59 -05:00
}
}
2017-06-17 20:23:17 -05:00
/// Coerces a Lua value to a string.
///
/// The value must be a string (in which case this is a no-op) or a number.
pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Result<String<'lua>> {
2017-05-21 18:50:59 -05:00
match v {
Value::String(s) => Ok(s),
2017-05-21 18:50:59 -05:00
v => unsafe {
stack_guard(self.state, 0, || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 1);
2017-08-01 12:36:26 -05:00
let ty = v.type_name();
self.push_value(self.state, v);
2017-05-21 18:50:59 -05:00
if ffi::lua_tostring(self.state, -1).is_null() {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
ffi::lua_pop(self.state, 1);
2017-08-01 12:36:26 -05:00
Err(Error::FromLuaConversionError {
from: ty,
to: "String",
message: Some("expected string or number".to_string()),
2017-08-01 12:36:26 -05:00
})
2017-05-21 18:50:59 -05:00
} else {
Ok(String(self.pop_ref(self.state)))
2017-05-21 18:50:59 -05:00
}
})
},
}
}
2017-06-17 20:23:17 -05:00
/// Coerces a Lua value to an integer.
///
/// The value must be an integer, or a floating point number or a string that can be converted
/// to an integer. Refer to the Lua manual for details.
pub fn coerce_integer(&self, v: Value) -> Result<Integer> {
2017-05-21 18:50:59 -05:00
match v {
Value::Integer(i) => Ok(i),
2017-05-21 18:50:59 -05:00
v => unsafe {
stack_guard(self.state, 0, || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 1);
2017-08-01 12:36:26 -05:00
let ty = v.type_name();
self.push_value(self.state, v);
2017-05-21 18:50:59 -05:00
let mut isint = 0;
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
ffi::lua_pop(self.state, 1);
2017-05-21 18:50:59 -05:00
if isint == 0 {
2017-08-01 12:36:26 -05:00
Err(Error::FromLuaConversionError {
from: ty,
to: "integer",
message: None,
})
2017-05-21 18:50:59 -05:00
} else {
Ok(i)
}
})
},
}
}
2017-06-17 20:23:17 -05:00
/// Coerce a Lua value to a number.
///
/// The value must be a number or a string that can be converted to a number. Refer to the Lua
/// manual for details.
pub fn coerce_number(&self, v: Value) -> Result<Number> {
2017-05-21 18:50:59 -05:00
match v {
Value::Number(n) => Ok(n),
2017-05-21 18:50:59 -05:00
v => unsafe {
stack_guard(self.state, 0, || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 1);
2017-08-01 12:36:26 -05:00
let ty = v.type_name();
self.push_value(self.state, v);
2017-05-21 18:50:59 -05:00
let mut isnum = 0;
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
ffi::lua_pop(self.state, 1);
2017-05-21 18:50:59 -05:00
if isnum == 0 {
2017-08-01 12:36:26 -05:00
Err(Error::FromLuaConversionError {
from: ty,
to: "number",
message: Some("number or string coercible to number".to_string()),
2017-08-01 12:36:26 -05:00
})
2017-05-21 18:50:59 -05:00
} else {
Ok(n)
}
})
},
}
}
/// Converts a value that implements `ToLua` into a `Value` instance.
pub fn pack<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<Value<'lua>> {
2017-05-21 18:50:59 -05:00
t.to_lua(self)
}
/// Converts a `Value` instance into a value that implements `FromLua`.
pub fn unpack<'lua, T: FromLua<'lua>>(&'lua self, value: Value<'lua>) -> Result<T> {
2017-05-21 18:50:59 -05:00
T::from_lua(value, self)
}
/// Converts a value that implements `ToLuaMulti` into a `MultiValue` instance.
pub fn pack_multi<'lua, T: ToLuaMulti<'lua>>(&'lua self, t: T) -> Result<MultiValue<'lua>> {
2017-05-21 18:50:59 -05:00
t.to_lua_multi(self)
}
/// Converts a `MultiValue` instance into a value that implements `FromLuaMulti`.
pub fn unpack_multi<'lua, T: FromLuaMulti<'lua>>(
&'lua self,
value: MultiValue<'lua>,
) -> Result<T> {
2017-05-21 18:50:59 -05:00
T::from_lua_multi(value, self)
}
fn create_callback_function<'lua>(&'lua self, func: Callback<'lua>) -> Function<'lua> {
2017-05-21 18:50:59 -05:00
unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int {
callback_error(state, || {
let lua = Lua {
state: state,
main_state: main_state(state),
2017-05-21 18:50:59 -05:00
ephemeral: true,
};
let func = get_userdata::<RefCell<Callback>>(state, ffi::lua_upvalueindex(1));
let mut func = if let Ok(func) = (*func).try_borrow_mut() {
func
} else {
lua_panic!(
state,
"recursive callback function call would mutably borrow function twice"
);
};
2017-05-21 18:50:59 -05:00
let nargs = ffi::lua_gettop(state);
let mut args = MultiValue::new();
2017-05-21 18:50:59 -05:00
for _ in 0..nargs {
args.push_front(lua.pop_value(state));
2017-05-21 18:50:59 -05:00
}
let results = func.deref_mut()(&lua, args)?;
2017-05-21 18:50:59 -05:00
let nresults = results.len() as c_int;
2017-10-23 14:17:46 -05:00
check_stack(state, nresults);
2017-05-21 18:50:59 -05:00
for r in results {
lua.push_value(state, r);
2017-05-21 18:50:59 -05:00
}
Ok(nresults)
})
}
unsafe {
stack_guard(self.state, 0, move || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 2);
2017-05-21 18:50:59 -05:00
push_userdata::<RefCell<Callback>>(self.state, RefCell::new(func));
2017-05-21 18:50:59 -05:00
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
self.state,
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
2017-05-21 18:50:59 -05:00
ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX);
ffi::lua_setmetatable(self.state, -2);
ffi::lua_pushcclosure(self.state, callback_call_impl, 1);
Function(self.pop_ref(self.state))
2017-05-21 18:50:59 -05:00
})
}
}
2017-10-23 14:17:46 -05:00
// Used 1 stack space, does not call checkstack
pub(crate) unsafe fn push_value(&self, state: *mut ffi::lua_State, value: Value) {
match value {
Value::Nil => {
ffi::lua_pushnil(state);
}
Value::Boolean(b) => {
ffi::lua_pushboolean(state, if b { 1 } else { 0 });
}
Value::LightUserData(ud) => {
ffi::lua_pushlightuserdata(state, ud.0);
}
Value::Integer(i) => {
ffi::lua_pushinteger(state, i);
}
Value::Number(n) => {
ffi::lua_pushnumber(state, n);
}
Value::String(s) => {
self.push_ref(state, &s.0);
}
Value::Table(t) => {
self.push_ref(state, &t.0);
}
Value::Function(f) => {
self.push_ref(state, &f.0);
}
Value::Thread(t) => {
self.push_ref(state, &t.0);
}
Value::UserData(ud) => {
self.push_ref(state, &ud.0);
}
Value::Error(e) => {
push_wrapped_error(state, e);
}
}
}
pub(crate) unsafe fn pop_value(&self, state: *mut ffi::lua_State) -> Value {
match ffi::lua_type(state, -1) {
ffi::LUA_TNIL => {
ffi::lua_pop(state, 1);
Nil
}
ffi::LUA_TBOOLEAN => {
let b = Value::Boolean(ffi::lua_toboolean(state, -1) != 0);
ffi::lua_pop(state, 1);
b
}
ffi::LUA_TLIGHTUSERDATA => {
let ud = Value::LightUserData(LightUserData(ffi::lua_touserdata(state, -1)));
ffi::lua_pop(state, 1);
ud
}
2017-10-23 15:42:20 -05:00
ffi::LUA_TNUMBER => if ffi::lua_isinteger(state, -1) != 0 {
let i = Value::Integer(ffi::lua_tointeger(state, -1));
ffi::lua_pop(state, 1);
i
} else {
let n = Value::Number(ffi::lua_tonumber(state, -1));
ffi::lua_pop(state, 1);
n
},
ffi::LUA_TSTRING => Value::String(String(self.pop_ref(state))),
ffi::LUA_TTABLE => Value::Table(Table(self.pop_ref(state))),
ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref(state))),
ffi::LUA_TUSERDATA => {
// It should not be possible to interact with userdata types
// other than custom UserData types OR a WrappedError.
// WrappedPanic should never be able to be caught in lua, so it
// should never be here.
if let Some(err) = pop_wrapped_error(state) {
Value::Error(err)
} else {
Value::UserData(AnyUserData(self.pop_ref(state)))
}
}
ffi::LUA_TTHREAD => Value::Thread(Thread(self.pop_ref(state))),
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
_ => unreachable!("internal error: LUA_TNONE in pop_value"),
}
}
2017-10-23 14:17:46 -05:00
// Used 1 stack space, does not call checkstack
pub(crate) unsafe fn push_ref(&self, state: *mut ffi::lua_State, lref: &LuaRef) {
2017-06-15 09:26:39 -05:00
assert_eq!(
lref.lua.main_state,
self.main_state,
"Lua instance passed Value created from a different Lua"
2017-06-15 09:26:39 -05:00
);
ffi::lua_rawgeti(
state,
ffi::LUA_REGISTRYINDEX,
lref.registry_id as ffi::lua_Integer,
);
}
// Pops the topmost element of the stack and stores a reference to it in the
// registry.
//
// This pins the object, preventing garbage collection until the returned
// `LuaRef` is dropped.
pub(crate) unsafe fn pop_ref(&self, state: *mut ffi::lua_State) -> LuaRef {
let registry_id = ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
LuaRef {
lua: self,
registry_id: registry_id,
}
}
pub(crate) unsafe fn userdata_metatable<T: UserData>(&self) -> c_int {
2017-05-21 18:50:59 -05:00
// Used if both an __index metamethod is set and regular methods, checks methods table
// first, then __index metamethod.
unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int {
2017-10-23 14:17:46 -05:00
check_stack(state, 2);
2017-05-21 18:50:59 -05:00
ffi::lua_pushvalue(state, -1);
ffi::lua_gettable(state, ffi::lua_upvalueindex(1));
if ffi::lua_isnil(state, -1) == 0 {
ffi::lua_insert(state, -3);
ffi::lua_pop(state, 2);
1
} else {
ffi::lua_pop(state, 1);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(2));
ffi::lua_insert(state, -3);
ffi::lua_call(state, 2, 1);
1
}
}
stack_guard(self.state, 0, move || {
Another major API change, out of stack space is not an Err It, ahem "should not" be possible to exhaust lua stack space in normal usage, and causing stack errors to be Err is slightly obnoxious. I have been wanting to make this change for a while, and removing the callback API from tables makes this sensible *I think*. I can think of a couple of ways that this is not technically true, but I think that they are acceptable, or should be handled differently. One, you can make arbitrarily sized LuaVariadic values. I think this is maybe a bug already, because there is an argument limit in Lua which is lower than the stack limit. I'm not sure what happens there, but if it is a stack based panic, (or any panic?) it is a bug. Two, I believe that if you recurse over and over between lua -> rust -> lua -> rust etc, and call rlua API functions, you might get a stack panic. I think for trusted lua code, this is morally equivalent to a regular stack overflow in plain rust, which is already.. well it's not a panic but it's some kind of safe crash I'm not sure, so I think this is acceptable. For *untrusted* lua code, this could theoretically be a problem if the API provided a callback that would call back into lua, then some lua script could force a stack based panic. There are so many concerns with untrusted lua code, and this library is NOT safe enough yet for untrusted code (it doesn't even provide an option to limit lua to the safe API subset yet!), so this is not currently an issue. When the library provides support for "safe lua", it should come with big warnings anyway, and being able to force a stack panic is pretty minor in comparison. I think if there are other ways to cause unbounded stack usage, that it is a bug, or there can be an error just for that situation, like argument count limits. This commit also fixes several stupid bugs with tests, stack checking, and panics.
2017-06-25 15:52:32 -05:00
check_stack(self.state, 5);
2017-05-21 18:50:59 -05:00
2017-06-15 09:26:39 -05:00
ffi::lua_pushlightuserdata(
self.state,
&LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void,
);
2017-05-21 18:50:59 -05:00
ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX);
let registered_userdata = get_userdata::<HashMap<TypeId, c_int>>(self.state, -1);
2017-05-21 18:50:59 -05:00
ffi::lua_pop(self.state, 1);
if let Some(table_id) = (*registered_userdata).get(&TypeId::of::<T>()) {
return *table_id;
}
2017-05-21 18:50:59 -05:00
let mut methods = UserDataMethods {
methods: HashMap::new(),
meta_methods: HashMap::new(),
_type: PhantomData,
};
T::add_methods(&mut methods);
2017-05-21 18:50:59 -05:00
ffi::lua_newtable(self.state);
2017-05-21 18:50:59 -05:00
let has_methods = !methods.methods.is_empty();
2017-05-21 18:50:59 -05:00
if has_methods {
push_string(self.state, "__index");
ffi::lua_newtable(self.state);
2017-05-21 18:50:59 -05:00
for (k, m) in methods.methods {
push_string(self.state, &k);
self.push_value(
self.state,
Value::Function(self.create_callback_function(m)),
);
ffi::lua_rawset(self.state, -3);
}
2017-05-21 18:50:59 -05:00
ffi::lua_rawset(self.state, -3);
}
2017-05-21 18:50:59 -05:00
for (k, m) in methods.meta_methods {
if k == MetaMethod::Index && has_methods {
push_string(self.state, "__index");
ffi::lua_pushvalue(self.state, -1);
ffi::lua_gettable(self.state, -3);
self.push_value(
self.state,
Value::Function(self.create_callback_function(m)),
);
ffi::lua_pushcclosure(self.state, meta_index_impl, 2);
ffi::lua_rawset(self.state, -3);
} else {
let name = match k {
MetaMethod::Add => "__add",
MetaMethod::Sub => "__sub",
MetaMethod::Mul => "__mul",
MetaMethod::Div => "__div",
MetaMethod::Mod => "__mod",
MetaMethod::Pow => "__pow",
MetaMethod::Unm => "__unm",
MetaMethod::IDiv => "__idiv",
MetaMethod::BAnd => "__band",
MetaMethod::BOr => "__bor",
MetaMethod::BXor => "__bxor",
MetaMethod::BNot => "__bnot",
MetaMethod::Shl => "__shl",
MetaMethod::Shr => "__shr",
MetaMethod::Concat => "__concat",
MetaMethod::Len => "__len",
MetaMethod::Eq => "__eq",
MetaMethod::Lt => "__lt",
MetaMethod::Le => "__le",
MetaMethod::Index => "__index",
MetaMethod::NewIndex => "__newindex",
MetaMethod::Call => "__call",
MetaMethod::ToString => "__tostring",
};
push_string(self.state, name);
self.push_value(
self.state,
Value::Function(self.create_callback_function(m)),
);
ffi::lua_rawset(self.state, -3);
2017-05-21 18:50:59 -05:00
}
}
push_string(self.state, "__gc");
ffi::lua_pushcfunction(self.state, userdata_destructor::<RefCell<T>>);
ffi::lua_rawset(self.state, -3);
push_string(self.state, "__metatable");
ffi::lua_pushboolean(self.state, 0);
ffi::lua_rawset(self.state, -3);
let id = ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX);
(*registered_userdata).insert(TypeId::of::<T>(), id);
id
2017-05-21 18:50:59 -05:00
})
}
}
static LUA_USERDATA_REGISTRY_KEY: u8 = 0;
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;