mlua/src/tests.rs

656 lines
19 KiB
Rust
Raw Normal View History

2017-05-21 18:50:59 -05:00
use std::fmt;
use std::error;
2017-05-21 18:50:59 -05:00
use std::panic::catch_unwind;
2017-10-23 15:42:20 -05:00
use {Error, ExternalError, Function, Lua, Result, Table, Thread, ThreadStatus, Value, Variadic};
2017-05-21 18:50:59 -05:00
#[test]
fn test_load() {
let lua = Lua::new();
let func = lua.load("return 1+2", None).unwrap();
let result: i32 = func.call(()).unwrap();
assert_eq!(result, 3);
assert!(lua.load("§$%§&$%&", None).is_err());
}
#[test]
fn test_load_debug() {
let lua = Lua::new();
2017-10-29 17:24:54 -05:00
lua.exec::<()>("assert(debug == nil)", None).unwrap();
unsafe {
lua.load_debug();
}
match lua.eval("debug", None).unwrap() {
Value::Table(_) => {},
val => {
panic!("Expected table for debug library, got {:#?}", val)
}
}
let traceback_output = lua.eval::<String>("debug.traceback()", None).unwrap();
assert_eq!(traceback_output.split("\n").next(), "stack traceback:".into());
}
2017-05-21 18:50:59 -05:00
#[test]
fn test_exec() {
2017-05-21 18:50:59 -05:00
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
res = 'foo'..'bar'
"#,
None,
).unwrap();
assert_eq!(globals.get::<_, String>("res").unwrap(), "foobar");
let module: Table = lua.exec(
r#"
local module = {}
function module.func()
return "hello"
end
return module
"#,
None,
).unwrap();
assert!(module.contains_key("func").unwrap());
2017-06-15 09:26:39 -05:00
assert_eq!(
module
.get::<_, Function>("func")
2017-06-15 09:26:39 -05:00
.unwrap()
.call::<_, String>(())
.unwrap(),
"hello"
);
2017-05-21 18:50:59 -05:00
}
#[test]
fn test_eval() {
let lua = Lua::new();
assert_eq!(lua.eval::<i32>("1 + 1", None).unwrap(), 2);
assert_eq!(lua.eval::<bool>("false == false", None).unwrap(), true);
assert_eq!(lua.eval::<i32>("return 1 + 2", None).unwrap(), 3);
match lua.eval::<()>("if true then", None) {
2017-10-23 15:42:20 -05:00
Err(Error::SyntaxError {
incomplete_input: true,
..
}) => {}
r => panic!(
"expected SyntaxError with incomplete_input=true, got {:?}",
r
),
2017-05-21 18:50:59 -05:00
}
}
#[test]
fn test_function() {
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
function concat(arg1, arg2)
return arg1 .. arg2
end
"#,
None,
).unwrap();
2017-05-21 18:50:59 -05:00
let concat = globals.get::<_, Function>("concat").unwrap();
assert_eq!(concat.call::<_, String>(("foo", "bar")).unwrap(), "foobar");
2017-05-21 18:50:59 -05:00
}
#[test]
fn test_bind() {
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
function concat(...)
local res = ""
for _, s in pairs({...}) do
res = res..s
end
return res
end
"#,
None,
).unwrap();
2017-05-21 18:50:59 -05:00
let mut concat = globals.get::<_, Function>("concat").unwrap();
2017-05-21 18:50:59 -05:00
concat = concat.bind("foo").unwrap();
concat = concat.bind("bar").unwrap();
concat = concat.bind(("baz", "baf")).unwrap();
2017-06-15 09:26:39 -05:00
assert_eq!(
concat.call::<_, String>(("hi", "wut")).unwrap(),
2017-06-15 09:26:39 -05:00
"foobarbazbafhiwut"
);
2017-05-21 18:50:59 -05:00
}
#[test]
fn test_rust_function() {
let lua = Lua::new();
let globals = lua.globals();
lua.exec::<()>(
r#"
function lua_function()
return rust_function()
end
2017-05-21 18:50:59 -05:00
-- Test to make sure chunk return is ignored
return 1
"#,
None,
).unwrap();
2017-05-21 18:50:59 -05:00
let lua_function = globals.get::<_, Function>("lua_function").unwrap();
2017-08-01 13:09:47 -05:00
let rust_function = lua.create_function(|_, ()| Ok("hello"));
2017-05-21 18:50:59 -05:00
globals.set("rust_function", rust_function).unwrap();
assert_eq!(lua_function.call::<_, String>(()).unwrap(), "hello");
2017-05-21 18:50:59 -05:00
}
#[test]
fn test_lua_multi() {
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
function concat(arg1, arg2)
return arg1 .. arg2
end
function mreturn()
return 1, 2, 3, 4, 5, 6
end
"#,
None,
).unwrap();
2017-05-21 18:50:59 -05:00
let concat = globals.get::<_, Function>("concat").unwrap();
let mreturn = globals.get::<_, Function>("mreturn").unwrap();
2017-05-21 18:50:59 -05:00
assert_eq!(concat.call::<_, String>(("foo", "bar")).unwrap(), "foobar");
let (a, b) = mreturn.call::<_, (u64, u64)>(()).unwrap();
2017-05-21 18:50:59 -05:00
assert_eq!((a, b), (1, 2));
let (a, b, v) = mreturn.call::<_, (u64, u64, Variadic<u64>)>(()).unwrap();
2017-05-21 18:50:59 -05:00
assert_eq!((a, b), (1, 2));
assert_eq!(v[..], [3, 4, 5, 6]);
2017-05-21 18:50:59 -05:00
}
#[test]
fn test_coercion() {
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
int = 123
str = "123"
num = 123.0
"#,
None,
).unwrap();
2017-05-21 18:50:59 -05:00
assert_eq!(globals.get::<_, String>("int").unwrap(), "123");
assert_eq!(globals.get::<_, i32>("str").unwrap(), 123);
assert_eq!(globals.get::<_, i32>("num").unwrap(), 123);
2017-05-21 18:50:59 -05:00
}
#[test]
fn test_error() {
#[derive(Debug)]
pub struct TestError;
impl fmt::Display for TestError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
2017-05-21 18:50:59 -05:00
write!(fmt, "test error")
}
}
impl error::Error for TestError {
2017-05-21 18:50:59 -05:00
fn description(&self) -> &str {
"test error"
}
fn cause(&self) -> Option<&error::Error> {
2017-05-21 18:50:59 -05:00
None
}
}
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
function no_error()
end
function lua_error()
error("this is a lua error")
end
function rust_error()
rust_error_function()
end
function return_error()
local status, res = pcall(rust_error_function)
assert(not status)
return res
end
function return_string_error()
return "this should be converted to an error"
end
2017-05-21 18:50:59 -05:00
function test_pcall()
local testvar = 0
pcall(function(arg)
testvar = testvar + arg
error("should be ignored")
end, 3)
local function handler(err)
testvar = testvar + err
return "should be ignored"
end
local status, res = xpcall(function()
2017-05-21 18:50:59 -05:00
error(5)
end, handler)
assert(not status)
2017-05-21 18:50:59 -05:00
if testvar ~= 8 then
error("testvar had the wrong value, pcall / xpcall misbehaving "..testvar)
end
end
function understand_recursion()
understand_recursion()
end
"#,
None,
).unwrap();
2017-05-21 18:50:59 -05:00
let rust_error_function =
2017-08-01 13:09:47 -05:00
lua.create_function(|_, ()| -> Result<()> { Err(TestError.to_lua_err()) });
globals
.set("rust_error_function", rust_error_function)
.unwrap();
2017-05-21 18:50:59 -05:00
let no_error = globals.get::<_, Function>("no_error").unwrap();
let lua_error = globals.get::<_, Function>("lua_error").unwrap();
let rust_error = globals.get::<_, Function>("rust_error").unwrap();
let return_error = globals.get::<_, Function>("return_error").unwrap();
2017-07-24 09:40:00 -05:00
let return_string_error = globals.get::<_, Function>("return_string_error").unwrap();
let test_pcall = globals.get::<_, Function>("test_pcall").unwrap();
2017-07-24 09:40:00 -05:00
let understand_recursion = globals.get::<_, Function>("understand_recursion").unwrap();
2017-05-21 18:50:59 -05:00
assert!(no_error.call::<_, ()>(()).is_ok());
match lua_error.call::<_, ()>(()) {
Err(Error::RuntimeError(_)) => {}
Err(_) => panic!("error is not RuntimeError kind"),
2017-06-17 22:50:40 -05:00
_ => panic!("error not returned"),
2017-05-21 18:50:59 -05:00
}
match rust_error.call::<_, ()>(()) {
Err(Error::CallbackError { .. }) => {}
Err(_) => panic!("error is not CallbackError kind"),
2017-06-17 22:50:40 -05:00
_ => panic!("error not returned"),
2017-05-21 18:50:59 -05:00
}
match return_error.call::<_, Value>(()) {
Ok(Value::Error(_)) => {}
_ => panic!("Value::Error not returned"),
}
assert!(return_string_error.call::<_, Error>(()).is_ok());
match lua.eval::<()>("if youre happy and you know it syntax error", None) {
2017-10-23 15:42:20 -05:00
Err(Error::SyntaxError {
incomplete_input: false,
..
}) => {}
Err(_) => panic!("error is not LuaSyntaxError::Syntax kind"),
_ => panic!("error not returned"),
}
match lua.eval::<()>("function i_will_finish_what_i()", None) {
2017-10-23 15:42:20 -05:00
Err(Error::SyntaxError {
incomplete_input: true,
..
}) => {}
Err(_) => panic!("error is not LuaSyntaxError::IncompleteStatement kind"),
_ => panic!("error not returned"),
}
2017-05-21 18:50:59 -05:00
test_pcall.call::<_, ()>(()).unwrap();
assert!(understand_recursion.call::<_, ()>(()).is_err());
match catch_unwind(|| -> Result<()> {
2017-05-21 18:50:59 -05:00
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
function rust_panic()
pcall(function () rust_panic_function() end)
end
"#,
None,
)?;
2017-08-01 13:09:47 -05:00
let rust_panic_function = lua.create_function(|_, ()| -> Result<()> {
2017-05-21 18:50:59 -05:00
panic!("expected panic, this panic should be caught in rust")
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
});
globals.set("rust_panic_function", rust_panic_function)?;
2017-05-21 18:50:59 -05:00
let rust_panic = globals.get::<_, Function>("rust_panic")?;
2017-05-21 18:50:59 -05:00
rust_panic.call::<_, ()>(())
}) {
Ok(Ok(_)) => panic!("no panic was detected, pcall caught it!"),
Ok(Err(e)) => panic!("error during panic test {:?}", e),
Err(_) => {}
};
match catch_unwind(|| -> Result<()> {
2017-05-21 18:50:59 -05:00
let lua = Lua::new();
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
let globals = lua.globals();
lua.exec::<()>(
r#"
2017-05-21 18:50:59 -05:00
function rust_panic()
xpcall(function() rust_panic_function() end, function() end)
end
"#,
None,
)?;
2017-08-01 13:09:47 -05:00
let rust_panic_function = lua.create_function(|_, ()| -> Result<()> {
2017-05-21 18:50:59 -05:00
panic!("expected panic, this panic should be caught in rust")
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
});
globals.set("rust_panic_function", rust_panic_function)?;
2017-05-21 18:50:59 -05:00
let rust_panic = globals.get::<_, Function>("rust_panic")?;
2017-05-21 18:50:59 -05:00
rust_panic.call::<_, ()>(())
}) {
Ok(Ok(_)) => panic!("no panic was detected, xpcall caught it!"),
Ok(Err(e)) => panic!("error during panic test {:?}", e),
Err(_) => {}
};
}
#[test]
fn test_thread() {
let lua = Lua::new();
let thread = lua.create_thread(
lua.eval::<Function>(
r#"
function (s)
local sum = s
for i = 1,4 do
sum = sum + coroutine.yield(sum)
end
return sum
end
"#,
None,
).unwrap(),
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
);
assert_eq!(thread.status(), ThreadStatus::Resumable);
2017-06-17 22:50:40 -05:00
assert_eq!(thread.resume::<_, i64>(0).unwrap(), 0);
assert_eq!(thread.status(), ThreadStatus::Resumable);
2017-06-17 22:50:40 -05:00
assert_eq!(thread.resume::<_, i64>(1).unwrap(), 1);
assert_eq!(thread.status(), ThreadStatus::Resumable);
2017-06-17 22:50:40 -05:00
assert_eq!(thread.resume::<_, i64>(2).unwrap(), 3);
assert_eq!(thread.status(), ThreadStatus::Resumable);
2017-06-17 22:50:40 -05:00
assert_eq!(thread.resume::<_, i64>(3).unwrap(), 6);
assert_eq!(thread.status(), ThreadStatus::Resumable);
2017-06-17 22:50:40 -05:00
assert_eq!(thread.resume::<_, i64>(4).unwrap(), 10);
assert_eq!(thread.status(), ThreadStatus::Unresumable);
let accumulate = lua.create_thread(
lua.eval::<Function>(
r#"
function (sum)
while true do
sum = sum + coroutine.yield(sum)
end
end
"#,
None,
).unwrap(),
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
);
for i in 0..4 {
accumulate.resume::<_, ()>(i).unwrap();
}
2017-06-17 22:50:40 -05:00
assert_eq!(accumulate.resume::<_, i64>(4).unwrap(), 10);
assert_eq!(accumulate.status(), ThreadStatus::Resumable);
assert!(accumulate.resume::<_, ()>("error").is_err());
assert_eq!(accumulate.status(), ThreadStatus::Error);
let thread = lua.eval::<Thread>(
r#"
coroutine.create(function ()
while true do
coroutine.yield(42)
end
end)
"#,
None,
).unwrap();
assert_eq!(thread.status(), ThreadStatus::Resumable);
2017-06-17 22:50:40 -05:00
assert_eq!(thread.resume::<_, i64>(()).unwrap(), 42);
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();
2017-06-17 22:50:40 -05:00
assert_eq!(thread.resume::<_, u32>(42).unwrap(), 123);
assert_eq!(thread.resume::<_, u32>(43).unwrap(), 987);
match thread.resume::<_, u32>(()) {
Err(Error::CoroutineInactive) => {}
2017-06-17 22:50:40 -05:00
Err(_) => panic!("resuming dead coroutine error is not CoroutineInactive kind"),
_ => panic!("resuming dead coroutine did not return error"),
}
}
#[test]
fn test_result_conversions() {
let lua = Lua::new();
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
let globals = lua.globals();
2017-08-01 13:09:47 -05:00
let err = lua.create_function(|_, ()| {
Ok(Err::<String, _>(
Big API incompatible error change, remove dependency on error_chain The current situation with error_chain is less than ideal, and there are lots of conflicting interests that are impossible to meet at once. Here is an unorganized brain dump of the current situation, stay awhile and listen! This change was triggered ultimately by the desire to make LuaError implement Clone, and this is currently impossible with error_chain. LuaError must implement Clone to be a proper lua citizen that can live as userdata within a lua runtime, because there is no way to limit what the lua runtime can do with a received error. Currently, this is solved by there being a rule that the error will "expire" if the error is passed back into rust, and this is very sub-optimal. In fact, one could easily imagine a scenario where lua is for example memoizing some function, and if the function has ever errored in the past the function should continue returning the same error, and this situation immediately fails with this restriciton in place. Additionally, there are other more minor problems with error_chain which make the API less good than it could be, or limit how we can use error_chain. This change has already solved a small bug in a Chucklefish project, where the conversion from an external error type (Borrow[Mut]Error) was allowed but not intended for user code, and was accidentally used. Additionally, pattern matching on error_chain errors, which should be common when dealing with Lua, is less convenient than a hand rolled error type. So, if we decide not to use error_chain, we now have a new set of problems if we decide interoperability with error_chain is important. The first problem we run into is that there are two natural bounds for wrapped errors that we would pick, (Error + Send + Sync), or just Error, and neither of them will interoperate well with error_chain. (Error + Send + Sync) means we can't wrap error chain errors into LuaError::ExternalError (they're missing the Sync bound), and having the bounds be just Error means the opposite, that we can't hold a LuaError inside an error_chain error. We could just decide that interoperability with error_chain is the most important qualification, and pick (Error + Send), but this causes a DIFFERENT set of problems. The rust ecosystem has the two primary error bounds as Error or (Error + Send + Sync), and there are Into impls from &str / String to Box<Error + Send + Sync> for example, but NOT (Error + Send). This means that we are forced to manually recreate the conversions from &str / String to LuaError rather than relying on a single Into<Box<Error + Send + Sync>> bound, but this means that string conversions have a different set of methods than other error types for external error conversion. I have not been able to figure out an API that I am happy with that uses the (Error + Send) bound. Box<Error> is obnoxious because not having errors implement Send causes needless problems in a multithreaded context, so that leaves (Error + Send + Sync). This is actually a completely reasonable bound for external errors, and has the nice String Into impls that we would want, the ONLY problem is that it is a pain to interoperate with the current version of error_chain. It would be nice to be able to specify the traits that an error generated by the error_chain macro would implement, and this is apparently in progress in the error_chain library. This would solve both the problem with not being able to implement Clone and the problems with (Error + Send) bounds. I am not convinced that this library should go back to using error_chain when that functionality is in stable error_chain though, because of the other minor usability problems with using error_chain. In that theoretical situation, the downside of NOT using error_chain is simply that there would not be automatic stacktraces of LuaError. This is not a huge problem, because stack traces of lua errors are not extremely useful, and for external errors it is not too hard to create a different version of the LuaExternalResult / LuaExternalError traits and do conversion from an error_chain type into a type that will print the stacktrace on display, or use downcasting in the error causes. So in summary, this library is no longer using error_chain, and probably will not use it again in the future. Currently this means that to interoperate with error_chain, you should use error_chain 0.8.1, which derives Sync on errors, or wait for a version that supports user defined trait derives. In the future when error_chain supports user defined trait derives, users may have to take an extra step to make wrapped external errors print the stacktrace that they capture. This change works, but is not entirely complete. There is no error documentation yet, and the change brought to a head an ugly module organization problem. There will be more commits for documentation and reorganization, then a new stable version of rlua.
2017-06-24 17:11:56 -05:00
"only through failure can we succeed".to_lua_err(),
))
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-08-01 13:09:47 -05:00
let ok = lua.create_function(|_, ()| Ok(Ok::<_, Error>("!".to_owned())));
globals.set("err", err).unwrap();
globals.set("ok", ok).unwrap();
lua.exec::<()>(
r#"
local r, e = err()
assert(r == nil)
assert(tostring(e) == "only through failure can we succeed")
local r, e = ok()
assert(r == "!")
assert(e == nil)
"#,
None,
).unwrap();
}
2017-06-21 16:53:52 -05:00
#[test]
fn test_num_conversion() {
let lua = Lua::new();
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
let globals = lua.globals();
2017-06-21 16:53:52 -05:00
globals.set("n", "1.0").unwrap();
assert_eq!(globals.get::<_, i64>("n").unwrap(), 1);
assert_eq!(globals.get::<_, f64>("n").unwrap(), 1.0);
assert_eq!(globals.get::<_, String>("n").unwrap(), "1.0");
globals.set("n", "1.5").unwrap();
assert!(globals.get::<_, i64>("n").is_err());
assert_eq!(globals.get::<_, f64>("n").unwrap(), 1.5);
assert_eq!(globals.get::<_, String>("n").unwrap(), "1.5");
globals.set("n", 1.5).unwrap();
assert!(globals.get::<_, i64>("n").is_err());
assert_eq!(globals.get::<_, f64>("n").unwrap(), 1.5);
assert_eq!(globals.get::<_, String>("n").unwrap(), "1.5");
lua.exec::<()>("a = math.huge", None).unwrap();
assert!(globals.get::<_, i64>("n").is_err());
2017-06-21 16:53:52 -05:00
}
#[test]
fn coroutine_from_closure() {
let lua = Lua::new();
2017-08-01 13:09:47 -05:00
let thrd_main = lua.create_function(|_, ()| Ok(()));
lua.globals().set("main", thrd_main).unwrap();
let thrd: Thread = lua.eval("coroutine.create(main)", None).unwrap();
thrd.resume::<_, ()>(()).unwrap();
}
#[test]
#[should_panic]
fn coroutine_panic() {
let lua = Lua::new();
2017-08-01 13:09:47 -05:00
let thrd_main = lua.create_function(|lua, ()| {
// whoops, 'main' has a wrong type
let _coro: u32 = lua.globals().get("main").unwrap();
Ok(())
});
lua.globals().set("main", thrd_main.clone()).unwrap();
let thrd: Thread = lua.create_thread(thrd_main);
thrd.resume::<_, ()>(()).unwrap();
}
#[test]
fn test_pcall_xpcall() {
let lua = Lua::new();
let globals = lua.globals();
// make sure that we handle not enough arguments
assert!(lua.exec::<()>("pcall()", None).is_err());
assert!(lua.exec::<()>("xpcall()", None).is_err());
assert!(lua.exec::<()>("xpcall(function() end)", None).is_err());
// Make sure that the return values from are correct on success
assert_eq!(lua.eval::<(bool, String)>("pcall(function(p) return p end, 'foo')", None).unwrap(), (true, "foo".to_owned()));
assert_eq!(lua.eval::<(bool, String)>("xpcall(function(p) return p end, print, 'foo')", None).unwrap(), (true, "foo".to_owned()));
// Make sure that the return values are correct on errors, and that error handling works
lua.exec::<()>(
r#"
pcall_error = nil
pcall_status, pcall_error = pcall(error, "testerror")
xpcall_error = nil
xpcall_status, _ = xpcall(error, function(err) xpcall_error = err end, "testerror")
"#,
None,
).unwrap();
assert_eq!(globals.get::<_, bool>("pcall_status").unwrap(), false);
assert_eq!(
2017-08-02 14:56:16 -05:00
globals.get::<_, String>("pcall_error").unwrap(),
"testerror"
);
assert_eq!(globals.get::<_, bool>("xpcall_statusr").unwrap(), false);
assert_eq!(
2017-08-02 14:56:16 -05:00
globals.get::<_, String>("xpcall_error").unwrap(),
"testerror"
);
2017-08-02 14:56:16 -05:00
// Make sure that weird xpcall error recursion at least doesn't cause unsafety or panics.
lua.exec::<()>(
r#"
function xpcall_recursion()
xpcall(error, function(err) error(err) end, "testerror")
end
"#,
None,
).unwrap();
2017-08-02 14:56:16 -05:00
let _ = globals
.get::<_, Function>("xpcall_recursion")
.unwrap()
.call::<_, ()>(());
}
#[test]
#[should_panic]
fn test_recursive_callback_panic() {
let lua = Lua::new();
let mut v = Some(Box::new(123));
let f = lua.create_function::<_, (), _>(move |lua, mutate: bool| {
if mutate {
v = None;
} else {
// Produce a mutable reference
let r = v.as_mut().unwrap();
// Whoops, this will recurse into the function and produce another mutable reference!
lua.globals()
.get::<_, Function>("f")
.unwrap()
.call::<_, ()>(true)
.unwrap();
println!("Should not get here, mutable aliasing has occurred!");
println!("value at {:p}", r as *mut _);
println!("value is {}", r);
}
Ok(())
});
lua.globals().set("f", f).unwrap();
lua.globals()
.get::<_, Function>("f")
.unwrap()
.call::<_, ()>(false)
.unwrap();
}
2017-09-30 00:13:58 -05:00
// TODO: Need to use compiletest-rs or similar to make sure these don't compile.
/*
#[test]
fn should_not_compile() {
let lua = Lua::new();
let globals = lua.globals();
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData;
impl UserData for MyUserData {};
let userdata_ref;
{
let touter = globals.get::<_, Table>("touter").unwrap();
touter.set("userdata", lua.create_userdata(MyUserData)).unwrap();
let userdata = touter.get::<_, AnyUserData>("userdata").unwrap();
userdata_ref = userdata.borrow::<MyUserData>();
}
// Should not allow self borrow of lua, it can change addresses
globals.set("boom", lua.create_function(|_, _| {
lua.eval::<i32>("1 + 1", None)
})).unwrap();
}
*/