Backport changes from rlua 0.16 (master branch)
This commit is contained in:
parent
53b352466e
commit
affa85feb0
11
Cargo.toml
11
Cargo.toml
|
@ -2,6 +2,7 @@
|
||||||
name = "rlua"
|
name = "rlua"
|
||||||
version = "0.15.3"
|
version = "0.15.3"
|
||||||
authors = ["kyren <catherine@chucklefish.org>"]
|
authors = ["kyren <catherine@chucklefish.org>"]
|
||||||
|
edition = "2018"
|
||||||
description = "High level bindings to Lua 5.3"
|
description = "High level bindings to Lua 5.3"
|
||||||
repository = "https://github.com/chucklefish/rlua"
|
repository = "https://github.com/chucklefish/rlua"
|
||||||
documentation = "https://docs.rs/rlua"
|
documentation = "https://docs.rs/rlua"
|
||||||
|
@ -14,17 +15,17 @@ license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = { version = "0.2" }
|
libc = { version = "0.2" }
|
||||||
failure = { version = "0.1.2" }
|
|
||||||
num-traits = { version = "0.2.6" }
|
num-traits = { version = "0.2.6" }
|
||||||
compiletest_rs = { version = "0.3", optional = true }
|
bstr = {version = "0.2", features = ["std"], default_features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = { version = "1.0" }
|
||||||
pkg-config = "0.3.11"
|
pkg-config = { version = "0.3.11" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustyline = "2.0.0"
|
rustyline = "5.0"
|
||||||
criterion = "0.2.0"
|
criterion = "0.2.0"
|
||||||
|
compiletest_rs = { version = "0.3", features = ["stable"] }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmark"
|
name = "benchmark"
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::string::String as StdString;
|
use std::string::String as StdString;
|
||||||
|
|
||||||
|
use bstr::{BStr, BString};
|
||||||
use num_traits::cast;
|
use num_traits::cast;
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use function::Function;
|
use crate::function::Function;
|
||||||
use lua::Lua;
|
use crate::lua::Lua;
|
||||||
use string::String;
|
use crate::string::String;
|
||||||
use table::Table;
|
use crate::table::Table;
|
||||||
use thread::Thread;
|
use crate::thread::Thread;
|
||||||
use types::{LightUserData, Number};
|
use crate::types::{LightUserData, Number};
|
||||||
use userdata::{AnyUserData, UserData};
|
use crate::userdata::{AnyUserData, UserData};
|
||||||
use value::{FromLua, Nil, ToLua, Value};
|
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||||
|
|
||||||
impl<'lua> ToLua<'lua> for Value<'lua> {
|
impl<'lua> ToLua<'lua> for Value<'lua> {
|
||||||
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
@ -35,7 +37,7 @@ impl<'lua> ToLua<'lua> for String<'lua> {
|
||||||
impl<'lua> FromLua<'lua> for String<'lua> {
|
impl<'lua> FromLua<'lua> for String<'lua> {
|
||||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
lua.coerce_string(value)
|
lua.coerce_string(value)?
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
to: "String",
|
to: "String",
|
||||||
|
@ -150,7 +152,7 @@ impl<'lua> FromLua<'lua> for Error {
|
||||||
match value {
|
match value {
|
||||||
Value::Error(err) => Ok(err),
|
Value::Error(err) => Ok(err),
|
||||||
val => Ok(Error::RuntimeError(
|
val => Ok(Error::RuntimeError(
|
||||||
lua.coerce_string(val)
|
lua.coerce_string(val)?
|
||||||
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
|
.and_then(|s| Some(s.to_str().ok()?.to_owned()))
|
||||||
.unwrap_or_else(|| "<unprintable error>".to_owned()),
|
.unwrap_or_else(|| "<unprintable error>".to_owned()),
|
||||||
)),
|
)),
|
||||||
|
@ -203,12 +205,13 @@ impl<'lua> FromLua<'lua> for StdString {
|
||||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
Ok(lua
|
Ok(lua
|
||||||
.coerce_string(value)
|
.coerce_string(value)?
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
to: "String",
|
to: "String",
|
||||||
message: Some("expected string or number".to_string()),
|
message: Some("expected string or number".to_string()),
|
||||||
})?.to_str()?
|
})?
|
||||||
|
.to_str()?
|
||||||
.to_owned())
|
.to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,6 +222,68 @@ impl<'lua, 'a> ToLua<'lua> for &'a str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> ToLua<'lua> for CString {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::String(lua.create_string(self.as_bytes())?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for CString {
|
||||||
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
|
let ty = value.type_name();
|
||||||
|
let string = lua
|
||||||
|
.coerce_string(value)?
|
||||||
|
.ok_or_else(|| Error::FromLuaConversionError {
|
||||||
|
from: ty,
|
||||||
|
to: "CString",
|
||||||
|
message: Some("expected string or number".to_string()),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
match CStr::from_bytes_with_nul(string.as_bytes_with_nul()) {
|
||||||
|
Ok(s) => Ok(s.into()),
|
||||||
|
Err(_) => Err(Error::FromLuaConversionError {
|
||||||
|
from: ty,
|
||||||
|
to: "CString",
|
||||||
|
message: Some("invalid C-style string".to_string()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, 'a> ToLua<'lua> for &'a CStr {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::String(lua.create_string(self.to_bytes())?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, 'a> ToLua<'lua> for BString {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::String(lua.create_string(&self)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for BString {
|
||||||
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
|
let ty = value.type_name();
|
||||||
|
Ok(BString::from(
|
||||||
|
lua.coerce_string(value)?
|
||||||
|
.ok_or_else(|| Error::FromLuaConversionError {
|
||||||
|
from: ty,
|
||||||
|
to: "String",
|
||||||
|
message: Some("expected string or number".to_string()),
|
||||||
|
})?
|
||||||
|
.as_bytes()
|
||||||
|
.to_vec(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, 'a> ToLua<'lua> for &BStr {
|
||||||
|
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||||
|
Ok(Value::String(lua.create_string(&self)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! lua_convert_int {
|
macro_rules! lua_convert_int {
|
||||||
($x:ty) => {
|
($x:ty) => {
|
||||||
impl<'lua> ToLua<'lua> for $x {
|
impl<'lua> ToLua<'lua> for $x {
|
||||||
|
@ -231,7 +296,8 @@ macro_rules! lua_convert_int {
|
||||||
from: stringify!($x),
|
from: stringify!($x),
|
||||||
to: "number",
|
to: "number",
|
||||||
message: Some("out of range".to_owned()),
|
message: Some("out of range".to_owned()),
|
||||||
}).map(Value::Number)
|
})
|
||||||
|
.map(Value::Number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,20 +305,20 @@ macro_rules! lua_convert_int {
|
||||||
impl<'lua> FromLua<'lua> for $x {
|
impl<'lua> FromLua<'lua> for $x {
|
||||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
(if let Some(i) = lua.coerce_integer(value.clone()) {
|
(if let Some(i) = lua.coerce_integer(value.clone())? {
|
||||||
cast(i)
|
cast(i)
|
||||||
} else {
|
} else {
|
||||||
cast(
|
cast(lua.coerce_number(value)?.ok_or_else(|| {
|
||||||
lua.coerce_number(value)
|
Error::FromLuaConversionError {
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
from: ty,
|
||||||
from: ty,
|
to: stringify!($x),
|
||||||
to: stringify!($x),
|
message: Some(
|
||||||
message: Some(
|
"expected number or string coercible to number".to_string(),
|
||||||
"expected number or string coercible to number".to_string(),
|
),
|
||||||
),
|
}
|
||||||
})?,
|
})?)
|
||||||
)
|
})
|
||||||
}).ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
to: stringify!($x),
|
to: stringify!($x),
|
||||||
message: Some("out of range".to_owned()),
|
message: Some("out of range".to_owned()),
|
||||||
|
@ -286,12 +352,13 @@ macro_rules! lua_convert_float {
|
||||||
impl<'lua> FromLua<'lua> for $x {
|
impl<'lua> FromLua<'lua> for $x {
|
||||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||||
let ty = value.type_name();
|
let ty = value.type_name();
|
||||||
lua.coerce_number(value)
|
lua.coerce_number(value)?
|
||||||
.ok_or_else(|| Error::FromLuaConversionError {
|
.ok_or_else(|| Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
to: stringify!($x),
|
to: stringify!($x),
|
||||||
message: Some("expected number or string coercible to number".to_string()),
|
message: Some("expected number or string coercible to number".to_string()),
|
||||||
}).and_then(|n| {
|
})
|
||||||
|
.and_then(|n| {
|
||||||
cast(n).ok_or_else(|| Error::FromLuaConversionError {
|
cast(n).ok_or_else(|| Error::FromLuaConversionError {
|
||||||
from: ty,
|
from: ty,
|
||||||
to: stringify!($x),
|
to: stringify!($x),
|
||||||
|
|
51
src/error.rs
51
src/error.rs
|
@ -1,16 +1,16 @@
|
||||||
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
|
use std::string::String as StdString;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use failure;
|
|
||||||
|
|
||||||
/// Error type returned by `rlua` methods.
|
/// Error type returned by `rlua` methods.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
/// Syntax error while parsing Lua source code.
|
/// Syntax error while parsing Lua source code.
|
||||||
SyntaxError {
|
SyntaxError {
|
||||||
/// The error message as returned by Lua.
|
/// The error message as returned by Lua.
|
||||||
message: String,
|
message: StdString,
|
||||||
/// `true` if the error can likely be fixed by appending more input to the source code.
|
/// `true` if the error can likely be fixed by appending more input to the source code.
|
||||||
///
|
///
|
||||||
/// This is useful for implementing REPLs as they can query the user for more input if this
|
/// This is useful for implementing REPLs as they can query the user for more input if this
|
||||||
|
@ -22,11 +22,16 @@ pub enum Error {
|
||||||
/// The Lua VM returns this error when a builtin operation is performed on incompatible types.
|
/// The Lua VM returns this error when a builtin operation is performed on incompatible types.
|
||||||
/// Among other things, this includes invoking operators on wrong types (such as calling or
|
/// Among other things, this includes invoking operators on wrong types (such as calling or
|
||||||
/// indexing a `nil` value).
|
/// indexing a `nil` value).
|
||||||
RuntimeError(String),
|
RuntimeError(StdString),
|
||||||
|
/// Lua memory error, aka `LUA_ERRMEM`
|
||||||
|
///
|
||||||
|
/// The Lua VM returns this error when the allocator does not return the requested memory, aka
|
||||||
|
/// it is an out-of-memory error.
|
||||||
|
MemoryError(StdString),
|
||||||
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
||||||
///
|
///
|
||||||
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||||
GarbageCollectorError(String),
|
GarbageCollectorError(StdString),
|
||||||
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
|
/// A mutable callback has triggered Lua code that has called the same mutable callback again.
|
||||||
///
|
///
|
||||||
/// This is an error because a mutable callback can only be borrowed mutably once.
|
/// This is an error because a mutable callback can only be borrowed mutably once.
|
||||||
|
@ -53,7 +58,7 @@ pub enum Error {
|
||||||
/// Name of the Lua type that could not be created.
|
/// Name of the Lua type that could not be created.
|
||||||
to: &'static str,
|
to: &'static str,
|
||||||
/// A message indicating why the conversion failed in more detail.
|
/// A message indicating why the conversion failed in more detail.
|
||||||
message: Option<String>,
|
message: Option<StdString>,
|
||||||
},
|
},
|
||||||
/// A Lua value could not be converted to the expected Rust type.
|
/// A Lua value could not be converted to the expected Rust type.
|
||||||
FromLuaConversionError {
|
FromLuaConversionError {
|
||||||
|
@ -62,7 +67,7 @@ pub enum Error {
|
||||||
/// Name of the Rust type that could not be created.
|
/// Name of the Rust type that could not be created.
|
||||||
to: &'static str,
|
to: &'static str,
|
||||||
/// A string containing more detailed error information.
|
/// A string containing more detailed error information.
|
||||||
message: Option<String>,
|
message: Option<StdString>,
|
||||||
},
|
},
|
||||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||||
///
|
///
|
||||||
|
@ -107,7 +112,7 @@ pub enum Error {
|
||||||
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
/// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
|
||||||
CallbackError {
|
CallbackError {
|
||||||
/// Lua call stack backtrace.
|
/// Lua call stack backtrace.
|
||||||
traceback: String,
|
traceback: StdString,
|
||||||
/// Original error returned by the Rust code.
|
/// Original error returned by the Rust code.
|
||||||
cause: Arc<Error>,
|
cause: Arc<Error>,
|
||||||
},
|
},
|
||||||
|
@ -118,7 +123,7 @@ pub enum Error {
|
||||||
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
/// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
|
||||||
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
/// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
|
||||||
/// from which the original error (and a stack traceback) can be recovered.
|
/// from which the original error (and a stack traceback) can be recovered.
|
||||||
ExternalError(Arc<failure::Error>),
|
ExternalError(Arc<dyn StdError + Send + Sync>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A specialized `Result` type used by `rlua`'s API.
|
/// A specialized `Result` type used by `rlua`'s API.
|
||||||
|
@ -129,6 +134,9 @@ impl fmt::Display for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
|
Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
|
||||||
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
|
Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
|
||||||
|
Error::MemoryError(ref msg) => {
|
||||||
|
write!(fmt, "memory error: {}", msg)
|
||||||
|
}
|
||||||
Error::GarbageCollectorError(ref msg) => {
|
Error::GarbageCollectorError(ref msg) => {
|
||||||
write!(fmt, "garbage collector error: {}", msg)
|
write!(fmt, "garbage collector error: {}", msg)
|
||||||
}
|
}
|
||||||
|
@ -174,34 +182,27 @@ impl fmt::Display for Error {
|
||||||
Error::MismatchedRegistryKey => {
|
Error::MismatchedRegistryKey => {
|
||||||
write!(fmt, "RegistryKey used from different Lua state")
|
write!(fmt, "RegistryKey used from different Lua state")
|
||||||
}
|
}
|
||||||
Error::CallbackError { ref traceback, .. } => {
|
Error::CallbackError { ref traceback, ref cause } => {
|
||||||
write!(fmt, "callback error: {}", traceback)
|
write!(fmt, "callback error: {}: {}", cause, traceback)
|
||||||
}
|
}
|
||||||
Error::ExternalError(ref err) => err.fmt(fmt),
|
Error::ExternalError(ref err) => write!(fmt, "external error: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl failure::Fail for Error {
|
impl StdError for Error {
|
||||||
fn cause(&self) -> Option<&dyn failure::Fail> {
|
fn source(&self) -> Option<&(dyn StdError + 'static)> {
|
||||||
match *self {
|
match *self {
|
||||||
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
|
Error::CallbackError { ref cause, .. } => Some(cause.as_ref()),
|
||||||
Error::ExternalError(ref err) => err.as_fail().cause(),
|
Error::ExternalError(ref err) => Some(err.as_ref()),
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn backtrace(&self) -> Option<&failure::Backtrace> {
|
|
||||||
match *self {
|
|
||||||
Error::ExternalError(ref err) => Some(err.backtrace()),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn external<T: Into<failure::Error>>(err: T) -> Error {
|
pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
|
||||||
Error::ExternalError(Arc::new(err.into()))
|
Error::ExternalError(err.into().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,7 +212,7 @@ pub trait ExternalError {
|
||||||
|
|
||||||
impl<E> ExternalError for E
|
impl<E> ExternalError for E
|
||||||
where
|
where
|
||||||
E: Into<failure::Error>,
|
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||||
{
|
{
|
||||||
fn to_lua_err(self) -> Error {
|
fn to_lua_err(self) -> Error {
|
||||||
Error::external(self)
|
Error::external(self)
|
||||||
|
|
|
@ -23,11 +23,11 @@
|
||||||
//! Contains definitions from `lauxlib.h`.
|
//! Contains definitions from `lauxlib.h`.
|
||||||
|
|
||||||
use libc::{c_int, c_long, c_char, c_void, size_t};
|
use libc::{c_int, c_long, c_char, c_void, size_t};
|
||||||
use ffi::lua;
|
|
||||||
use ffi::lua::{lua_State, lua_CFunction, lua_Integer, lua_Number};
|
|
||||||
use ffi::luaconf::LUAL_BUFFERSIZE;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
use super::lua::{self, lua_State, lua_CFunction, lua_Integer, lua_Number};
|
||||||
|
use super::luaconf::LUAL_BUFFERSIZE;
|
||||||
|
|
||||||
pub use super::glue::LUAL_NUMSIZES;
|
pub use super::glue::LUAL_NUMSIZES;
|
||||||
pub use super::glue::LUA_FILEHANDLE;
|
pub use super::glue::LUA_FILEHANDLE;
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,10 @@
|
||||||
//! Contains definitions from `lua.h`.
|
//! Contains definitions from `lua.h`.
|
||||||
|
|
||||||
use libc::{c_void, c_int, c_char, c_uchar, size_t};
|
use libc::{c_void, c_int, c_char, c_uchar, size_t};
|
||||||
use ffi::luaconf;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
use super::luaconf;
|
||||||
|
|
||||||
pub use super::glue::{LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_NUM, LUA_VERSION_RELEASE};
|
pub use super::glue::{LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_NUM, LUA_VERSION_RELEASE};
|
||||||
pub use super::glue::{LUA_VERSION, LUA_RELEASE, LUA_COPYRIGHT, LUA_AUTHORS};
|
pub use super::glue::{LUA_VERSION, LUA_RELEASE, LUA_COPYRIGHT, LUA_AUTHORS};
|
||||||
pub use super::glue::{LUA_REGISTRYINDEX};
|
pub use super::glue::{LUA_REGISTRYINDEX};
|
||||||
|
|
|
@ -22,9 +22,10 @@
|
||||||
|
|
||||||
//! Contains definitions from `lualib.h`.
|
//! Contains definitions from `lualib.h`.
|
||||||
|
|
||||||
use ffi::lua::lua_State;
|
|
||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
|
|
||||||
|
use super::lua::lua_State;
|
||||||
|
|
||||||
pub use super::glue::{
|
pub use super::glue::{
|
||||||
LUA_COLIBNAME, LUA_TABLIBNAME, LUA_IOLIBNAME, LUA_OSLIBNAME, LUA_STRLIBNAME,
|
LUA_COLIBNAME, LUA_TABLIBNAME, LUA_IOLIBNAME, LUA_OSLIBNAME, LUA_STRLIBNAME,
|
||||||
LUA_UTF8LIBNAME, LUA_BITLIBNAME, LUA_MATHLIBNAME, LUA_DBLIBNAME, LUA_LOADLIBNAME
|
LUA_UTF8LIBNAME, LUA_BITLIBNAME, LUA_MATHLIBNAME, LUA_DBLIBNAME, LUA_LOADLIBNAME
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use ffi;
|
use crate::ffi;
|
||||||
use types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use util::{
|
use crate::util::{
|
||||||
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
||||||
};
|
};
|
||||||
use value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||||
|
|
||||||
/// Handle to an internal Lua function.
|
/// Handle to an internal Lua function.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -23,9 +23,8 @@ impl<'lua> Function<'lua> {
|
||||||
/// Call Lua's built-in `tostring` function:
|
/// Call Lua's built-in `tostring` function:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Function, Result};
|
/// # use rlua::{Lua, Function, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// let globals = lua.globals();
|
/// let globals = lua.globals();
|
||||||
///
|
///
|
||||||
|
@ -35,32 +34,26 @@ impl<'lua> Function<'lua> {
|
||||||
///
|
///
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Call a function with multiple arguments:
|
/// Call a function with multiple arguments:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Function, Result};
|
/// # use rlua::{Lua, Function, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
///
|
///
|
||||||
/// let sum: Function = lua.eval(r#"
|
/// let sum: Function = lua.load(
|
||||||
/// function(a, b)
|
/// r#"
|
||||||
/// return a + b
|
/// function(a, b)
|
||||||
/// end
|
/// return a + b
|
||||||
/// "#, None)?;
|
/// end
|
||||||
|
/// "#).eval()?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
|
/// assert_eq!(sum.call::<_, u32>((3, 4))?, 3 + 4);
|
||||||
///
|
///
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
|
@ -76,7 +69,7 @@ impl<'lua> Function<'lua> {
|
||||||
let stack_start = ffi::lua_gettop(lua.state);
|
let stack_start = ffi::lua_gettop(lua.state);
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
for arg in args {
|
for arg in args {
|
||||||
lua.push_value(arg);
|
lua.push_value(arg)?;
|
||||||
}
|
}
|
||||||
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
|
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
|
||||||
if ret != ffi::LUA_OK {
|
if ret != ffi::LUA_OK {
|
||||||
|
@ -102,16 +95,15 @@ impl<'lua> Function<'lua> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Function, Result};
|
/// # use rlua::{Lua, Function, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
///
|
///
|
||||||
/// let sum: Function = lua.eval(r#"
|
/// let sum: Function = lua_context.load(r#"
|
||||||
/// function(a, b)
|
/// function(a, b)
|
||||||
/// return a + b
|
/// return a + b
|
||||||
/// end
|
/// end
|
||||||
/// "#, None)?;
|
/// "#).eval()?;
|
||||||
///
|
///
|
||||||
/// let bound_a = sum.bind(1)?;
|
/// let bound_a = sum.bind(1)?;
|
||||||
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
|
/// assert_eq!(bound_a.call::<_, u32>(2)?, 1 + 2);
|
||||||
|
@ -121,9 +113,6 @@ impl<'lua> Function<'lua> {
|
||||||
///
|
///
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
|
||||||
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
@ -161,7 +150,7 @@ impl<'lua> Function<'lua> {
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
|
||||||
for arg in args {
|
for arg in args {
|
||||||
lua.push_value(arg);
|
lua.push_value(arg)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
protect_lua_closure(lua.state, nargs + 2, 1, |state| {
|
protect_lua_closure(lua.state, nargs + 2, 1, |state| {
|
||||||
|
|
33
src/lib.rs
33
src/lib.rs
|
@ -40,15 +40,12 @@
|
||||||
// warnings at all.
|
// warnings at all.
|
||||||
#![doc(test(attr(deny(warnings))))]
|
#![doc(test(attr(deny(warnings))))]
|
||||||
|
|
||||||
extern crate failure;
|
|
||||||
extern crate libc;
|
|
||||||
extern crate num_traits;
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
mod ffi;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
mod conversion;
|
mod conversion;
|
||||||
|
mod error;
|
||||||
|
mod ffi;
|
||||||
mod function;
|
mod function;
|
||||||
mod lua;
|
mod lua;
|
||||||
mod multi;
|
mod multi;
|
||||||
|
@ -61,18 +58,18 @@ mod userdata;
|
||||||
mod util;
|
mod util;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
pub use error::{Error, ExternalError, ExternalResult, Result};
|
pub use crate::ffi::lua_State;
|
||||||
pub use function::Function;
|
|
||||||
pub use lua::Lua;
|
|
||||||
pub use multi::Variadic;
|
|
||||||
pub use scope::Scope;
|
|
||||||
pub use string::String;
|
|
||||||
pub use table::{Table, TablePairs, TableSequence};
|
|
||||||
pub use thread::{Thread, ThreadStatus};
|
|
||||||
pub use types::{Integer, LightUserData, Number, RegistryKey};
|
|
||||||
pub use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
|
||||||
pub use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
|
||||||
|
|
||||||
pub use ffi::lua_State;
|
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||||
|
pub use crate::function::Function;
|
||||||
|
pub use crate::lua::{Lua, Chunk};
|
||||||
|
pub use crate::multi::Variadic;
|
||||||
|
pub use crate::scope::Scope;
|
||||||
|
pub use crate::string::String;
|
||||||
|
pub use crate::table::{Table, TablePairs, TableSequence};
|
||||||
|
pub use crate::thread::{Thread, ThreadStatus};
|
||||||
|
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||||
|
pub use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
|
pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
|
|
559
src/lua.rs
559
src/lua.rs
|
@ -4,35 +4,33 @@ use std::collections::HashMap;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::string::String as StdString;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{mem, ptr, str};
|
use std::{mem, ptr, str};
|
||||||
|
|
||||||
use libc;
|
use crate::error::{Error, Result};
|
||||||
|
use crate::ffi;
|
||||||
use error::{Error, Result};
|
use crate::function::Function;
|
||||||
use ffi;
|
use crate::scope::Scope;
|
||||||
use function::Function;
|
use crate::string::String;
|
||||||
use scope::Scope;
|
use crate::table::Table;
|
||||||
use string::String;
|
use crate::thread::Thread;
|
||||||
use table::Table;
|
use crate::types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey};
|
||||||
use thread::Thread;
|
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
use types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey};
|
use crate::util::{
|
||||||
use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
assert_stack, callback_error, check_stack, get_userdata, get_wrapped_error,
|
||||||
use util::{
|
init_error_registry, init_userdata_metatable, main_state, pop_error,
|
||||||
assert_stack, callback_error, check_stack, gc_guard, get_userdata, get_wrapped_error,
|
protect_lua, protect_lua_closure, safe_pcall, safe_xpcall,
|
||||||
init_error_metatables, init_userdata_metatable, main_state, pop_error, protect_lua,
|
push_string, push_userdata, push_wrapped_error, StackGuard,
|
||||||
protect_lua_closure, push_string, push_userdata, push_wrapped_error, safe_pcall, safe_xpcall,
|
userdata_destructor,
|
||||||
userdata_destructor, StackGuard,
|
|
||||||
};
|
};
|
||||||
use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
/// Top level Lua struct which holds the Lua state itself.
|
/// Top level Lua struct which holds the Lua state itself.
|
||||||
pub struct Lua {
|
pub struct Lua {
|
||||||
pub(crate) state: *mut ffi::lua_State,
|
pub(crate) state: *mut ffi::lua_State,
|
||||||
main_state: *mut ffi::lua_State,
|
main_state: *mut ffi::lua_State,
|
||||||
// Lua has lots of interior mutability, should not be RefUnwindSafe
|
// Lua has lots of interior mutability, should not be RefUnwindSafe
|
||||||
_phantom: PhantomData<UnsafeCell<()>>,
|
_no_ref_unwind_safe: PhantomData<UnsafeCell<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for Lua {}
|
unsafe impl Send for Lua {}
|
||||||
|
@ -42,33 +40,55 @@ impl Lua {
|
||||||
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
|
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
|
||||||
let state_top = ffi::lua_gettop(state);
|
let state_top = ffi::lua_gettop(state);
|
||||||
|
|
||||||
init_error_metatables(state);
|
let ref_thread = rlua_expect!(
|
||||||
|
protect_lua_closure(state, 0, 0, |state| {
|
||||||
|
init_error_registry(state);
|
||||||
|
|
||||||
// Create the function metatable
|
// Create the function metatable
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||||
|
);
|
||||||
|
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||||
|
ffi::lua_pushcfunction(state, userdata_destructor::<Callback>);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||||
|
ffi::lua_pushboolean(state, 0);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
// Override pcall and xpcall with versions that cannot be used to catch rust panics.
|
||||||
|
|
||||||
|
/*
|
||||||
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("pcall"));
|
||||||
|
ffi::lua_pushcfunction(state, safe_pcall);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("xpcall"));
|
||||||
|
ffi::lua_pushcfunction(state, safe_xpcall);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
||||||
|
// collected.
|
||||||
|
|
||||||
|
let _ref_thread = ffi::lua_newthread(state);
|
||||||
|
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
_ref_thread
|
||||||
|
}),
|
||||||
|
"Error during Lua construction",
|
||||||
);
|
);
|
||||||
|
|
||||||
ffi::lua_newtable(state);
|
|
||||||
|
|
||||||
push_string(state, "__gc").unwrap();
|
|
||||||
ffi::lua_pushcfunction(state, userdata_destructor::<Callback>);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
push_string(state, "__metatable").unwrap();
|
|
||||||
ffi::lua_pushboolean(state, 0);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
|
||||||
// collected.
|
|
||||||
|
|
||||||
let ref_thread = ffi::lua_newthread(state);
|
|
||||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
// Create ExtraData, and place it in the lua_State "extra space"
|
// Create ExtraData, and place it in the lua_State "extra space"
|
||||||
|
|
||||||
let extra = Box::into_raw(Box::new(ExtraData {
|
let extra = Box::into_raw(Box::new(ExtraData {
|
||||||
|
@ -80,97 +100,148 @@ impl Lua {
|
||||||
ref_stack_max: 0,
|
ref_stack_max: 0,
|
||||||
ref_free: Vec::new(),
|
ref_free: Vec::new(),
|
||||||
}));
|
}));
|
||||||
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData) = extra;
|
|
||||||
|
|
||||||
rlua_debug_assert!(ffi::lua_gettop(state) == state_top, "stack leak during creation");
|
rlua_debug_assert!(ffi::lua_gettop(state) == state_top, "stack leak during creation");
|
||||||
assert_stack(state, ffi::LUA_MINSTACK);
|
assert_stack(state, ffi::LUA_MINSTACK);
|
||||||
|
|
||||||
|
// Place pointer to ExtraData in the lua_State "extra space"
|
||||||
|
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData) = extra;
|
||||||
|
|
||||||
Lua {
|
Lua {
|
||||||
state,
|
state,
|
||||||
main_state: main_state(state),
|
main_state: main_state(state),
|
||||||
_phantom: PhantomData,
|
_no_ref_unwind_safe: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a chunk of Lua code and returns it as a function.
|
/// Returns true if the garbage collector is currently running automatically.
|
||||||
|
pub fn gc_is_running(&self) -> bool {
|
||||||
|
unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop the Lua GC from running
|
||||||
|
pub fn gc_stop(&self) {
|
||||||
|
unsafe {
|
||||||
|
ffi::lua_gc(self.main_state, ffi::LUA_GCSTOP, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restarts the Lua GC if it is not running
|
||||||
|
pub fn gc_restart(&self) {
|
||||||
|
unsafe {
|
||||||
|
ffi::lua_gc(self.main_state, ffi::LUA_GCRESTART, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a full garbage-collection cycle.
|
||||||
///
|
///
|
||||||
/// The source can be named by setting the `name` parameter. This is generally recommended as it
|
/// It may be necessary to call this function twice to collect all currently unreachable
|
||||||
/// results in better error traces.
|
/// objects. Once to finish the current gc cycle, and once to start and finish the next cycle.
|
||||||
|
pub fn gc_collect(&self) -> Result<()> {
|
||||||
|
unsafe {
|
||||||
|
protect_lua_closure(self.main_state, 0, 0, |state| {
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steps the garbage collector one indivisible step.
|
||||||
///
|
///
|
||||||
/// Equivalent to Lua's `load` function.
|
/// Returns true if this has finished a collection cycle.
|
||||||
pub fn load<S>(&self, source: &S, name: Option<&str>) -> Result<Function>
|
pub fn gc_step(&self) -> Result<bool> {
|
||||||
|
self.gc_step_kbytes(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steps the garbage collector as though memory had been allocated.
|
||||||
|
///
|
||||||
|
/// if `kbytes` is 0, then this is the same as calling `gc_step`. Returns true if this step has
|
||||||
|
/// finished a collection cycle.
|
||||||
|
pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> {
|
||||||
|
unsafe {
|
||||||
|
protect_lua_closure(self.main_state, 0, 0, |state| {
|
||||||
|
ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the 'pause' value of the collector.
|
||||||
|
///
|
||||||
|
/// Returns the previous value of 'pause'. More information can be found in the [Lua 5.3
|
||||||
|
/// documentation][lua_doc].
|
||||||
|
///
|
||||||
|
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5
|
||||||
|
pub fn gc_set_pause(&self, pause: c_int) -> c_int {
|
||||||
|
unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCSETPAUSE, pause) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the 'step multiplier' value of the collector.
|
||||||
|
///
|
||||||
|
/// Returns the previous value of the 'step multiplier'. More information can be found in the
|
||||||
|
/// [Lua 5.3 documentation][lua_doc].
|
||||||
|
///
|
||||||
|
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5
|
||||||
|
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
|
||||||
|
unsafe { ffi::lua_gc(self.main_state, ffi::LUA_GCSETSTEPMUL, step_multiplier) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns Lua source code as a `Chunk` builder type.
|
||||||
|
///
|
||||||
|
/// In order to actually compile or run the resulting code, you must call [`Chunk::exec`] or
|
||||||
|
/// similar on the returned builder. Code is not even parsed until one of these methods is
|
||||||
|
/// called.
|
||||||
|
///
|
||||||
|
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
|
||||||
|
pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
|
||||||
where
|
where
|
||||||
S: ?Sized + AsRef<[u8]>,
|
S: ?Sized + AsRef<[u8]>,
|
||||||
{
|
{
|
||||||
|
Chunk {
|
||||||
|
lua: self,
|
||||||
|
source: source.as_ref(),
|
||||||
|
name: None,
|
||||||
|
env: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_chunk<'lua>(
|
||||||
|
&'lua self,
|
||||||
|
source: &[u8],
|
||||||
|
name: Option<&CString>,
|
||||||
|
env: Option<Value<'lua>>
|
||||||
|
) -> Result<Function<'lua>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 1);
|
assert_stack(self.state, 1);
|
||||||
let source = source.as_ref();
|
|
||||||
|
|
||||||
match if let Some(name) = name {
|
match if let Some(name) = name {
|
||||||
let name =
|
ffi::luaL_loadbufferx(
|
||||||
CString::new(name.to_owned()).map_err(|e| Error::ToLuaConversionError {
|
|
||||||
from: "&str",
|
|
||||||
to: "string",
|
|
||||||
message: Some(e.to_string()),
|
|
||||||
})?;
|
|
||||||
ffi::luaL_loadbuffer(
|
|
||||||
self.state,
|
self.state,
|
||||||
source.as_ptr() as *const c_char,
|
source.as_ptr() as *const c_char,
|
||||||
source.len(),
|
source.len(),
|
||||||
name.as_ptr(),
|
name.as_ptr() as *const c_char,
|
||||||
|
cstr!("t"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ffi::luaL_loadbuffer(
|
ffi::luaL_loadbufferx(
|
||||||
self.state,
|
self.state,
|
||||||
source.as_ptr() as *const c_char,
|
source.as_ptr() as *const c_char,
|
||||||
source.len(),
|
source.len(),
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
|
cstr!("t"),
|
||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
ffi::LUA_OK => Ok(Function(self.pop_ref())),
|
ffi::LUA_OK => {
|
||||||
|
if let Some(env) = env {
|
||||||
|
self.push_value(env)?;
|
||||||
|
ffi::lua_setupvalue(self.state, -2, 1);
|
||||||
|
}
|
||||||
|
Ok(Function(self.pop_ref()))
|
||||||
|
}
|
||||||
err => Err(pop_error(self.state, err)),
|
err => Err(pop_error(self.state, err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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, S, R: FromLuaMulti<'lua>>(
|
|
||||||
&'lua self,
|
|
||||||
source: &S,
|
|
||||||
name: Option<&str>,
|
|
||||||
) -> Result<R>
|
|
||||||
where
|
|
||||||
S: ?Sized + AsRef<[u8]>,
|
|
||||||
R: FromLuaMulti<'lua>,
|
|
||||||
{
|
|
||||||
self.load(source, name)?.call(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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, S, R>(&'lua self, source: &S, name: Option<&str>) -> Result<R>
|
|
||||||
where
|
|
||||||
S: ?Sized + AsRef<[u8]>,
|
|
||||||
R: FromLuaMulti<'lua>,
|
|
||||||
{
|
|
||||||
// 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.
|
|
||||||
let mut return_source = "return ".as_bytes().to_vec();
|
|
||||||
return_source.extend(source.as_ref());
|
|
||||||
self.load(&return_source, name)
|
|
||||||
.or_else(|_| self.load(source, name))?
|
|
||||||
.call(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create and return an interned Lua string. Lua strings can be arbitrary [u8] data including
|
/// Create and return an interned Lua string. Lua strings can be arbitrary [u8] data including
|
||||||
/// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]`
|
/// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]`
|
||||||
/// here.
|
/// here.
|
||||||
|
@ -220,8 +291,8 @@ impl Lua {
|
||||||
protect_lua(self.state, 0, new_table)?;
|
protect_lua(self.state, 0, new_table)?;
|
||||||
|
|
||||||
for (k, v) in cont {
|
for (k, v) in cont {
|
||||||
self.push_value(k.to_lua(self)?);
|
self.push_value(k.to_lua(self)?)?;
|
||||||
self.push_value(v.to_lua(self)?);
|
self.push_value(v.to_lua(self)?)?;
|
||||||
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
1
|
1
|
||||||
|
@ -257,9 +328,8 @@ impl Lua {
|
||||||
/// Create a function which prints its argument:
|
/// Create a function which prints its argument:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Result};
|
/// # use rlua::{Lua, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
///
|
///
|
||||||
/// let greet = lua.create_function(|_, name: String| {
|
/// let greet = lua.create_function(|_, name: String| {
|
||||||
|
@ -269,17 +339,13 @@ impl Lua {
|
||||||
/// # let _ = greet; // used
|
/// # let _ = greet; // used
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Use tuples to accept multiple arguments:
|
/// Use tuples to accept multiple arguments:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Result};
|
/// # use rlua::{Lua, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
///
|
///
|
||||||
/// let print_person = lua.create_function(|_, (name, age): (String, u8)| {
|
/// let print_person = lua.create_function(|_, (name, age): (String, u8)| {
|
||||||
|
@ -289,9 +355,6 @@ impl Lua {
|
||||||
/// # let _ = print_person; // used
|
/// # let _ = print_person; // used
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`ToLua`]: trait.ToLua.html
|
/// [`ToLua`]: trait.ToLua.html
|
||||||
|
@ -365,6 +428,15 @@ impl Lua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a handle to the active `Thread`. For calls to `Lua` this will be the main Lua thread,
|
||||||
|
/// for parameters given to a callback, this will be whatever Lua thread called the callback.
|
||||||
|
pub fn current_thread<'lua>(&'lua self) -> Thread<'lua> {
|
||||||
|
unsafe {
|
||||||
|
ffi::lua_pushthread(self.state);
|
||||||
|
Thread(self.pop_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Calls the given function with a `Scope` parameter, giving the function the ability to create
|
/// Calls the given function with a `Scope` parameter, giving the function the ability to create
|
||||||
/// userdata and callbacks from rust types that are !Send or non-'static.
|
/// userdata and callbacks from rust types that are !Send or non-'static.
|
||||||
///
|
///
|
||||||
|
@ -376,20 +448,20 @@ impl Lua {
|
||||||
/// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only
|
/// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only
|
||||||
/// outlive the scope lifetime.
|
/// outlive the scope lifetime.
|
||||||
///
|
///
|
||||||
/// Handles that `Lua::scope` produces have a `'lua` lifetime of the scope parameter, to prevent
|
/// Inside the scope callback, all handles created through Scope will share the same unique 'lua
|
||||||
/// the handles from escaping the callback. However, this is not the only way for values to
|
/// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in
|
||||||
/// escape the callback, as they can be smuggled through Lua itself. This is safe to do, but
|
/// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function).
|
||||||
/// not very useful, because after the scope is dropped, all references to scoped values,
|
/// However, this also enables handles to scoped values to be trivially leaked from the given
|
||||||
/// whether in Lua or in rust, are invalidated. `Function` types will error when called, and
|
/// callback. This is not dangerous, though! After the callback returns, all scoped values are
|
||||||
/// `AnyUserData` types will be typeless.
|
/// invalidated, which means that though references may exist, the Rust types backing them have
|
||||||
|
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
|
||||||
|
/// would be impossible to prevent handles to scoped values from escaping anyway, since you
|
||||||
|
/// would always be able to smuggle them through Lua state.
|
||||||
pub fn scope<'scope, 'lua: 'scope, F, R>(&'lua self, f: F) -> R
|
pub fn scope<'scope, 'lua: 'scope, F, R>(&'lua self, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce(&Scope<'scope>) -> R,
|
F: FnOnce(&Scope<'lua, 'scope>) -> R,
|
||||||
{
|
{
|
||||||
let scope = Scope::new(self);
|
f(&Scope::new(self))
|
||||||
let r = f(&scope);
|
|
||||||
drop(scope);
|
|
||||||
r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
|
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
|
||||||
|
@ -397,22 +469,23 @@ impl Lua {
|
||||||
///
|
///
|
||||||
/// To succeed, the value must be a string (in which case this is a no-op), an integer, or a
|
/// To succeed, the value must be a string (in which case this is a no-op), an integer, or a
|
||||||
/// number.
|
/// number.
|
||||||
pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Option<String<'lua>> {
|
pub fn coerce_string<'lua>(&'lua self, v: Value<'lua>) -> Result<Option<String<'lua>>> {
|
||||||
match v {
|
Ok(match v {
|
||||||
Value::String(s) => Some(s),
|
Value::String(s) => Some(s),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 4);
|
assert_stack(self.state, 4);
|
||||||
|
|
||||||
self.push_value(v);
|
self.push_value(v)?;
|
||||||
let s = gc_guard(self.state, || ffi::lua_tostring(self.state, -1));
|
if protect_lua_closure(self.state, 1, 1, |state| {
|
||||||
if s.is_null() {
|
!ffi::lua_tostring(state, -1).is_null()
|
||||||
None
|
})? {
|
||||||
} else {
|
|
||||||
Some(String(self.pop_ref()))
|
Some(String(self.pop_ref()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to coerce a Lua value into an integer in a manner consistent with Lua's internal
|
/// Attempts to coerce a Lua value into an integer in a manner consistent with Lua's internal
|
||||||
|
@ -421,14 +494,14 @@ impl Lua {
|
||||||
/// To succeed, the value must be an integer, a floating point number that has an exact
|
/// To succeed, the value must be an integer, a floating point number that has an exact
|
||||||
/// representation as an integer, or a string that can be converted to an integer. Refer to the
|
/// representation as an integer, or a string that can be converted to an integer. Refer to the
|
||||||
/// Lua manual for details.
|
/// Lua manual for details.
|
||||||
pub fn coerce_integer(&self, v: Value) -> Option<Integer> {
|
pub fn coerce_integer(&self, v: Value) -> Result<Option<Integer>> {
|
||||||
match v {
|
Ok(match v {
|
||||||
Value::Integer(i) => Some(i),
|
Value::Integer(i) => Some(i),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 2);
|
assert_stack(self.state, 2);
|
||||||
|
|
||||||
self.push_value(v);
|
self.push_value(v)?;
|
||||||
let mut isint = 0;
|
let mut isint = 0;
|
||||||
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
|
let i = ffi::lua_tointegerx(self.state, -1, &mut isint);
|
||||||
if isint == 0 {
|
if isint == 0 {
|
||||||
|
@ -437,7 +510,7 @@ impl Lua {
|
||||||
Some(i)
|
Some(i)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to coerce a Lua value into a Number in a manner consistent with Lua's internal
|
/// Attempts to coerce a Lua value into a Number in a manner consistent with Lua's internal
|
||||||
|
@ -445,14 +518,14 @@ impl Lua {
|
||||||
///
|
///
|
||||||
/// To succeed, the value must be a number or a string that can be converted to a number. Refer
|
/// To succeed, the value must be a number or a string that can be converted to a number. Refer
|
||||||
/// to the Lua manual for details.
|
/// to the Lua manual for details.
|
||||||
pub fn coerce_number(&self, v: Value) -> Option<Number> {
|
pub fn coerce_number(&self, v: Value) -> Result<Option<Number>> {
|
||||||
match v {
|
Ok(match v {
|
||||||
Value::Number(n) => Some(n),
|
Value::Number(n) => Some(n),
|
||||||
v => unsafe {
|
v => unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 2);
|
assert_stack(self.state, 2);
|
||||||
|
|
||||||
self.push_value(v);
|
self.push_value(v)?;
|
||||||
let mut isnum = 0;
|
let mut isnum = 0;
|
||||||
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
|
let n = ffi::lua_tonumberx(self.state, -1, &mut isnum);
|
||||||
if isnum == 0 {
|
if isnum == 0 {
|
||||||
|
@ -461,7 +534,7 @@ impl Lua {
|
||||||
Some(n)
|
Some(n)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a value that implements `ToLua` into a `Value` instance.
|
/// Converts a value that implements `ToLua` into a `Value` instance.
|
||||||
|
@ -491,18 +564,22 @@ impl Lua {
|
||||||
///
|
///
|
||||||
/// This value will be available to rust from all `Lua` instances which share the same main
|
/// This value will be available to rust from all `Lua` instances which share the same main
|
||||||
/// state.
|
/// state.
|
||||||
pub fn set_named_registry_value<'lua, T: ToLua<'lua>>(
|
pub fn set_named_registry_value<'lua, S, T>(
|
||||||
&'lua self,
|
&'lua self,
|
||||||
name: &str,
|
name: &S,
|
||||||
t: T,
|
t: T,
|
||||||
) -> Result<()> {
|
) -> Result<()>
|
||||||
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
|
T: ToLua<'lua>,
|
||||||
|
{
|
||||||
let t = t.to_lua(self)?;
|
let t = t.to_lua(self)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 5);
|
assert_stack(self.state, 5);
|
||||||
|
|
||||||
push_string(self.state, name)?;
|
push_string(self.state, name)?;
|
||||||
self.push_value(t);
|
self.push_value(t)?;
|
||||||
|
|
||||||
unsafe extern "C" fn set_registry(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn set_registry(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
@ -518,7 +595,11 @@ impl Lua {
|
||||||
/// get a value previously set by [`set_named_registry_value`].
|
/// get a value previously set by [`set_named_registry_value`].
|
||||||
///
|
///
|
||||||
/// [`set_named_registry_value`]: #method.set_named_registry_value
|
/// [`set_named_registry_value`]: #method.set_named_registry_value
|
||||||
pub fn named_registry_value<'lua, T: FromLua<'lua>>(&'lua self, name: &str) -> Result<T> {
|
pub fn named_registry_value<'lua, S, T>(&'lua self, name: &S) -> Result<T>
|
||||||
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
|
T: FromLua<'lua>,
|
||||||
|
{
|
||||||
let value = unsafe {
|
let value = unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 4);
|
assert_stack(self.state, 4);
|
||||||
|
@ -540,7 +621,10 @@ impl Lua {
|
||||||
/// Equivalent to calling [`set_named_registry_value`] with a value of Nil.
|
/// Equivalent to calling [`set_named_registry_value`] with a value of Nil.
|
||||||
///
|
///
|
||||||
/// [`set_named_registry_value`]: #method.set_named_registry_value
|
/// [`set_named_registry_value`]: #method.set_named_registry_value
|
||||||
pub fn unset_named_registry_value<'lua>(&'lua self, name: &str) -> Result<()> {
|
pub fn unset_named_registry_value<'lua, S>(&'lua self, name: &S) -> Result<()>
|
||||||
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
|
{
|
||||||
self.set_named_registry_value(name, Nil)
|
self.set_named_registry_value(name, Nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,16 +632,21 @@ impl Lua {
|
||||||
///
|
///
|
||||||
/// This value will be available to rust from all `Lua` instances which share the same main
|
/// This value will be available to rust from all `Lua` instances which share the same main
|
||||||
/// state.
|
/// state.
|
||||||
|
///
|
||||||
|
/// Be warned, garbage collection of values held inside the registry is not automatic, see
|
||||||
|
/// [`RegistryKey`] for more details.
|
||||||
|
///
|
||||||
|
/// [`RegistryKey`]: struct.RegistryKey.html
|
||||||
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
|
pub fn create_registry_value<'lua, T: ToLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
|
||||||
let t = t.to_lua(self)?;
|
let t = t.to_lua(self)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 2);
|
assert_stack(self.state, 2);
|
||||||
|
|
||||||
self.push_value(t);
|
self.push_value(t)?;
|
||||||
let registry_id = gc_guard(self.state, || {
|
let registry_id = protect_lua_closure(self.state, 1, 0, |state| {
|
||||||
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
|
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||||
});
|
})?;
|
||||||
|
|
||||||
Ok(RegistryKey {
|
Ok(RegistryKey {
|
||||||
registry_id,
|
registry_id,
|
||||||
|
@ -634,13 +723,13 @@ impl Lua {
|
||||||
pub fn expire_registry_values(&self) {
|
pub fn expire_registry_values(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let unref_list = mem::replace(
|
let unref_list = mem::replace(
|
||||||
&mut *(*extra_data(self.state))
|
&mut *rlua_expect!(
|
||||||
.registry_unref_list
|
(*extra_data(self.state)).registry_unref_list.lock(),
|
||||||
.lock()
|
"unref list poisoned"
|
||||||
.unwrap(),
|
),
|
||||||
Some(Vec::new()),
|
Some(Vec::new()),
|
||||||
);
|
);
|
||||||
for id in unref_list.unwrap() {
|
for id in rlua_expect!(unref_list, "unref list not set") {
|
||||||
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, id);
|
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,7 +737,7 @@ impl Lua {
|
||||||
|
|
||||||
// Uses 2 stack spaces, does not call checkstack
|
// Uses 2 stack spaces, does not call checkstack
|
||||||
// TODO: return to original
|
// TODO: return to original
|
||||||
pub unsafe fn push_value(&self, value: Value) {
|
pub unsafe fn push_value(&self, value: Value) -> Result<()> {
|
||||||
match value {
|
match value {
|
||||||
Value::Nil => {
|
Value::Nil => {
|
||||||
ffi::lua_pushnil(self.state);
|
ffi::lua_pushnil(self.state);
|
||||||
|
@ -691,9 +780,11 @@ impl Lua {
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Error(e) => {
|
Value::Error(e) => {
|
||||||
push_wrapped_error(self.state, e);
|
push_wrapped_error(self.state, e)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses 2 stack spaces, does not call checkstack
|
// Uses 2 stack spaces, does not call checkstack
|
||||||
|
@ -717,15 +808,17 @@ impl Lua {
|
||||||
ud
|
ud
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::LUA_TNUMBER => if ffi::lua_isinteger(self.state, -1) != 0 {
|
ffi::LUA_TNUMBER => {
|
||||||
let i = Value::Integer(ffi::lua_tointeger(self.state, -1));
|
if ffi::lua_isinteger(self.state, -1) != 0 {
|
||||||
ffi::lua_pop(self.state, 1);
|
let i = Value::Integer(ffi::lua_tointeger(self.state, -1));
|
||||||
i
|
ffi::lua_pop(self.state, 1);
|
||||||
} else {
|
i
|
||||||
let n = Value::Number(ffi::lua_tonumber(self.state, -1));
|
} else {
|
||||||
ffi::lua_pop(self.state, 1);
|
let n = Value::Number(ffi::lua_tonumber(self.state, -1));
|
||||||
n
|
ffi::lua_pop(self.state, 1);
|
||||||
},
|
n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ffi::LUA_TSTRING => Value::String(String(self.pop_ref())),
|
ffi::LUA_TSTRING => Value::String(String(self.pop_ref())),
|
||||||
|
|
||||||
|
@ -766,7 +859,7 @@ impl Lua {
|
||||||
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
||||||
// preventing garbage collection until the returned `LuaRef` is dropped.
|
// preventing garbage collection until the returned `LuaRef` is dropped.
|
||||||
//
|
//
|
||||||
// References are stored in the stack of a specially created auxillary thread that exists only
|
// References are stored in the stack of a specially created auxiliary thread that exists only
|
||||||
// to store reference values. This is much faster than storing these in the registry, and also
|
// to store reference values. This is much faster than storing these in the registry, and also
|
||||||
// much more flexible and requires less bookkeeping than storing them directly in the currently
|
// much more flexible and requires less bookkeeping than storing them directly in the currently
|
||||||
// used stack. The implementation is somewhat biased towards the use case of a relatively small
|
// used stack. The implementation is somewhat biased towards the use case of a relatively small
|
||||||
|
@ -816,7 +909,7 @@ impl Lua {
|
||||||
})?;
|
})?;
|
||||||
for (k, m) in methods.meta_methods {
|
for (k, m) in methods.meta_methods {
|
||||||
push_string(self.state, k.name())?;
|
push_string(self.state, k.name())?;
|
||||||
self.push_value(Value::Function(self.create_callback(m)?));
|
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||||
|
|
||||||
protect_lua_closure(self.state, 3, 1, |state| {
|
protect_lua_closure(self.state, 3, 1, |state| {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
|
@ -831,7 +924,7 @@ impl Lua {
|
||||||
})?;
|
})?;
|
||||||
for (k, m) in methods.methods {
|
for (k, m) in methods.methods {
|
||||||
push_string(self.state, &k)?;
|
push_string(self.state, &k)?;
|
||||||
self.push_value(Value::Function(self.create_callback(m)?));
|
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||||
protect_lua_closure(self.state, 3, 1, |state| {
|
protect_lua_closure(self.state, 3, 1, |state| {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
})?;
|
||||||
|
@ -841,9 +934,9 @@ impl Lua {
|
||||||
ffi::lua_pop(self.state, 1);
|
ffi::lua_pop(self.state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = gc_guard(self.state, || {
|
let id = protect_lua_closure(self.state, 1, 0, |state| {
|
||||||
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
|
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||||
});
|
})?;
|
||||||
(*extra_data(self.state))
|
(*extra_data(self.state))
|
||||||
.registered_userdata
|
.registered_userdata
|
||||||
.insert(TypeId::of::<T>(), id);
|
.insert(TypeId::of::<T>(), id);
|
||||||
|
@ -863,12 +956,11 @@ impl Lua {
|
||||||
func: Callback<'callback, 'static>,
|
func: Callback<'callback, 'static>,
|
||||||
) -> Result<Function<'lua>> {
|
) -> Result<Function<'lua>> {
|
||||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||||
callback_error(state, || {
|
callback_error(state, |nargs| {
|
||||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
|
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
|
||||||
return Err(Error::CallbackDestructed);
|
return Err(Error::CallbackDestructed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let nargs = ffi::lua_gettop(state);
|
|
||||||
if nargs < ffi::LUA_MINSTACK {
|
if nargs < ffi::LUA_MINSTACK {
|
||||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||||
}
|
}
|
||||||
|
@ -876,7 +968,7 @@ impl Lua {
|
||||||
let lua = Lua {
|
let lua = Lua {
|
||||||
state: state,
|
state: state,
|
||||||
main_state: main_state(state),
|
main_state: main_state(state),
|
||||||
_phantom: PhantomData,
|
_no_ref_unwind_safe: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut args = MultiValue::new();
|
let mut args = MultiValue::new();
|
||||||
|
@ -892,7 +984,7 @@ impl Lua {
|
||||||
|
|
||||||
check_stack(state, nresults)?;
|
check_stack(state, nresults)?;
|
||||||
for r in results {
|
for r in results {
|
||||||
lua.push_value(r);
|
lua.push_value(r)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(nresults)
|
Ok(nresults)
|
||||||
|
@ -957,6 +1049,89 @@ unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
|
||||||
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData)
|
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||||
|
///
|
||||||
|
/// [`Lua::load`]: struct.Lua.html#method.load
|
||||||
|
#[must_use = "`Chunk`s do nothing unless one of `exec`, `eval`, `call`, or `into_function` are called on them"]
|
||||||
|
pub struct Chunk<'lua, 'a> {
|
||||||
|
lua: &'lua Lua,
|
||||||
|
source: &'a [u8],
|
||||||
|
name: Option<CString>,
|
||||||
|
env: Option<Value<'lua>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||||
|
/// Sets the name of this chunk, which results in more informative error traces.
|
||||||
|
pub fn set_name<S: ?Sized + AsRef<[u8]>>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
|
||||||
|
let name =
|
||||||
|
CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
|
||||||
|
from: "&str",
|
||||||
|
to: "string",
|
||||||
|
message: Some(e.to_string()),
|
||||||
|
})?;
|
||||||
|
self.name = Some(name);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
|
||||||
|
///
|
||||||
|
/// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
|
||||||
|
/// variable inside the chunk. By default this value is set to the global environment.
|
||||||
|
///
|
||||||
|
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
|
||||||
|
/// the chunk will refer to the given environment rather than the global one.
|
||||||
|
///
|
||||||
|
/// All global variables (including the standard library!) are looked up in `_ENV`, so it may be
|
||||||
|
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||||
|
/// useful.
|
||||||
|
pub fn set_environment<V: ToLua<'lua>>(mut self, env: V) -> Result<Chunk<'lua, 'a>> {
|
||||||
|
self.env = Some(env.to_lua(self.lua)?);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute this chunk of code.
|
||||||
|
///
|
||||||
|
/// This is equivalent to calling the chunk function with no arguments and no return values.
|
||||||
|
pub fn exec(self) -> Result<()> {
|
||||||
|
self.call(())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate the chunk as either an expression or block.
|
||||||
|
///
|
||||||
|
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
|
||||||
|
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
|
||||||
|
/// and this is equivalent to calling `exec`.
|
||||||
|
pub fn eval<R: FromLuaMulti<'lua>>(self) -> 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.
|
||||||
|
let mut expression_source = b"return ".to_vec();
|
||||||
|
expression_source.extend(self.source);
|
||||||
|
if let Ok(function) =
|
||||||
|
self.lua.load_chunk(&expression_source, self.name.as_ref(), self.env.clone())
|
||||||
|
{
|
||||||
|
function.call(())
|
||||||
|
} else {
|
||||||
|
self.call(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the chunk function and call it with the given arguemnts.
|
||||||
|
///
|
||||||
|
/// This is equivalent to `into_function` and calling the resulting function.
|
||||||
|
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(self, args: A) -> Result<R> {
|
||||||
|
self.into_function()?.call(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load this chunk into a regular `Function`.
|
||||||
|
///
|
||||||
|
/// This simply compiles the chunk without actually executing it.
|
||||||
|
pub fn into_function(self) -> Result<Function<'lua>> {
|
||||||
|
self.lua.load_chunk(self.source, self.name.as_ref(), self.env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
||||||
if let Some(free) = (*extra).ref_free.pop() {
|
if let Some(free) = (*extra).ref_free.pop() {
|
||||||
ffi::lua_replace((*extra).ref_thread, free);
|
ffi::lua_replace((*extra).ref_thread, free);
|
||||||
|
@ -966,7 +1141,7 @@ unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
||||||
// It is a user error to create enough references to exhaust the Lua max stack size for
|
// It is a user error to create enough references to exhaust the Lua max stack size for
|
||||||
// the ref thread.
|
// the ref thread.
|
||||||
if ffi::lua_checkstack((*extra).ref_thread, (*extra).ref_stack_size) == 0 {
|
if ffi::lua_checkstack((*extra).ref_thread, (*extra).ref_stack_size) == 0 {
|
||||||
panic!("cannot create a Lua reference, out of auxillary stack space");
|
rlua_panic!("cannot create a Lua reference, out of auxiliary stack space");
|
||||||
}
|
}
|
||||||
(*extra).ref_stack_size *= 2;
|
(*extra).ref_stack_size *= 2;
|
||||||
}
|
}
|
||||||
|
@ -978,60 +1153,64 @@ unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
||||||
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
|
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
|
|
||||||
struct StaticUserDataMethods<'lua, T: 'static + UserData> {
|
struct StaticUserDataMethods<'lua, T: 'static + UserData> {
|
||||||
methods: HashMap<StdString, Callback<'lua, 'static>>,
|
methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
|
||||||
meta_methods: HashMap<MetaMethod, Callback<'lua, 'static>>,
|
meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
|
||||||
_type: PhantomData<T>,
|
_type: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
|
impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
|
||||||
fn default() -> StaticUserDataMethods<'lua, T> {
|
fn default() -> StaticUserDataMethods<'lua, T> {
|
||||||
StaticUserDataMethods {
|
StaticUserDataMethods {
|
||||||
methods: HashMap::new(),
|
methods: Vec::new(),
|
||||||
meta_methods: HashMap::new(),
|
meta_methods: Vec::new(),
|
||||||
_type: PhantomData,
|
_type: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> {
|
impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> {
|
||||||
fn add_method<A, R, M>(&mut self, name: &str, method: M)
|
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods
|
self.methods
|
||||||
.insert(name.to_owned(), Self::box_method(method));
|
.push((name.as_ref().to_vec(), Self::box_method(method)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_method_mut<A, R, M>(&mut self, name: &str, method: M)
|
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods
|
self.methods
|
||||||
.insert(name.to_owned(), Self::box_method_mut(method));
|
.push((name.as_ref().to_vec(), Self::box_method_mut(method)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_function<A, R, F>(&mut self, name: &str, function: F)
|
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods
|
self.methods
|
||||||
.insert(name.to_owned(), Self::box_function(function));
|
.push((name.as_ref().to_vec(), Self::box_function(function)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_function_mut<A, R, F>(&mut self, name: &str, function: F)
|
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods
|
self.methods
|
||||||
.insert(name.to_owned(), Self::box_function_mut(function));
|
.push((name.as_ref().to_vec(), Self::box_function_mut(function)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||||
|
@ -1040,7 +1219,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.insert(meta, Self::box_method(method));
|
self.meta_methods.push((meta, Self::box_method(method)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||||
|
@ -1049,7 +1228,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.insert(meta, Self::box_method_mut(method));
|
self.meta_methods.push((meta, Self::box_method_mut(method)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||||
|
@ -1058,7 +1237,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.insert(meta, Self::box_function(function));
|
self.meta_methods.push((meta, Self::box_function(function)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||||
|
@ -1068,7 +1247,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods
|
self.meta_methods
|
||||||
.insert(meta, Self::box_function_mut(function));
|
.push((meta, Self::box_function_mut(function)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
macro_rules! bug_msg {
|
||||||
|
($arg:expr) => {
|
||||||
|
concat!(
|
||||||
|
"rlua internal error: ",
|
||||||
|
$arg,
|
||||||
|
" (this is a bug, please file an issue)"
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! cstr {
|
macro_rules! cstr {
|
||||||
($s:expr) => {
|
($s:expr) => {
|
||||||
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
|
concat!($s, "\0") as *const str as *const [::std::os::raw::c_char]
|
||||||
|
@ -5,62 +15,66 @@ macro_rules! cstr {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! abort {
|
|
||||||
($msg:expr) => {
|
|
||||||
{
|
|
||||||
eprintln!($msg);
|
|
||||||
::std::process::abort()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
($msg:expr, $($arg:tt)+) => {
|
|
||||||
{
|
|
||||||
eprintln!($msg, $($arg)+);
|
|
||||||
::std::process::abort()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! rlua_panic {
|
macro_rules! rlua_panic {
|
||||||
($msg:expr) => {
|
($msg:expr) => {
|
||||||
panic!(concat!("rlua internal error: ", $msg));
|
panic!(bug_msg!($msg));
|
||||||
};
|
};
|
||||||
|
|
||||||
($msg:expr, $($arg:tt)+) => {
|
($msg:expr,) => {
|
||||||
panic!(concat!("rlua internal error: ", $msg), $($arg)+);
|
rlua_panic!($msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
($msg:expr, $($arg:expr),+) => {
|
||||||
|
panic!(bug_msg!($msg), $($arg),+);
|
||||||
|
};
|
||||||
|
|
||||||
|
($msg:expr, $($arg:expr),+,) => {
|
||||||
|
rlua_panic!($msg, $($arg),+);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! rlua_assert {
|
macro_rules! rlua_assert {
|
||||||
($cond:expr, $msg:expr) => {
|
($cond:expr, $msg:expr) => {
|
||||||
assert!($cond, concat!("rlua internal error: ", $msg));
|
assert!($cond, bug_msg!($msg));
|
||||||
};
|
};
|
||||||
|
|
||||||
($cond:expr, $msg:expr, $($arg:tt)+) => {
|
($cond:expr, $msg:expr,) => {
|
||||||
assert!($cond, concat!("rlua internal error: ", $msg), $($arg)+);
|
rlua_assert!($cond, $msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
($cond:expr, $msg:expr, $($arg:expr),+) => {
|
||||||
|
assert!($cond, bug_msg!($msg), $($arg),+);
|
||||||
|
};
|
||||||
|
|
||||||
|
($cond:expr, $msg:expr, $($arg:expr),+,) => {
|
||||||
|
rlua_assert!($cond, $msg, $($arg),+);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! rlua_debug_assert {
|
macro_rules! rlua_debug_assert {
|
||||||
($cond:expr, $msg:expr) => {
|
($cond:expr, $msg:expr) => {
|
||||||
debug_assert!($cond, concat!("rlua internal error: ", $msg));
|
debug_assert!($cond, bug_msg!($msg));
|
||||||
};
|
};
|
||||||
|
|
||||||
($cond:expr, $msg:expr, $($arg:tt)+) => {
|
($cond:expr, $msg:expr,) => {
|
||||||
debug_assert!($cond, concat!("rlua internal error: ", $msg), $($arg)+);
|
rlua_debug_assert!($cond, $msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
($cond:expr, $msg:expr, $($arg:expr),+) => {
|
||||||
|
debug_assert!($cond, bug_msg!($msg), $($arg),+);
|
||||||
|
};
|
||||||
|
|
||||||
|
($cond:expr, $msg:expr, $($arg:expr),+,) => {
|
||||||
|
rlua_debug_assert!($cond, $msg, $($arg),+);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! rlua_abort {
|
macro_rules! rlua_expect {
|
||||||
($msg:expr) => {
|
($res:expr, $msg:expr) => {
|
||||||
{
|
$res.expect(bug_msg!($msg))
|
||||||
abort!(concat!("rlua internal error: ", $msg));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
($msg:expr, $($arg:tt)+) => {
|
($res:expr, $msg:expr,) => {
|
||||||
{
|
rlua_expect!($res, $msg)
|
||||||
abort!(concat!("rlua internal error, aborting!: ", $msg), $($arg)+);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
21
src/multi.rs
21
src/multi.rs
|
@ -2,9 +2,9 @@ use std::iter::FromIterator;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
|
|
||||||
use error::Result;
|
use crate::error::Result;
|
||||||
use lua::Lua;
|
use crate::lua::Lua;
|
||||||
use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
|
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
|
||||||
|
|
||||||
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
|
||||||
/// on success, or in the case of an error, returning `nil` and an error message.
|
/// on success, or in the case of an error, returning `nil` and an error message.
|
||||||
|
@ -62,21 +62,16 @@ impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Variadic, Result};
|
/// # use rlua::{Lua, Variadic, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
///
|
|
||||||
/// let add = lua.create_function(|_, vals: Variadic<f64>| -> Result<f64> {
|
/// let add = lua.create_function(|_, vals: Variadic<f64>| -> Result<f64> {
|
||||||
/// Ok(vals.iter().sum())
|
/// Ok(vals.iter().sum())
|
||||||
/// }).unwrap();
|
/// }).unwrap();
|
||||||
/// lua.globals().set("add", add)?;
|
/// lua.globals().set("add", add)?;
|
||||||
/// assert_eq!(lua.eval::<_, f64>("add(3, 2, 5)", None)?, 10.0);
|
/// assert_eq!(lua.load("add(3, 2, 5)").eval::<f32>()?, 10.0);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`FromLua`]: trait.FromLua.html
|
/// [`FromLua`]: trait.FromLua.html
|
||||||
|
@ -91,6 +86,12 @@ impl<T> Variadic<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Variadic<T> {
|
||||||
|
fn default() -> Variadic<T> {
|
||||||
|
Variadic::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> FromIterator<T> for Variadic<T> {
|
impl<T> FromIterator<T> for Variadic<T> {
|
||||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||||
Variadic(Vec::from_iter(iter))
|
Variadic(Vec::from_iter(iter))
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//! Re-exports most types with an extra `Lua*` prefix to prevent name clashes.
|
//! Re-exports most types with an extra `Lua*` prefix to prevent name clashes.
|
||||||
|
|
||||||
pub use {
|
pub use crate::{
|
||||||
AnyUserData as LuaAnyUserData, Error as LuaError, ExternalError as LuaExternalError,
|
AnyUserData as LuaAnyUserData, Chunk as LuaChunk,
|
||||||
ExternalResult as LuaExternalResult, FromLua, FromLuaMulti, Function as LuaFunction,
|
Error as LuaError, ExternalError as LuaExternalError, ExternalResult as LuaExternalResult,
|
||||||
|
FromLua, FromLuaMulti, Function as LuaFunction,
|
||||||
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod,
|
Integer as LuaInteger, LightUserData as LuaLightUserData, Lua, MetaMethod as LuaMetaMethod,
|
||||||
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
MultiValue as LuaMultiValue, Nil as LuaNil, Number as LuaNumber, RegistryKey as LuaRegistryKey,
|
||||||
Result as LuaResult, Scope as LuaScope, String as LuaString, Table as LuaTable,
|
Result as LuaResult, Scope as LuaScope, String as LuaString, Table as LuaTable,
|
||||||
|
|
221
src/scope.rs
221
src/scope.rs
|
@ -1,67 +1,65 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::string::String as StdString;
|
use std::cell::Cell;
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use ffi;
|
use crate::lua::Lua;
|
||||||
use function::Function;
|
use crate::ffi;
|
||||||
use lua::Lua;
|
use crate::function::Function;
|
||||||
use types::Callback;
|
use crate::types::{Callback, LuaRef};
|
||||||
use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
use util::{
|
use crate::util::{
|
||||||
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
|
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
|
||||||
take_userdata, StackGuard,
|
take_userdata, StackGuard,
|
||||||
};
|
};
|
||||||
use value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
||||||
|
|
||||||
/// Constructed by the [`Lua::scope`] method, allows temporarily passing to Lua userdata that is
|
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||||
/// !Send, and callbacks that are !Send and not 'static.
|
/// callbacks that are not required to be Send or 'static.
|
||||||
///
|
///
|
||||||
/// See [`Lua::scope`] for more details.
|
/// See [`Lua::scope`] for more details.
|
||||||
///
|
///
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
pub struct Scope<'scope> {
|
pub struct Scope<'lua, 'scope> {
|
||||||
lua: &'scope Lua,
|
lua: &'lua Lua,
|
||||||
destructors: RefCell<Vec<Box<Fn() -> Box<Any> + 'scope>>>,
|
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Box<dyn Any>)>>,
|
||||||
// 'scope lifetime must be invariant
|
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||||
_scope: PhantomData<&'scope mut &'scope ()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'scope> Scope<'scope> {
|
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
pub(crate) fn new(lua: &'scope Lua) -> Scope {
|
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
|
||||||
Scope {
|
Scope {
|
||||||
lua,
|
lua,
|
||||||
destructors: RefCell::new(Vec::new()),
|
destructors: RefCell::new(Vec::new()),
|
||||||
_scope: PhantomData,
|
_scope_invariant: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
|
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
|
||||||
///
|
///
|
||||||
/// This is a version of [`Lua::create_function`] that creates a callback which expires on scope
|
/// This is a version of [`Lua::create_function`] that creates a callback which expires on
|
||||||
/// drop. See [`Lua::scope`] for more details.
|
/// scope drop. See [`Lua::scope`] for more details.
|
||||||
///
|
///
|
||||||
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
|
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
|
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
|
||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'callback>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'callback>,
|
||||||
F: 'scope + Fn(&'lua Lua, A) -> Result<R>,
|
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
// Safe, because 'scope must outlive 'lua (due to Self containing 'scope), however the
|
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
|
||||||
// callback itself must be 'scope lifetime, so the function should not be able to capture
|
// callback itself must be 'scope lifetime, so the function should not be able to capture
|
||||||
// anything of 'lua lifetime. 'scope can't be shortened due to being invariant, and the
|
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
|
||||||
// 'lua lifetime here can't be enlarged due to coming from a universal quantification in
|
// the 'callback lifetime here can't be enlarged due to coming from a universal
|
||||||
// Lua::scope.
|
// quantification in Lua::scope.
|
||||||
//
|
//
|
||||||
// I hope I got this explanation right, but in any case this is tested with compiletest_rs
|
// I hope I got this explanation right, but in any case this is tested with compiletest_rs
|
||||||
// to make sure callbacks can't capture handles with lifetimes outside the scope, inside the
|
// to make sure callbacks can't capture handles with lifetime outside the scope, inside the
|
||||||
// scope, and owned inside the callback itself.
|
// scope, and owned inside the callback itself.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.create_callback(Box::new(move |lua, args| {
|
self.create_callback(Box::new(move |lua, args| {
|
||||||
|
@ -72,17 +70,20 @@ impl<'scope> Scope<'scope> {
|
||||||
|
|
||||||
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
|
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
|
||||||
///
|
///
|
||||||
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires on
|
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
|
||||||
/// scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
|
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
|
||||||
///
|
///
|
||||||
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
|
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
/// [`Scope::create_function`]: #method.create_function
|
/// [`Scope::create_function`]: #method.create_function
|
||||||
pub fn create_function_mut<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
|
pub fn create_function_mut<'callback, A, R, F>(
|
||||||
|
&'callback self,
|
||||||
|
func: F,
|
||||||
|
) -> Result<Function<'lua>>
|
||||||
where
|
where
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'callback>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'callback>,
|
||||||
F: 'scope + FnMut(&'lua Lua, A) -> Result<R>,
|
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
let func = RefCell::new(func);
|
let func = RefCell::new(func);
|
||||||
self.create_function(move |lua, args| {
|
self.create_function(move |lua, args| {
|
||||||
|
@ -94,13 +95,13 @@ impl<'scope> Scope<'scope> {
|
||||||
|
|
||||||
/// Create a Lua userdata object from a custom userdata type.
|
/// Create a Lua userdata object from a custom userdata type.
|
||||||
///
|
///
|
||||||
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on scope
|
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
|
||||||
/// drop, and does not require that the userdata type be Send (but still requires that the
|
/// scope drop, and does not require that the userdata type be Send (but still requires that the
|
||||||
/// UserData be 'static). See [`Lua::scope`] for more details.
|
/// UserData be 'static). See [`Lua::scope`] for more details.
|
||||||
///
|
///
|
||||||
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
pub fn create_static_userdata<'lua, T>(&'lua self, data: T) -> Result<AnyUserData<'lua>>
|
pub fn create_static_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||||
where
|
where
|
||||||
T: 'static + UserData,
|
T: 'static + UserData,
|
||||||
{
|
{
|
||||||
|
@ -108,13 +109,12 @@ impl<'scope> Scope<'scope> {
|
||||||
// thread while the Scope is alive (or the returned AnyUserData handle even).
|
// thread while the Scope is alive (or the returned AnyUserData handle even).
|
||||||
unsafe {
|
unsafe {
|
||||||
let u = self.lua.make_userdata(data)?;
|
let u = self.lua.make_userdata(data)?;
|
||||||
let mut destructors = self.destructors.borrow_mut();
|
self.destructors.borrow_mut().push((u.0.clone(), |u| {
|
||||||
let u_destruct = u.0.clone();
|
let state = u.lua.state;
|
||||||
destructors.push(Box::new(move || {
|
assert_stack(state, 2);
|
||||||
let state = u_destruct.lua.state;
|
u.lua.push_ref(&u);
|
||||||
let _sg = StackGuard::new(state);
|
// We know the destructor has not run yet because we hold a reference to the
|
||||||
assert_stack(state, 1);
|
// userdata.
|
||||||
u_destruct.lua.push_ref(&u_destruct);
|
|
||||||
Box::new(take_userdata::<RefCell<T>>(state))
|
Box::new(take_userdata::<RefCell<T>>(state))
|
||||||
}));
|
}));
|
||||||
Ok(u)
|
Ok(u)
|
||||||
|
@ -123,9 +123,9 @@ impl<'scope> Scope<'scope> {
|
||||||
|
|
||||||
/// Create a Lua userdata object from a custom userdata type.
|
/// Create a Lua userdata object from a custom userdata type.
|
||||||
///
|
///
|
||||||
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on scope
|
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
|
||||||
/// drop, and does not require that the userdata type be Send or 'static. See [`Lua::scope`] for
|
/// scope drop, and does not require that the userdata type be Send or 'static. See
|
||||||
/// more details.
|
/// [`Lua::scope`] for more details.
|
||||||
///
|
///
|
||||||
/// Lifting the requirement that the UserData type be 'static comes with some important
|
/// Lifting the requirement that the UserData type be 'static comes with some important
|
||||||
/// limitations, so if you only need to eliminate the Send requirement, it is probably better to
|
/// limitations, so if you only need to eliminate the Send requirement, it is probably better to
|
||||||
|
@ -144,34 +144,34 @@ impl<'scope> Scope<'scope> {
|
||||||
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
||||||
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||||
pub fn create_nonstatic_userdata<'lua, T>(&'lua self, data: T) -> Result<AnyUserData<'lua>>
|
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||||
where
|
where
|
||||||
T: 'scope + UserData,
|
T: 'scope + UserData,
|
||||||
{
|
{
|
||||||
let data = Rc::new(RefCell::new(data));
|
let data = Rc::new(RefCell::new(data));
|
||||||
|
|
||||||
// 'callback outliving 'scope is a lie to make the types work out, required due to the
|
// 'callback outliving 'scope is a lie to make the types work out, required due to the
|
||||||
// inability to work with the "correct" universally quantified callback type. This is safe
|
// inability to work with the more correct callback type that is universally quantified over
|
||||||
// though, because actual method callbacks are all 'static so they can't capture 'callback
|
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
|
||||||
// handles anyway.
|
// lifetime, so none of the static methods UserData types can add can possibly capture
|
||||||
|
// parameters.
|
||||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||||
scope: &'lua Scope<'scope>,
|
scope: &Scope<'lua, 'scope>,
|
||||||
data: Rc<RefCell<T>>,
|
data: Rc<RefCell<T>>,
|
||||||
method: NonStaticMethod<'callback, T>,
|
method: NonStaticMethod<'callback, T>,
|
||||||
) -> Result<Function<'lua>> {
|
) -> Result<Function<'lua>> {
|
||||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||||
// userdata, where we pretend there is a unique type per call to
|
// userdata, where we pretend there is a unique type per call to
|
||||||
// Scope::create_nonstatic_userdata. You can grab a method from a userdata and call it
|
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
|
||||||
// on a mismatched userdata type, which when using normal 'static userdata will fail
|
// it on a mismatched userdata type, which when using normal 'static userdata will fail
|
||||||
// with a type mismatch, but here without this check would proceed as though you had
|
// with a type mismatch, but here without this check would proceed as though you had
|
||||||
// called the method on the original value (since we otherwise completely ignore the
|
// called the method on the original value (since we otherwise completely ignore the
|
||||||
// first argument).
|
// first argument).
|
||||||
let check_data = data.clone();
|
let check_data = data.clone();
|
||||||
let check_ud_type = move |lua: &Lua, value| {
|
let check_ud_type = move |lua: &'callback Lua, value| {
|
||||||
if let Some(value) = value {
|
if let Some(value) = value {
|
||||||
if let Value::UserData(u) = value {
|
if let Value::UserData(u) = value {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
|
||||||
assert_stack(lua.state, 1);
|
assert_stack(lua.state, 1);
|
||||||
lua.push_ref(&u.0);
|
lua.push_ref(&u.0);
|
||||||
ffi::lua_getuservalue(lua.state, -1);
|
ffi::lua_getuservalue(lua.state, -1);
|
||||||
|
@ -248,7 +248,7 @@ impl<'scope> Scope<'scope> {
|
||||||
|
|
||||||
for (k, m) in ud_methods.meta_methods {
|
for (k, m) in ud_methods.meta_methods {
|
||||||
push_string(lua.state, k.name())?;
|
push_string(lua.state, k.name())?;
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?));
|
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
||||||
|
|
||||||
protect_lua_closure(lua.state, 3, 1, |state| {
|
protect_lua_closure(lua.state, 3, 1, |state| {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
|
@ -263,7 +263,7 @@ impl<'scope> Scope<'scope> {
|
||||||
})?;
|
})?;
|
||||||
for (k, m) in ud_methods.methods {
|
for (k, m) in ud_methods.methods {
|
||||||
push_string(lua.state, &k)?;
|
push_string(lua.state, &k)?;
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?));
|
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
||||||
protect_lua_closure(lua.state, 3, 1, |state| {
|
protect_lua_closure(lua.state, 3, 1, |state| {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
})?;
|
})?;
|
||||||
|
@ -279,25 +279,26 @@ impl<'scope> Scope<'scope> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsafe, because the callback (since it is non-'static) can capture any value with 'callback
|
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as
|
||||||
// scope, such as improperly holding onto an argument. So in order for this to be safe, the
|
// improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
|
||||||
// callback must NOT capture any arguments.
|
// lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
|
||||||
unsafe fn create_callback<'lua, 'callback>(
|
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
|
||||||
&'lua self,
|
// must NOT capture any parameters.
|
||||||
|
unsafe fn create_callback<'callback>(
|
||||||
|
&self,
|
||||||
f: Callback<'callback, 'scope>,
|
f: Callback<'callback, 'scope>,
|
||||||
) -> Result<Function<'lua>> {
|
) -> Result<Function<'lua>> {
|
||||||
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'callback, 'static>>(f);
|
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
|
||||||
let f = self.lua.create_callback(f)?;
|
let f = self.lua.create_callback(f)?;
|
||||||
|
|
||||||
let mut destructors = self.destructors.borrow_mut();
|
let mut destructors = self.destructors.borrow_mut();
|
||||||
let f_destruct = f.0.clone();
|
destructors.push((f.0.clone(), |f| {
|
||||||
destructors.push(Box::new(move || {
|
let state = f.lua.state;
|
||||||
let state = f_destruct.lua.state;
|
assert_stack(state, 3);
|
||||||
let _sg = StackGuard::new(state);
|
f.lua.push_ref(&f);
|
||||||
assert_stack(state, 2);
|
|
||||||
f_destruct.lua.push_ref(&f_destruct);
|
|
||||||
|
|
||||||
ffi::lua_getupvalue(state, -1, 1);
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
|
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||||
let ud = take_userdata::<Callback>(state);
|
let ud = take_userdata::<Callback>(state);
|
||||||
|
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(state);
|
||||||
|
@ -310,98 +311,104 @@ impl<'scope> Scope<'scope> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'scope> Drop for Scope<'scope> {
|
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// We separate the action of invalidating the userdata in Lua and actually dropping the
|
// We separate the action of invalidating the userdata in Lua and actually dropping the
|
||||||
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
|
// userdata type into two phases. This is so that, in the event a userdata drop panics, we
|
||||||
// can be sure that all of the userdata in Lua is actually invalidated.
|
// can be sure that all of the userdata in Lua is actually invalidated.
|
||||||
|
|
||||||
|
// All destructors are non-panicking, so this is fine
|
||||||
let to_drop = self
|
let to_drop = self
|
||||||
.destructors
|
.destructors
|
||||||
.get_mut()
|
.get_mut()
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|destructor| destructor())
|
.map(|(r, dest)| dest(r))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
drop(to_drop);
|
drop(to_drop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NonStaticMethod<'lua, T> {
|
enum NonStaticMethod<'lua, T> {
|
||||||
Method(Box<Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
MethodMut(Box<FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
Function(Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
FunctionMut(Box<FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NonStaticUserDataMethods<'lua, T: UserData> {
|
struct NonStaticUserDataMethods<'lua, T: UserData> {
|
||||||
methods: HashMap<StdString, NonStaticMethod<'lua, T>>,
|
methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||||
meta_methods: HashMap<MetaMethod, NonStaticMethod<'lua, T>>,
|
meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
|
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
|
||||||
fn default() -> NonStaticUserDataMethods<'lua, T> {
|
fn default() -> NonStaticUserDataMethods<'lua, T> {
|
||||||
NonStaticUserDataMethods {
|
NonStaticUserDataMethods {
|
||||||
methods: HashMap::new(),
|
methods: Vec::new(),
|
||||||
meta_methods: HashMap::new(),
|
meta_methods: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
|
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
|
||||||
fn add_method<A, R, M>(&mut self, name: &str, method: M)
|
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods.insert(
|
self.methods.push((
|
||||||
name.to_owned(),
|
name.as_ref().to_vec(),
|
||||||
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_method_mut<A, R, M>(&mut self, name: &str, mut method: M)
|
fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods.insert(
|
self.methods.push((
|
||||||
name.to_owned(),
|
name.as_ref().to_vec(),
|
||||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_function<A, R, F>(&mut self, name: &str, function: F)
|
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods.insert(
|
self.methods.push((
|
||||||
name.to_owned(),
|
name.as_ref().to_vec(),
|
||||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_function_mut<A, R, F>(&mut self, name: &str, mut function: F)
|
fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.methods.insert(
|
self.methods.push((
|
||||||
name.to_owned(),
|
name.as_ref().to_vec(),
|
||||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||||
|
@ -410,12 +417,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.insert(
|
self.meta_methods.push((
|
||||||
meta,
|
meta,
|
||||||
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M)
|
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M)
|
||||||
|
@ -424,12 +431,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.insert(
|
self.meta_methods.push((
|
||||||
meta,
|
meta,
|
||||||
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||||
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||||
|
@ -438,12 +445,12 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.insert(
|
self.meta_methods.push((
|
||||||
meta,
|
meta,
|
||||||
NonStaticMethod::Function(Box::new(move |lua, args| {
|
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F)
|
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F)
|
||||||
|
@ -452,11 +459,11 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
{
|
{
|
||||||
self.meta_methods.insert(
|
self.meta_methods.push((
|
||||||
meta,
|
meta,
|
||||||
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||||
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
})),
|
})),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{slice, str};
|
use std::{slice, str};
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use ffi;
|
use crate::ffi;
|
||||||
use types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use util::{assert_stack, StackGuard};
|
use crate::util::{assert_stack, StackGuard};
|
||||||
|
|
||||||
/// Handle to an internal Lua string.
|
/// Handle to an internal Lua string.
|
||||||
///
|
///
|
||||||
|
@ -17,22 +17,18 @@ impl<'lua> String<'lua> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, String, Result};
|
/// # use rlua::{Lua, String, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// let globals = lua.globals();
|
/// let globals = lua.globals();
|
||||||
///
|
///
|
||||||
/// let version: String = globals.get("_VERSION")?;
|
/// let version: String = globals.get("_VERSION")?;
|
||||||
/// assert!(version.to_str().unwrap().contains("Lua"));
|
/// assert!(version.to_str().unwrap().contains("Lua"));
|
||||||
///
|
///
|
||||||
/// let non_utf8: String = lua.eval(r#" "test\xff" "#, None)?;
|
/// let non_utf8: String = lua.load(r#" "test\xff" "#).eval()?;
|
||||||
/// assert!(non_utf8.to_str().is_err());
|
/// assert!(non_utf8.to_str().is_err());
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn to_str(&self) -> Result<&str> {
|
pub fn to_str(&self) -> Result<&str> {
|
||||||
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
|
str::from_utf8(self.as_bytes()).map_err(|e| Error::FromLuaConversionError {
|
||||||
|
@ -50,14 +46,13 @@ impl<'lua> String<'lua> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
/// # use rlua::{Lua, String, Result};
|
||||||
/// # use rlua::{Lua, String};
|
/// # fn main() -> Result<()> {
|
||||||
/// # fn main() {
|
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
///
|
/// let non_utf8: String = lua.load(r#" "test\xff" "#).eval()?;
|
||||||
/// let non_utf8: String = lua.eval(r#" "test\xff" "#, None).unwrap();
|
|
||||||
/// assert!(non_utf8.to_str().is_err()); // oh no :(
|
/// assert!(non_utf8.to_str().is_err()); // oh no :(
|
||||||
/// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
|
/// assert_eq!(non_utf8.as_bytes(), &b"test\xff"[..]);
|
||||||
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
|
59
src/table.rs
59
src/table.rs
|
@ -1,11 +1,11 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
use error::Result;
|
use crate::error::Result;
|
||||||
use ffi;
|
use crate::ffi;
|
||||||
use types::{Integer, LuaRef};
|
use crate::types::{Integer, LuaRef};
|
||||||
use util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
use crate::util::{assert_stack, protect_lua, protect_lua_closure, StackGuard};
|
||||||
use value::{FromLua, Nil, ToLua, Value};
|
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||||
|
|
||||||
/// Handle to an internal Lua table.
|
/// Handle to an internal Lua table.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -24,9 +24,8 @@ impl<'lua> Table<'lua> {
|
||||||
/// Export a value as a global to make it usable from Lua:
|
/// Export a value as a global to make it usable from Lua:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Result};
|
/// # use rlua::{Lua, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// let globals = lua.globals();
|
/// let globals = lua.globals();
|
||||||
///
|
///
|
||||||
|
@ -43,9 +42,6 @@ impl<'lua> Table<'lua> {
|
||||||
/// "#, None)?;
|
/// "#, None)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`raw_set`]: #method.raw_set
|
/// [`raw_set`]: #method.raw_set
|
||||||
|
@ -58,8 +54,8 @@ impl<'lua> Table<'lua> {
|
||||||
assert_stack(lua.state, 6);
|
assert_stack(lua.state, 6);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key);
|
lua.push_value(key)?;
|
||||||
lua.push_value(value);
|
lua.push_value(value)?;
|
||||||
|
|
||||||
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_settable(state, -3);
|
ffi::lua_settable(state, -3);
|
||||||
|
@ -81,9 +77,8 @@ impl<'lua> Table<'lua> {
|
||||||
/// Query the version of the Lua interpreter:
|
/// Query the version of the Lua interpreter:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Result};
|
/// # use rlua::{Lua, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// let globals = lua.globals();
|
/// let globals = lua.globals();
|
||||||
///
|
///
|
||||||
|
@ -91,9 +86,6 @@ impl<'lua> Table<'lua> {
|
||||||
/// println!("Lua version: {}", version);
|
/// println!("Lua version: {}", version);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`raw_get`]: #method.raw_get
|
/// [`raw_get`]: #method.raw_get
|
||||||
|
@ -105,7 +97,7 @@ impl<'lua> Table<'lua> {
|
||||||
assert_stack(lua.state, 5);
|
assert_stack(lua.state, 5);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key);
|
lua.push_value(key)?;
|
||||||
|
|
||||||
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_gettable(state, -2);
|
ffi::lua_gettable(state, -2);
|
||||||
|
@ -127,7 +119,7 @@ impl<'lua> Table<'lua> {
|
||||||
assert_stack(lua.state, 5);
|
assert_stack(lua.state, 5);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key);
|
lua.push_value(key)?;
|
||||||
|
|
||||||
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_gettable(state, -2);
|
ffi::lua_gettable(state, -2);
|
||||||
|
@ -151,8 +143,8 @@ impl<'lua> Table<'lua> {
|
||||||
assert_stack(lua.state, 6);
|
assert_stack(lua.state, 6);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key);
|
lua.push_value(key)?;
|
||||||
lua.push_value(value);
|
lua.push_value(value)?;
|
||||||
|
|
||||||
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
|
@ -173,7 +165,7 @@ impl<'lua> Table<'lua> {
|
||||||
assert_stack(lua.state, 3);
|
assert_stack(lua.state, 3);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(key);
|
lua.push_value(key)?;
|
||||||
ffi::lua_rawget(lua.state, -2);
|
ffi::lua_rawget(lua.state, -2);
|
||||||
lua.pop_value()
|
lua.pop_value()
|
||||||
};
|
};
|
||||||
|
@ -261,9 +253,8 @@ impl<'lua> Table<'lua> {
|
||||||
/// Iterate over all globals:
|
/// Iterate over all globals:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Result, Value};
|
/// # use rlua::{Lua, Result, Value};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// let globals = lua.globals();
|
/// let globals = lua.globals();
|
||||||
///
|
///
|
||||||
|
@ -274,9 +265,6 @@ impl<'lua> Table<'lua> {
|
||||||
/// }
|
/// }
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Result`]: type.Result.html
|
/// [`Result`]: type.Result.html
|
||||||
|
@ -307,11 +295,17 @@ impl<'lua> Table<'lua> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, Result, Table};
|
/// # use rlua::{Lua, Result, Table};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// let my_table: Table = lua.eval("{ [1] = 4, [2] = 5, [4] = 7, key = 2 }", None)?;
|
/// let my_table: Table = lua.load(r#"
|
||||||
|
/// {
|
||||||
|
/// [1] = 4,
|
||||||
|
/// [2] = 5,
|
||||||
|
/// [4] = 7,
|
||||||
|
/// key = 2
|
||||||
|
/// }
|
||||||
|
/// "#).eval()?;
|
||||||
///
|
///
|
||||||
/// let expected = [4, 5];
|
/// let expected = [4, 5];
|
||||||
/// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) {
|
/// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) {
|
||||||
|
@ -319,9 +313,6 @@ impl<'lua> Table<'lua> {
|
||||||
/// }
|
/// }
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`pairs`]: #method.pairs
|
/// [`pairs`]: #method.pairs
|
||||||
|
@ -364,7 +355,7 @@ where
|
||||||
assert_stack(lua.state, 6);
|
assert_stack(lua.state, 6);
|
||||||
|
|
||||||
lua.push_ref(&self.table);
|
lua.push_ref(&self.table);
|
||||||
lua.push_value(next_key);
|
lua.push_value(next_key)?;
|
||||||
|
|
||||||
if protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
if protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
|
||||||
ffi::lua_next(state, -2) != 0
|
ffi::lua_next(state, -2) != 0
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use ffi;
|
use crate::ffi;
|
||||||
use types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
|
use crate::util::{
|
||||||
use value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
assert_stack, check_stack, error_traceback, pop_error, protect_lua_closure, StackGuard,
|
||||||
|
};
|
||||||
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
|
||||||
|
|
||||||
/// Status of a Lua thread (or coroutine).
|
/// Status of a Lua thread (or coroutine).
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
@ -44,9 +46,8 @@ impl<'lua> Thread<'lua> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
/// # use rlua::{Lua, Thread, Error};
|
||||||
/// # use rlua::{Lua, Thread, Error, Result};
|
/// # fn main() -> Result<()> {
|
||||||
/// # fn try_main() -> Result<()> {
|
|
||||||
/// let lua = Lua::new();
|
/// let lua = Lua::new();
|
||||||
/// let thread: Thread = lua.eval(r#"
|
/// let thread: Thread = lua.eval(r#"
|
||||||
/// coroutine.create(function(arg)
|
/// coroutine.create(function(arg)
|
||||||
|
@ -65,10 +66,6 @@ impl<'lua> Thread<'lua> {
|
||||||
/// Err(Error::CoroutineInactive) => {},
|
/// Err(Error::CoroutineInactive) => {},
|
||||||
/// unexpected => panic!("unexpected result {:?}", unexpected),
|
/// unexpected => panic!("unexpected result {:?}", unexpected),
|
||||||
/// }
|
/// }
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn resume<A, R>(&self, args: A) -> Result<R>
|
pub fn resume<A, R>(&self, args: A) -> Result<R>
|
||||||
|
@ -80,7 +77,7 @@ impl<'lua> Thread<'lua> {
|
||||||
let args = args.to_lua_multi(lua)?;
|
let args = args.to_lua_multi(lua)?;
|
||||||
let results = unsafe {
|
let results = unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 1);
|
assert_stack(lua.state, 3);
|
||||||
|
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
let thread_state = ffi::lua_tothread(lua.state, -1);
|
let thread_state = ffi::lua_tothread(lua.state, -1);
|
||||||
|
@ -97,13 +94,16 @@ impl<'lua> Thread<'lua> {
|
||||||
check_stack(thread_state, nargs + 1)?;
|
check_stack(thread_state, nargs + 1)?;
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
lua.push_value(arg);
|
lua.push_value(arg)?;
|
||||||
}
|
}
|
||||||
ffi::lua_xmove(lua.state, thread_state, nargs);
|
ffi::lua_xmove(lua.state, thread_state, nargs);
|
||||||
|
|
||||||
let ret = ffi::lua_resume(thread_state, lua.state, nargs);
|
let ret = ffi::lua_resume(thread_state, lua.state, nargs);
|
||||||
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
|
||||||
error_traceback(thread_state);
|
protect_lua_closure(lua.state, 0, 0, |_| {
|
||||||
|
error_traceback(thread_state);
|
||||||
|
0
|
||||||
|
})?;
|
||||||
return Err(pop_error(thread_state, ret));
|
return Err(pop_error(thread_state, ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
src/types.rs
10
src/types.rs
|
@ -2,10 +2,10 @@ use std::os::raw::{c_int, c_void};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{fmt, mem, ptr};
|
use std::{fmt, mem, ptr};
|
||||||
|
|
||||||
use error::Result;
|
use crate::error::Result;
|
||||||
use ffi;
|
use crate::ffi;
|
||||||
use lua::Lua;
|
use crate::lua::Lua;
|
||||||
use value::MultiValue;
|
use crate::value::MultiValue;
|
||||||
|
|
||||||
/// Type of Lua integer numbers.
|
/// Type of Lua integer numbers.
|
||||||
pub type Integer = ffi::lua_Integer;
|
pub type Integer = ffi::lua_Integer;
|
||||||
|
@ -52,7 +52,7 @@ impl fmt::Debug for RegistryKey {
|
||||||
|
|
||||||
impl Drop for RegistryKey {
|
impl Drop for RegistryKey {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(list) = self.unref_list.lock().unwrap().as_mut() {
|
if let Some(list) = rlua_expect!(self.unref_list.lock(), "unref_list poisoned").as_mut() {
|
||||||
list.push(self.registry_id);
|
list.push(self.registry_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::cell::{Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use ffi;
|
use crate::ffi;
|
||||||
use lua::Lua;
|
use crate::lua::Lua;
|
||||||
use types::LuaRef;
|
use crate::types::LuaRef;
|
||||||
use util::{assert_stack, get_userdata, StackGuard};
|
use crate::util::{assert_stack, get_userdata, StackGuard};
|
||||||
use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||||
|
|
||||||
/// Kinds of metamethods that can be overridden.
|
/// Kinds of metamethods that can be overridden.
|
||||||
///
|
///
|
||||||
|
@ -66,31 +66,31 @@ pub enum MetaMethod {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaMethod {
|
impl MetaMethod {
|
||||||
pub(crate) fn name(self) -> &'static str {
|
pub(crate) fn name(self) -> &'static [u8] {
|
||||||
match self {
|
match self {
|
||||||
MetaMethod::Add => "__add",
|
MetaMethod::Add => b"__add",
|
||||||
MetaMethod::Sub => "__sub",
|
MetaMethod::Sub => b"__sub",
|
||||||
MetaMethod::Mul => "__mul",
|
MetaMethod::Mul => b"__mul",
|
||||||
MetaMethod::Div => "__div",
|
MetaMethod::Div => b"__div",
|
||||||
MetaMethod::Mod => "__mod",
|
MetaMethod::Mod => b"__mod",
|
||||||
MetaMethod::Pow => "__pow",
|
MetaMethod::Pow => b"__pow",
|
||||||
MetaMethod::Unm => "__unm",
|
MetaMethod::Unm => b"__unm",
|
||||||
MetaMethod::IDiv => "__idiv",
|
MetaMethod::IDiv => b"__idiv",
|
||||||
MetaMethod::BAnd => "__band",
|
MetaMethod::BAnd => b"__band",
|
||||||
MetaMethod::BOr => "__bor",
|
MetaMethod::BOr => b"__bor",
|
||||||
MetaMethod::BXor => "__bxor",
|
MetaMethod::BXor => b"__bxor",
|
||||||
MetaMethod::BNot => "__bnot",
|
MetaMethod::BNot => b"__bnot",
|
||||||
MetaMethod::Shl => "__shl",
|
MetaMethod::Shl => b"__shl",
|
||||||
MetaMethod::Shr => "__shr",
|
MetaMethod::Shr => b"__shr",
|
||||||
MetaMethod::Concat => "__concat",
|
MetaMethod::Concat => b"__concat",
|
||||||
MetaMethod::Len => "__len",
|
MetaMethod::Len => b"__len",
|
||||||
MetaMethod::Eq => "__eq",
|
MetaMethod::Eq => b"__eq",
|
||||||
MetaMethod::Lt => "__lt",
|
MetaMethod::Lt => b"__lt",
|
||||||
MetaMethod::Le => "__le",
|
MetaMethod::Le => b"__le",
|
||||||
MetaMethod::Index => "__index",
|
MetaMethod::Index => b"__index",
|
||||||
MetaMethod::NewIndex => "__newindex",
|
MetaMethod::NewIndex => b"__newindex",
|
||||||
MetaMethod::Call => "__call",
|
MetaMethod::Call => b"__call",
|
||||||
MetaMethod::ToString => "__tostring",
|
MetaMethod::ToString => b"__tostring",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,9 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||||
///
|
///
|
||||||
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
||||||
/// be used as a fall-back if no regular method is found.
|
/// be used as a fall-back if no regular method is found.
|
||||||
fn add_method<A, R, M>(&mut self, name: &str, method: M)
|
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>;
|
M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result<R>;
|
||||||
|
@ -117,33 +118,37 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||||
/// Refer to [`add_method`] for more information about the implementation.
|
/// Refer to [`add_method`] for more information about the implementation.
|
||||||
///
|
///
|
||||||
/// [`add_method`]: #method.add_method
|
/// [`add_method`]: #method.add_method
|
||||||
fn add_method_mut<A, R, M>(&mut self, name: &str, method: M)
|
fn add_method_mut<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
|
||||||
|
|
||||||
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
/// Add a regular method as a function which accepts generic arguments, the first argument will
|
||||||
/// always be a `UserData` of type T.
|
/// be a `UserData` of type T if the method is called with Lua method syntax:
|
||||||
|
/// `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first argument:
|
||||||
|
/// `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
||||||
///
|
///
|
||||||
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
/// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use.
|
||||||
///
|
///
|
||||||
/// [`add_method`]: #method.add_method
|
/// [`add_method`]: #method.add_method
|
||||||
/// [`add_method_mut`]: #method.add_method_mut
|
/// [`add_method_mut`]: #method.add_method_mut
|
||||||
fn add_function<A, R, F>(&mut self, name: &str, function: F)
|
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>;
|
F: 'static + Send + Fn(&'lua Lua, A) -> Result<R>;
|
||||||
|
|
||||||
/// Add a regular method as a mutable function which accepts generic arguments, the first
|
/// Add a regular method as a mutable function which accepts generic arguments.
|
||||||
/// argument will always be a `UserData` of type T.
|
|
||||||
///
|
///
|
||||||
/// This is a version of [`add_function`] that accepts a FnMut argument.
|
/// This is a version of [`add_function`] that accepts a FnMut argument.
|
||||||
///
|
///
|
||||||
/// [`add_function`]: #method.add_function
|
/// [`add_function`]: #method.add_function
|
||||||
fn add_function_mut<A, R, F>(&mut self, name: &str, function: F)
|
fn add_function_mut<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
where
|
where
|
||||||
|
S: ?Sized + AsRef<[u8]>,
|
||||||
A: FromLuaMulti<'lua>,
|
A: FromLuaMulti<'lua>,
|
||||||
R: ToLuaMulti<'lua>,
|
R: ToLuaMulti<'lua>,
|
||||||
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>;
|
F: 'static + Send + FnMut(&'lua Lua, A) -> Result<R>;
|
||||||
|
@ -207,9 +212,8 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, UserData, Result};
|
/// # use rlua::{Lua, UserData, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// struct MyUserData(i32);
|
/// struct MyUserData(i32);
|
||||||
///
|
///
|
||||||
/// impl UserData for MyUserData {}
|
/// impl UserData for MyUserData {}
|
||||||
|
@ -222,18 +226,14 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||||
/// lua.exec::<_, ()>("assert(type(myobject) == 'userdata')", None)?;
|
/// lua.exec::<_, ()>("assert(type(myobject) == 'userdata')", None)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
|
/// Custom methods and operators can be provided by implementing `add_methods` (refer to
|
||||||
/// [`UserDataMethods`] for more information):
|
/// [`UserDataMethods`] for more information):
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # extern crate rlua;
|
|
||||||
/// # use rlua::{Lua, MetaMethod, UserData, UserDataMethods, Result};
|
/// # use rlua::{Lua, MetaMethod, UserData, UserDataMethods, Result};
|
||||||
/// # fn try_main() -> Result<()> {
|
/// # fn main() -> Result<()> {
|
||||||
/// struct MyUserData(i32);
|
/// struct MyUserData(i32);
|
||||||
///
|
///
|
||||||
/// impl UserData for MyUserData {
|
/// impl UserData for MyUserData {
|
||||||
|
@ -265,9 +265,6 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
||||||
/// "#, None)?;
|
/// "#, None)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// # fn main() {
|
|
||||||
/// # try_main().unwrap();
|
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`ToLua`]: trait.ToLua.html
|
/// [`ToLua`]: trait.ToLua.html
|
||||||
|
@ -343,7 +340,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 2);
|
assert_stack(lua.state, 2);
|
||||||
lua.push_ref(&self.0);
|
lua.push_ref(&self.0);
|
||||||
lua.push_value(v);
|
lua.push_value(v)?;
|
||||||
ffi::lua_setuservalue(lua.state, -2);
|
ffi::lua_setuservalue(lua.state, -2);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
284
src/util.rs
284
src/util.rs
|
@ -1,12 +1,13 @@
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::ffi::CStr;
|
use std::borrow::Cow;
|
||||||
|
use std::fmt::Write;
|
||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr, slice};
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use ffi;
|
use crate::ffi;
|
||||||
|
|
||||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||||
// panic with an internal error message.
|
// panic with an internal error message.
|
||||||
|
@ -20,7 +21,7 @@ pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similar to `assert_stack`, but returns `Error::StackError` on failure.
|
// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
|
||||||
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
|
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
|
||||||
if ffi::lua_checkstack(state, amount) == 0 {
|
if ffi::lua_checkstack(state, amount) == 0 {
|
||||||
Err(Error::StackError)
|
Err(Error::StackError)
|
||||||
|
@ -37,7 +38,7 @@ pub struct StackGuard {
|
||||||
impl StackGuard {
|
impl StackGuard {
|
||||||
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
|
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
|
||||||
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
|
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
|
||||||
// the beginning, this is considered a fatal logic error and will result in an abort.
|
// the beginning, this is considered a fatal logic error and will result in a panic.
|
||||||
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
|
||||||
StackGuard {
|
StackGuard {
|
||||||
state,
|
state,
|
||||||
|
@ -179,16 +180,10 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||||
if let Some(p) = (*panic).0.take() {
|
if let Some(p) = (*panic).0.take() {
|
||||||
resume_unwind(p);
|
resume_unwind(p);
|
||||||
} else {
|
} else {
|
||||||
rlua_panic!("panic was resumed twice")
|
rlua_panic!("error during panic handling, panic was resumed twice")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let err_string = gc_guard(state, || {
|
let err_string = to_string(state, -1).into_owned();
|
||||||
if let Some(s) = ffi::lua_tostring(state, -1).as_ref() {
|
|
||||||
CStr::from_ptr(s).to_string_lossy().into_owned()
|
|
||||||
} else {
|
|
||||||
"<unprintable error>".to_owned()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
|
|
||||||
match err_code {
|
match err_code {
|
||||||
|
@ -202,17 +197,13 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ffi::LUA_ERRERR => {
|
ffi::LUA_ERRERR => {
|
||||||
// The Lua manual documents this error wrongly: It is not raised when a message
|
// This error is raised when the error handler raises an error too many times
|
||||||
// handler errors, but rather when some specific situations regarding stack
|
// recursively, and continuing to trigger the error handler would cause a stack
|
||||||
// overflow handling occurs. Since it is not very useful do differentiate
|
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||||
// between that and "ordinary" runtime errors, we handle them the same way.
|
// runtime errors, so we handle them the same way.
|
||||||
Error::RuntimeError(err_string)
|
Error::RuntimeError(err_string)
|
||||||
}
|
}
|
||||||
ffi::LUA_ERRMEM => {
|
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||||
// This should be impossible, as we set the lua allocator to one that aborts
|
|
||||||
// instead of failing.
|
|
||||||
rlua_abort!("impossible Lua allocation error")
|
|
||||||
}
|
|
||||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||||
_ => rlua_panic!("unrecognized lua error code"),
|
_ => rlua_panic!("unrecognized lua error code"),
|
||||||
}
|
}
|
||||||
|
@ -246,7 +237,9 @@ pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
|
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
|
||||||
// userdata.
|
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
|
||||||
|
// previously invalidated, and this method does not check for this. Uses 1 extra stack space and
|
||||||
|
// does not call checkstack
|
||||||
pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
|
||||||
// We set the metatable of userdata on __gc to a special table with no __gc method and with
|
// We set the metatable of userdata on __gc to a special table with no __gc method and with
|
||||||
// metamethods that trigger an error on access. We do this so that it will not be double
|
// metamethods that trigger an error on access. We do this so that it will not be double
|
||||||
|
@ -336,7 +329,8 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||||
callback_error(state, || {
|
callback_error(state, |_| {
|
||||||
|
check_stack(state, 1)?;
|
||||||
take_userdata::<T>(state);
|
take_userdata::<T>(state);
|
||||||
Ok(0)
|
Ok(0)
|
||||||
})
|
})
|
||||||
|
@ -346,24 +340,54 @@ pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c
|
||||||
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
||||||
// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
|
// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
|
||||||
// the rust side, it will resume the panic.
|
// the rust side, it will resume the panic.
|
||||||
|
//
|
||||||
|
// This function assumes the structure of the stack at the beginning of a callback, that the only
|
||||||
|
// elements on the stack are the arguments to the callback.
|
||||||
|
//
|
||||||
|
// This function uses some of the bottom of the stack for error handling, the given callback will be
|
||||||
|
// given the number of arguments available as an argument, and should return the number of returns
|
||||||
|
// as normal, but cannot assume that the arguments available start at 0.
|
||||||
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce() -> Result<R>,
|
F: FnOnce(c_int) -> Result<R>,
|
||||||
{
|
{
|
||||||
match catch_unwind(AssertUnwindSafe(f)) {
|
let nargs = ffi::lua_gettop(state);
|
||||||
Ok(Ok(r)) => r,
|
|
||||||
|
// We need one extra stack space to store preallocated memory, and at least 3 stack spaces
|
||||||
|
// overall for handling error metatables
|
||||||
|
let extra_stack = if nargs < 3 { 3 - nargs } else { 1 };
|
||||||
|
ffi::luaL_checkstack(
|
||||||
|
state,
|
||||||
|
extra_stack,
|
||||||
|
cstr!("not enough stack space for callback error handling"),
|
||||||
|
);
|
||||||
|
|
||||||
|
// We cannot shadow rust errors with Lua ones, we pre-allocate enough memory to store a wrapped
|
||||||
|
// error or panic *before* we proceed.
|
||||||
|
let ud = ffi::lua_newuserdata(
|
||||||
|
state,
|
||||||
|
mem::size_of::<WrappedError>().max(mem::size_of::<WrappedPanic>()),
|
||||||
|
);
|
||||||
|
ffi::lua_rotate(state, 1, 1);
|
||||||
|
|
||||||
|
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||||
|
Ok(Ok(r)) => {
|
||||||
|
ffi::lua_rotate(state, 1, -1);
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
r
|
||||||
|
}
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
ffi::lua_settop(state, 0);
|
ffi::lua_settop(state, 1);
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
ptr::write(ud as *mut WrappedError, WrappedError(err));
|
||||||
push_wrapped_error(state, err);
|
get_error_metatable(state);
|
||||||
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
Err(p) => {
|
Err(p) => {
|
||||||
ffi::lua_settop(state, 0);
|
ffi::lua_settop(state, 1);
|
||||||
if ffi::lua_checkstack(state, 2) == 0 {
|
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
||||||
rlua_abort!("not enough stack space to propagate panic");
|
get_panic_metatable(state);
|
||||||
}
|
ffi::lua_setmetatable(state, -2);
|
||||||
push_wrapped_panic(state, p);
|
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,50 +395,48 @@ where
|
||||||
|
|
||||||
// Takes an error at the top of the stack, and if it is a WrappedError, converts it to an
|
// Takes an error at the top of the stack, and if it is a WrappedError, converts it to an
|
||||||
// Error::CallbackError with a traceback, if it is some lua type, prints the error along with a
|
// Error::CallbackError with a traceback, if it is some lua type, prints the error along with a
|
||||||
// traceback, and if it is a WrappedPanic, does not modify it. This function should never panic or
|
// traceback, and if it is a WrappedPanic, does not modify it. This function does its best to avoid
|
||||||
// trigger a error (longjmp).
|
// triggering another error and shadowing previous rust errors, but it may trigger Lua errors that
|
||||||
|
// shadow rust errors under certain memory conditions. This function ensures that such behavior
|
||||||
|
// will *never* occur with a rust panic, however.
|
||||||
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||||
// I believe luaL_traceback requires this much free stack to not error.
|
// I believe luaL_traceback requires this much free stack to not error.
|
||||||
const LUA_TRACEBACK_STACK: c_int = 11;
|
const LUA_TRACEBACK_STACK: c_int = 11;
|
||||||
|
|
||||||
if ffi::lua_checkstack(state, 2) == 0 {
|
if ffi::lua_checkstack(state, 2) == 0 {
|
||||||
// If we don't have enough stack space to even check the error type, do nothing
|
// If we don't have enough stack space to even check the error type, do nothing so we don't
|
||||||
} else if let Some(error) = get_wrapped_error(state, 1).as_ref() {
|
// risk shadowing a rust panic.
|
||||||
|
} else if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
||||||
|
// lua_newuserdata and luaL_traceback may error, but nothing that implements Drop should be
|
||||||
|
// on the rust stack at this time.
|
||||||
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
||||||
let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
|
let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
|
||||||
gc_guard(state, || {
|
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
|
||||||
});
|
let traceback = to_string(state, -1).into_owned();
|
||||||
let traceback = CStr::from_ptr(ffi::lua_tostring(state, -1))
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
traceback
|
traceback
|
||||||
} else {
|
} else {
|
||||||
"not enough stack space for traceback".to_owned()
|
"<not enough stack space for traceback>".to_owned()
|
||||||
};
|
};
|
||||||
|
|
||||||
let error = error.clone();
|
let error = error.clone();
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_remove(state, -2);
|
||||||
|
|
||||||
push_wrapped_error(
|
ptr::write(
|
||||||
state,
|
ud,
|
||||||
Error::CallbackError {
|
WrappedError(Error::CallbackError {
|
||||||
traceback,
|
traceback,
|
||||||
cause: Arc::new(error),
|
cause: Arc::new(error),
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
} else if !is_wrapped_panic(state, 1) {
|
get_error_metatable(state);
|
||||||
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
} else if !is_wrapped_panic(state, -1) {
|
||||||
if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
|
if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
|
||||||
gc_guard(state, || {
|
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||||
let s = ffi::lua_tostring(state, 1);
|
ffi::luaL_traceback(state, state, s, 0);
|
||||||
let s = if s.is_null() {
|
ffi::lua_remove(state, -2);
|
||||||
cstr!("<unprintable lua error>")
|
|
||||||
} else {
|
|
||||||
s
|
|
||||||
};
|
|
||||||
ffi::luaL_traceback(state, state, s, 0);
|
|
||||||
ffi::lua_remove(state, -2);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
|
1
|
||||||
|
@ -493,16 +515,16 @@ pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
||||||
main_state
|
main_state
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes a WrappedError::Error to the top of the stack. Uses two stack spaces and does not call
|
// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
|
||||||
// lua_checkstack.
|
// lua_checkstack.
|
||||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
|
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
||||||
gc_guard(state, || {
|
let ud = protect_lua_closure(state, 0, 1, move |state| {
|
||||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError
|
||||||
ptr::write(ud, WrappedError(err))
|
})?;
|
||||||
});
|
ptr::write(ud, WrappedError(err));
|
||||||
|
|
||||||
get_error_metatable(state);
|
get_error_metatable(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
||||||
|
@ -528,49 +550,43 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs the given function with the Lua garbage collector disabled. `rlua` assumes that all
|
|
||||||
// allocation failures are aborts, so when the garbage collector is disabled, 'm' functions that can
|
|
||||||
// cause either an allocation error or a a `__gc` metamethod error are prevented from causing errors
|
|
||||||
// at all. The given function should never panic or longjmp, because this could inadverntently
|
|
||||||
// disable the gc. This is useful when error handling must allocate, and `__gc` errors at that time
|
|
||||||
// would shadow more important errors, or be extremely difficult to handle safely.
|
|
||||||
pub unsafe fn gc_guard<R, F: FnOnce() -> R>(state: *mut ffi::lua_State, f: F) -> R {
|
|
||||||
if ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0) != 0 {
|
|
||||||
ffi::lua_gc(state, ffi::LUA_GCSTOP, 0);
|
|
||||||
let r = f();
|
|
||||||
ffi::lua_gc(state, ffi::LUA_GCRESTART, 0);
|
|
||||||
r
|
|
||||||
} else {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the error, panic, and destructed userdata metatables.
|
// Initialize the error, panic, and destructed userdata metatables.
|
||||||
pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
|
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||||
assert_stack(state, 8);
|
assert_stack(state, 8);
|
||||||
|
|
||||||
// Create error metatable
|
// Create error metatable
|
||||||
|
|
||||||
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
let err_buf = callback_error(state, |_| {
|
||||||
|
check_stack(state, 3)?;
|
||||||
callback_error(state, || {
|
|
||||||
if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
||||||
let error_str = error.to_string();
|
ffi::lua_pushlightuserdata(
|
||||||
gc_guard(state, || {
|
state,
|
||||||
ffi::lua_pushlstring(
|
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
|
||||||
state,
|
);
|
||||||
error_str.as_ptr() as *const c_char,
|
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||||
error_str.len(),
|
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||||
)
|
ffi::lua_pop(state, 2);
|
||||||
});
|
|
||||||
ffi::lua_remove(state, -2);
|
|
||||||
|
|
||||||
Ok(1)
|
(*err_buf).clear();
|
||||||
|
// Depending on how the API is used and what error types scripts are given, it may
|
||||||
|
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||||
|
// kind of recursive error structure?)
|
||||||
|
let _ = write!(&mut (*err_buf), "{}", error);
|
||||||
|
Ok(err_buf)
|
||||||
} else {
|
} else {
|
||||||
panic!("userdata mismatch in Error metamethod");
|
// I'm not sure whether this is possible to trigger without bugs in rlua?
|
||||||
|
Err(Error::UserDataTypeMismatch)
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
ffi::lua_pushlstring(
|
||||||
|
state,
|
||||||
|
(*err_buf).as_ptr() as *const c_char,
|
||||||
|
(*err_buf).len(),
|
||||||
|
);
|
||||||
|
(*err_buf).clear();
|
||||||
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
|
@ -615,7 +631,11 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
|
||||||
|
|
||||||
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
push_wrapped_error(state, Error::CallbackDestructed);
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedError>()) as *mut WrappedError;
|
||||||
|
|
||||||
|
ptr::write(ud, WrappedError(Error::CallbackDestructed));
|
||||||
|
get_error_metatable(state);
|
||||||
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,21 +678,56 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
// Create error print buffer
|
||||||
|
|
||||||
|
ffi::lua_pushlightuserdata(state, &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void);
|
||||||
|
|
||||||
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<String>()) as *mut String;
|
||||||
|
ptr::write(ud, String::new());
|
||||||
|
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||||
|
ffi::lua_pushcfunction(state, userdata_destructor::<String>);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
|
||||||
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WrappedError(pub Error);
|
struct WrappedError(pub Error);
|
||||||
struct WrappedPanic(pub Option<Box<dyn Any + Send>>);
|
struct WrappedPanic(pub Option<Box<dyn Any + Send>>);
|
||||||
|
|
||||||
// Pushes a WrappedError::Panic to the top of the stack. Uses two stack spaces and does not call
|
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
||||||
// lua_checkstack.
|
// panicking.
|
||||||
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<dyn Any + Send>) {
|
unsafe fn to_string<'a>(state: *mut ffi::lua_State, index: c_int) -> Cow<'a, str> {
|
||||||
gc_guard(state, || {
|
match ffi::lua_type(state, index) {
|
||||||
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedPanic>()) as *mut WrappedPanic;
|
ffi::LUA_TNONE => "<none>".into(),
|
||||||
ptr::write(ud, WrappedPanic(Some(panic)))
|
ffi::LUA_TNIL => "<nil>".into(),
|
||||||
});
|
ffi::LUA_TBOOLEAN => (ffi::lua_toboolean(state, index) != 1).to_string().into(),
|
||||||
|
ffi::LUA_TLIGHTUSERDATA => {
|
||||||
get_panic_metatable(state);
|
format!("<lightuserdata {:?}>", ffi::lua_topointer(state, index)).into()
|
||||||
ffi::lua_setmetatable(state, -2);
|
}
|
||||||
|
ffi::LUA_TNUMBER => {
|
||||||
|
let mut isint = 0;
|
||||||
|
let i = ffi::lua_tointegerx(state, -1, &mut isint);
|
||||||
|
if isint == 0 {
|
||||||
|
ffi::lua_tonumber(state, index).to_string().into()
|
||||||
|
} else {
|
||||||
|
i.to_string().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ffi::LUA_TSTRING => {
|
||||||
|
let mut size = 0;
|
||||||
|
let data = ffi::lua_tolstring(state, index, &mut size);
|
||||||
|
String::from_utf8_lossy(slice::from_raw_parts(data as *const u8, size))
|
||||||
|
}
|
||||||
|
ffi::LUA_TTABLE => format!("<table {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||||
|
ffi::LUA_TFUNCTION => format!("<function {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||||
|
ffi::LUA_TUSERDATA => format!("<userdata {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||||
|
ffi::LUA_TTHREAD => format!("<thread {:?}>", ffi::lua_topointer(state, index)).into(),
|
||||||
|
_ => "<unknown>".into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
|
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
|
||||||
|
@ -720,3 +775,4 @@ unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
|
||||||
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
|
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
static PANIC_METATABLE_REGISTRY_KEY: u8 = 0;
|
static PANIC_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||||
|
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
||||||
|
|
26
src/value.rs
26
src/value.rs
|
@ -1,14 +1,14 @@
|
||||||
use std::iter::{self, FromIterator};
|
use std::iter::{self, FromIterator};
|
||||||
use std::{slice, str, vec};
|
use std::{slice, str, vec};
|
||||||
|
|
||||||
use error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use function::Function;
|
use crate::function::Function;
|
||||||
use lua::Lua;
|
use crate::lua::Lua;
|
||||||
use string::String;
|
use crate::string::String;
|
||||||
use table::Table;
|
use crate::table::Table;
|
||||||
use thread::Thread;
|
use crate::thread::Thread;
|
||||||
use types::{Integer, LightUserData, Number};
|
use crate::types::{Integer, LightUserData, Number};
|
||||||
use userdata::AnyUserData;
|
use crate::userdata::AnyUserData;
|
||||||
|
|
||||||
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
|
||||||
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
|
||||||
|
@ -86,6 +86,12 @@ impl<'lua> MultiValue<'lua> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'lua> Default for MultiValue<'lua> {
|
||||||
|
fn default() -> MultiValue<'lua> {
|
||||||
|
MultiValue::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
|
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
|
||||||
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
|
||||||
MultiValue::from_vec(Vec::from_iter(iter))
|
MultiValue::from_vec(Vec::from_iter(iter))
|
||||||
|
@ -138,6 +144,10 @@ impl<'lua> MultiValue<'lua> {
|
||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
|
||||||
self.0.iter().rev()
|
self.0.iter().rev()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue