mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de1cfa070f | |||
| ec1fa04085 | |||
| 714dd6249f | |||
| 5089dd73c0 | |||
| 9533f08d3a | |||
| 0a3b65af88 | |||
| 3a9c8c2da2 |
@@ -1,3 +1,9 @@
|
||||
## v0.8.0-beta.2
|
||||
|
||||
- Luau vector datatype support
|
||||
- Luau readonly table attribute
|
||||
- Other Luau improvements
|
||||
|
||||
## v0.8.0-beta.1
|
||||
|
||||
- Roblox Luau support
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.8.0-beta.1" # remember to update html_root_url and mlua_derive
|
||||
version = "0.8.0-beta.2" # remember to update html_root_url and mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2018"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -59,7 +59,7 @@ cc = { version = "1.0" }
|
||||
pkg-config = { version = "0.3.17" }
|
||||
lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.2.0", optional = true }
|
||||
luau0-src = { version = "0.2.1", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "9.0"
|
||||
|
||||
@@ -104,7 +104,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.8.0-beta.1", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.8.0-beta.2", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -139,7 +139,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.8.0-beta.1", features = ["lua54", "vendored", "module"] }
|
||||
mlua = { version = "0.8.0-beta.2", features = ["lua54", "vendored", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
|
||||
+10
-9
@@ -64,7 +64,8 @@ pub enum ChunkMode {
|
||||
}
|
||||
|
||||
/// Luau compiler
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Compiler {
|
||||
optimization_level: u8,
|
||||
@@ -72,7 +73,7 @@ pub struct Compiler {
|
||||
coverage_level: u8,
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl Default for Compiler {
|
||||
fn default() -> Self {
|
||||
// Defaults are taken from luacode.h
|
||||
@@ -84,7 +85,7 @@ impl Default for Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
impl Compiler {
|
||||
/// Creates Luau compiler instance with default options
|
||||
pub fn new() -> Self {
|
||||
@@ -97,7 +98,6 @@ impl Compiler {
|
||||
/// 0 - no optimization
|
||||
/// 1 - baseline optimization level that doesn't prevent debuggability (default)
|
||||
/// 2 - includes optimizations that harm debuggability such as inlining
|
||||
#[cfg(feature = "luau")]
|
||||
pub fn set_optimization_level(mut self, level: u8) -> Self {
|
||||
self.optimization_level = level;
|
||||
self
|
||||
@@ -109,7 +109,6 @@ impl Compiler {
|
||||
/// 0 - no debugging support
|
||||
/// 1 - line info & function names only; sufficient for backtraces (default)
|
||||
/// 2 - full debug info with local & upvalue names; necessary for debugger
|
||||
#[cfg(feature = "luau")]
|
||||
pub fn set_debug_level(mut self, level: u8) -> Self {
|
||||
self.debug_level = level;
|
||||
self
|
||||
@@ -121,7 +120,6 @@ impl Compiler {
|
||||
/// 0 - no code coverage support (default)
|
||||
/// 1 - statement coverage
|
||||
/// 2 - statement and expression coverage (verbose)
|
||||
#[cfg(feature = "luau")]
|
||||
pub fn set_coverage_level(mut self, level: u8) -> Self {
|
||||
self.coverage_level = level;
|
||||
self
|
||||
@@ -190,7 +188,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// See [`Compiler::set_optimization_level`] for details.
|
||||
///
|
||||
/// Requires `feature = "luau`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_optimization_level(mut self, level: u8) -> Self {
|
||||
self.compiler
|
||||
.get_or_insert_with(Default::default)
|
||||
@@ -203,7 +202,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// See [`Compiler::set_debug_level`] for details.
|
||||
///
|
||||
/// Requires `feature = "luau`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_debug_level(mut self, level: u8) -> Self {
|
||||
self.compiler
|
||||
.get_or_insert_with(Default::default)
|
||||
@@ -216,7 +216,8 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// See [`Compiler::set_coverage_level`] for details.
|
||||
///
|
||||
/// Requires `feature = "luau`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_coverage_level(mut self, level: u8) -> Self {
|
||||
self.compiler
|
||||
.get_or_insert_with(Default::default)
|
||||
|
||||
+37
-28
@@ -464,21 +464,33 @@ impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
|
||||
where
|
||||
T: FromLua<'lua>,
|
||||
{
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "Table",
|
||||
to: "Array",
|
||||
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
|
||||
})
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) if N == 3 => Ok(mlua_expect!(
|
||||
vec![
|
||||
T::from_lua(Value::Number(x as _), _lua)?,
|
||||
T::from_lua(Value::Number(y as _), _lua)?,
|
||||
T::from_lua(Value::Number(z as _), _lua)?,
|
||||
]
|
||||
.try_into()
|
||||
.map_err(|_| ()),
|
||||
"cannot convert vector to array"
|
||||
)),
|
||||
Value::Table(table) => {
|
||||
let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
|
||||
vec.try_into()
|
||||
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
|
||||
from: "Table",
|
||||
to: "Array",
|
||||
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
|
||||
})
|
||||
}
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Array",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,16 +502,8 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.sequence_values().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Box<[T]>",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}
|
||||
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
|
||||
Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,15 +514,20 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||
if let Value::Table(table) = value {
|
||||
table.sequence_values().collect()
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
|
||||
match value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => Ok(vec![
|
||||
T::from_lua(Value::Number(x as _), _lua)?,
|
||||
T::from_lua(Value::Number(y as _), _lua)?,
|
||||
T::from_lua(Value::Number(z as _), _lua)?,
|
||||
]),
|
||||
Value::Table(table) => table.sequence_values().collect(),
|
||||
_ => Err(Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Vec",
|
||||
message: Some("expected table".to_string()),
|
||||
})
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -37,7 +37,8 @@ pub enum Error {
|
||||
/// Lua garbage collector error, aka `LUA_ERRGCMM`.
|
||||
///
|
||||
/// The Lua VM returns this error when there is an error running a `__gc` metamethod.
|
||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||
#[cfg(any(feature = "lua53", feature = "lua52", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52"))))]
|
||||
GarbageCollectorError(StdString),
|
||||
/// Potentially unsafe action in safe mode.
|
||||
SafetyError(StdString),
|
||||
|
||||
@@ -214,6 +214,7 @@ impl<'lua> Function<'lua> {
|
||||
/// If `strip` is true, the binary representation may not include all debug information
|
||||
/// about the function, to save space.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn dump(&self, strip: bool) -> Vec<u8> {
|
||||
use std::os::raw::c_void;
|
||||
use std::slice;
|
||||
|
||||
@@ -48,6 +48,7 @@ impl<'lua> Debug<'lua> {
|
||||
///
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn event(&self) -> DebugEvent {
|
||||
unsafe {
|
||||
match (*self.ar.get()).event {
|
||||
@@ -131,6 +132,7 @@ impl<'lua> Debug<'lua> {
|
||||
/// Corresponds to the `t` what mask. Returns true if the hook is in a function tail call, false
|
||||
/// otherwise.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn is_tail_call(&self) -> bool {
|
||||
unsafe {
|
||||
mlua_assert!(
|
||||
@@ -241,6 +243,7 @@ pub struct DebugStack {
|
||||
|
||||
/// Determines when a hook function will be called by Lua.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct HookTriggers {
|
||||
/// Before a function call.
|
||||
|
||||
+3
-2
@@ -72,7 +72,7 @@
|
||||
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
|
||||
|
||||
// mlua types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.8.0-beta.1")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.8.0-beta.2")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -124,7 +124,8 @@ pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub use crate::hook::HookTriggers;
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub use crate::chunk::Compiler;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
|
||||
+28
-10
@@ -152,8 +152,8 @@ pub struct LuaOptions {
|
||||
|
||||
/// Max size of thread (coroutine) object cache used to execute asynchronous functions.
|
||||
///
|
||||
/// It works only on Lua 5.4 or LuaJIT (vendored) with [`lua_resetthread`] function,
|
||||
/// and allows to reuse old coroutines with reset state.
|
||||
/// It works on Lua 5.4, LuaJIT (vendored) and Luau, where [`lua_resetthread`] function
|
||||
/// is available and allows to reuse old coroutines with reset state.
|
||||
///
|
||||
/// Default: **0** (disabled)
|
||||
///
|
||||
@@ -408,11 +408,6 @@ impl Lua {
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
let state = ffi::luaL_newstate();
|
||||
|
||||
// #[cfg(feature = "luau")]
|
||||
// {
|
||||
// ffi::luaL_sandbox(state);
|
||||
// }
|
||||
|
||||
ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
@@ -815,6 +810,7 @@ impl Lua {
|
||||
/// [`HookTriggers`]: crate::HookTriggers
|
||||
/// [`HookTriggers.every_nth_instruction`]: crate::HookTriggers::every_nth_instruction
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
|
||||
where
|
||||
F: 'static + MaybeSend + FnMut(&Lua, Debug) -> Result<()>,
|
||||
@@ -853,6 +849,7 @@ impl Lua {
|
||||
/// Remove any hook previously set by `set_hook`. This function has no effect if a hook was not
|
||||
/// previously set.
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn remove_hook(&self) {
|
||||
// If main_state is not available, then sethook wasn't called.
|
||||
let state = match self.main_state {
|
||||
@@ -987,7 +984,7 @@ impl Lua {
|
||||
|
||||
/// Returns true if the garbage collector is currently running automatically.
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
@@ -1051,6 +1048,7 @@ impl Lua {
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn gc_set_pause(&self, pause: c_int) -> c_int {
|
||||
let state = self.main_state.unwrap_or(self.state);
|
||||
unsafe { ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause) }
|
||||
@@ -1074,6 +1072,7 @@ impl Lua {
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5.1
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub fn gc_inc(&self, pause: c_int, step_multiplier: c_int, step_size: c_int) -> GCMode {
|
||||
let state = self.main_state.unwrap_or(self.state);
|
||||
|
||||
@@ -1972,6 +1971,11 @@ impl Lua {
|
||||
ffi::lua_pushnumber(self.state, n);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => {
|
||||
ffi::lua_pushvector(self.state, x, y, z);
|
||||
}
|
||||
|
||||
Value::String(s) => {
|
||||
self.push_ref(&s.0);
|
||||
}
|
||||
@@ -2033,6 +2037,15 @@ impl Lua {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TVECTOR => {
|
||||
let v = ffi::lua_tovector(state, -1);
|
||||
mlua_debug_assert!(!v.is_null(), "vector is null");
|
||||
let vec = Value::Vector(*v, *v.add(1), *v.add(2));
|
||||
ffi::lua_pop(state, 1);
|
||||
vec
|
||||
}
|
||||
|
||||
ffi::LUA_TSTRING => Value::String(String(self.pop_ref())),
|
||||
|
||||
ffi::LUA_TTABLE => Value::Table(Table(self.pop_ref())),
|
||||
@@ -2081,8 +2094,13 @@ impl Lua {
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
let extra = &*self.extra.get();
|
||||
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||
ffi::lua_xmove(extra.ref_thread, self.state, 1);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||
ffi::lua_xmove(extra.ref_thread, self.state, 1);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_xpush(extra.ref_thread, self.state, lref.index);
|
||||
}
|
||||
|
||||
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
||||
|
||||
@@ -123,6 +123,8 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => visitor.visit_f64(n.into()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_, _, _) => self.deserialize_seq(visitor),
|
||||
Value::String(s) => match s.to_str() {
|
||||
Ok(s) => visitor.visit_str(s),
|
||||
Err(_) => visitor.visit_bytes(s.as_bytes()),
|
||||
@@ -214,6 +216,16 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => {
|
||||
let mut deserializer = VecDeserializer {
|
||||
vec: [x, y, z],
|
||||
next: 0,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
visitor.visit_seq(&mut deserializer)
|
||||
}
|
||||
Value::Table(t) => {
|
||||
let _guard = RecursionGuard::new(&t, &self.visited);
|
||||
|
||||
@@ -352,6 +364,39 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
struct VecDeserializer {
|
||||
vec: [f32; 3],
|
||||
next: usize,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<FxHashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
impl<'de> de::SeqAccess<'de> for VecDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.vec.get(self.next) {
|
||||
Some(&n) => {
|
||||
self.next += 1;
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let deserializer =
|
||||
Deserializer::from_parts(Value::Number(n as _), self.options, visited);
|
||||
seed.deserialize(deserializer).map(Some)
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
Some(3)
|
||||
}
|
||||
}
|
||||
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub trait LuaSerdeExt<'lua> {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
|
||||
+5
-3
@@ -8,7 +8,7 @@ pub struct StdLib(u32);
|
||||
impl StdLib {
|
||||
/// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53/lua52"`
|
||||
/// Requires `feature = "lua54/lua53/lua52/luau"`
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
feature = "lua53",
|
||||
@@ -20,6 +20,7 @@ impl StdLib {
|
||||
pub const TABLE: StdLib = StdLib(1 << 1);
|
||||
/// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const IO: StdLib = StdLib(1 << 2);
|
||||
/// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library
|
||||
pub const OS: StdLib = StdLib(1 << 3);
|
||||
@@ -27,18 +28,19 @@ impl StdLib {
|
||||
pub const STRING: StdLib = StdLib(1 << 4);
|
||||
/// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library
|
||||
///
|
||||
/// Requires `feature = "lua54/lua53"`
|
||||
/// Requires `feature = "lua54/lua53/luau"`
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
||||
pub const UTF8: StdLib = StdLib(1 << 5);
|
||||
/// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library
|
||||
///
|
||||
/// Requires `feature = "lua52/luajit"`
|
||||
/// Requires `feature = "lua52/luajit/luau"`
|
||||
#[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))]
|
||||
pub const BIT: StdLib = StdLib(1 << 6);
|
||||
/// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library
|
||||
pub const MATH: StdLib = StdLib(1 << 7);
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
#[cfg(not(feature = "luau"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
|
||||
@@ -348,6 +348,28 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets `readonly` attribute on the table.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn set_readonly(&self, enabled: bool) {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
lua.ref_thread_exec(|refthr| ffi::lua_setreadonly(refthr, self.0.index, enabled as _));
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `readonly` attribute of the table.
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
#[cfg(feature = "luau")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn is_readonly(&self) -> bool {
|
||||
let lua = self.0.lua;
|
||||
unsafe { lua.ref_thread_exec(|refthr| ffi::lua_getreadonly(refthr, self.0.index) != 0) }
|
||||
}
|
||||
|
||||
/// Consume this table and return an iterator over the pairs of the table.
|
||||
///
|
||||
/// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
|
||||
|
||||
+12
-4
@@ -7,7 +7,11 @@ use crate::types::LuaRef;
|
||||
use crate::util::{check_stack, error_traceback, pop_error, StackGuard};
|
||||
use crate::value::{FromLuaMulti, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
all(feature = "luajit", feature = "vendored"),
|
||||
feature = "luau",
|
||||
))]
|
||||
use crate::function::Function;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
@@ -173,16 +177,20 @@ impl<'lua> Thread<'lua> {
|
||||
/// Returns a error in case of either the original error that stopped the thread or errors
|
||||
/// in closing methods.
|
||||
///
|
||||
/// In [LuaJIT]: resets to the initial state of a newly created Lua thread.
|
||||
/// In [LuaJIT] and Luau: resets to the initial state of a newly created Lua thread.
|
||||
/// Lua threads in arbitrary states (like yielded or errored) can be reset properly.
|
||||
///
|
||||
/// Sets a Lua function for the thread afterwards.
|
||||
///
|
||||
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"`
|
||||
/// Requires `feature = "lua54"` OR `feature = "luajit,vendored"` OR `feature = "luau"`
|
||||
///
|
||||
/// [Lua 5.4]: https://www.lua.org/manual/5.4/manual.html#lua_resetthread
|
||||
/// [LuaJIT]: https://github.com/openresty/luajit2#lua_resetthread
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
all(feature = "luajit", feature = "vendored"),
|
||||
feature = "luau",
|
||||
))]
|
||||
pub fn reset(&self, func: Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
|
||||
@@ -865,7 +865,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
///
|
||||
/// [`get_user_value`]: #method.get_user_value
|
||||
/// [`set_nth_user_value`]: #method.set_nth_user_value
|
||||
// #[cfg(not(feature = "luau"))]
|
||||
#[inline]
|
||||
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
|
||||
self.set_nth_user_value(1, v)
|
||||
@@ -877,7 +876,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
///
|
||||
/// [`set_user_value`]: #method.set_user_value
|
||||
/// [`get_nth_user_value`]: #method.get_nth_user_value
|
||||
// #[cfg(not(feature = "luau"))]
|
||||
#[inline]
|
||||
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
|
||||
self.get_nth_user_value(1)
|
||||
@@ -893,7 +891,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// For other Lua versions this functionality is provided using a wrapping table.
|
||||
///
|
||||
/// [`get_nth_user_value`]: #method.get_nth_user_value
|
||||
// #[cfg(not(feature = "luau"))]
|
||||
pub fn set_nth_user_value<V: ToLua<'lua>>(&self, n: usize, v: V) -> Result<()> {
|
||||
if n < 1 || n > u16::MAX as usize {
|
||||
return Err(Error::RuntimeError(
|
||||
@@ -948,7 +945,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// For other Lua versions this functionality is provided using a wrapping table.
|
||||
///
|
||||
/// [`set_nth_user_value`]: #method.set_nth_user_value
|
||||
// #[cfg(not(feature = "luau"))]
|
||||
pub fn get_nth_user_value<V: FromLua<'lua>>(&self, n: usize) -> Result<V> {
|
||||
if n < 1 || n > u16::MAX as usize {
|
||||
return Err(Error::RuntimeError(
|
||||
@@ -990,7 +986,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// The value can be retrieved with [`get_named_user_value`].
|
||||
///
|
||||
/// [`get_named_user_value`]: #method.get_named_user_value
|
||||
// #[cfg(not(feature = "luau"))]
|
||||
pub fn set_named_user_value<S, V>(&self, name: &S, v: V) -> Result<()>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
@@ -1030,7 +1025,6 @@ impl<'lua> AnyUserData<'lua> {
|
||||
/// Returns an associated value by name set by [`set_named_user_value`].
|
||||
///
|
||||
/// [`set_named_user_value`]: #method.set_named_user_value
|
||||
// #[cfg(not(feature = "luau"))]
|
||||
pub fn get_named_user_value<S, V>(&self, name: &S) -> Result<V>
|
||||
where
|
||||
S: AsRef<[u8]> + ?Sized,
|
||||
|
||||
@@ -965,6 +965,13 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri
|
||||
i.to_string()
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::LUA_TVECTOR => {
|
||||
let v = ffi::lua_tovector(state, index);
|
||||
mlua_debug_assert!(!v.is_null(), "vector is null");
|
||||
let (x, y, z) = (*v, *v.add(1), *v.add(2));
|
||||
format!("vector({},{},{})", x, y, z)
|
||||
}
|
||||
ffi::LUA_TSTRING => {
|
||||
let mut size = 0;
|
||||
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
||||
|
||||
@@ -34,6 +34,10 @@ pub enum Value<'lua> {
|
||||
Integer(Integer),
|
||||
/// A floating point number.
|
||||
Number(Number),
|
||||
/// A Luau vector.
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
Vector(f32, f32, f32),
|
||||
/// An interned string, managed by Lua.
|
||||
///
|
||||
/// Unlike Rust strings, Lua strings may not be valid UTF-8.
|
||||
@@ -61,6 +65,8 @@ impl<'lua> Value<'lua> {
|
||||
Value::LightUserData(_) => "lightuserdata",
|
||||
Value::Integer(_) => "integer",
|
||||
Value::Number(_) => "number",
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(_, _, _) => "vector",
|
||||
Value::String(_) => "string",
|
||||
Value::Table(_) => "table",
|
||||
Value::Function(_) => "function",
|
||||
@@ -99,6 +105,8 @@ impl<'lua> PartialEq for Value<'lua> {
|
||||
(Value::Integer(a), Value::Number(b)) => *a as Number == *b,
|
||||
(Value::Number(a), Value::Integer(b)) => *a == *b as Number,
|
||||
(Value::Number(a), Value::Number(b)) => *a == *b,
|
||||
#[cfg(feature = "luau")]
|
||||
(Value::Vector(x1, y1, z1), Value::Vector(x2, y2, z2)) => (x1, y1, z1) == (x2, y2, z2),
|
||||
(Value::String(a), Value::String(b)) => a == b,
|
||||
(Value::Table(a), Value::Table(b)) => a == b,
|
||||
(Value::Function(a), Value::Function(b)) => a == b,
|
||||
@@ -130,6 +138,8 @@ impl<'lua> Serialize for Value<'lua> {
|
||||
.serialize_i64((*i).try_into().expect("cannot convert lua_Integer to i64")),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
Value::Number(n) => serializer.serialize_f64(*n),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => (x, y, z).serialize(serializer),
|
||||
Value::String(s) => s.serialize(serializer),
|
||||
Value::Table(t) => t.serialize(serializer),
|
||||
Value::UserData(ud) => ud.serialize(serializer),
|
||||
|
||||
+39
-1
@@ -3,7 +3,7 @@
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
use mlua::{Lua, Result};
|
||||
use mlua::{Error, Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
@@ -29,3 +29,41 @@ fn test_require() -> Result<()> {
|
||||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?,
|
||||
)?;
|
||||
|
||||
let v: [f32; 3] = lua
|
||||
.load("return vector(1, 2, 3) + vector(3, 2, 1)")
|
||||
.eval()?;
|
||||
assert_eq!(v, [4.0, 4.0, 4.0]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_readonly_table() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let t = lua.create_table()?;
|
||||
assert!(!t.is_readonly());
|
||||
t.set_readonly(true);
|
||||
assert!(t.is_readonly());
|
||||
|
||||
match t.set("key", "value") {
|
||||
Err(Error::RuntimeError(err)) if err.contains("Attempt to modify a readonly table") => {}
|
||||
r => panic!(
|
||||
"expected RuntimeError(...) with a specific message, got {:?}",
|
||||
r
|
||||
),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -144,6 +144,29 @@ fn test_serialize_failure() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[test]
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z)| Ok(Value::Vector(x, y, z)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?;
|
||||
let json = serde_json::json!({
|
||||
"_vector": [1.0, 2.0, 3.0],
|
||||
});
|
||||
assert_eq!(serde_json::to_value(&val)?, json);
|
||||
|
||||
let expected_json = lua.from_value::<serde_json::Value>(val)?;
|
||||
assert_eq!(expected_json, json);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_value_struct() -> LuaResult<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+16
-7
@@ -94,7 +94,11 @@ fn test_thread() -> Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))]
|
||||
#[cfg(any(
|
||||
feature = "lua54",
|
||||
all(feature = "luajit", feature = "vendored"),
|
||||
feature = "luau",
|
||||
))]
|
||||
fn test_thread_reset() -> Result<()> {
|
||||
use mlua::{AnyUserData, UserData};
|
||||
use std::sync::Arc;
|
||||
@@ -121,14 +125,14 @@ fn test_thread_reset() -> Result<()> {
|
||||
assert_eq!(Arc::strong_count(&arc), 1);
|
||||
}
|
||||
|
||||
// Check for errors (Lua 5.4 only)
|
||||
// Check for errors
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
#[cfg(feature = "lua54")]
|
||||
{
|
||||
let func: Function = lua.load(r#"function(ud) error("test error") end"#).eval()?;
|
||||
let thread = lua.create_thread(func.clone())?;
|
||||
let _ = thread.resume::<_, AnyUserData>(MyUserData(arc.clone()));
|
||||
assert_eq!(thread.status(), ThreadStatus::Error);
|
||||
assert_eq!(Arc::strong_count(&arc), 2);
|
||||
assert!(thread.reset(func.clone()).is_err());
|
||||
// Reset behavior has changed in Lua v5.4.4
|
||||
// It's became possible to force reset thread by popping error object
|
||||
@@ -140,6 +144,11 @@ fn test_thread_reset() -> Result<()> {
|
||||
// assert!(thread.reset(func.clone()).is_ok());
|
||||
// assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "luau"))]
|
||||
{
|
||||
assert!(thread.reset(func.clone()).is_ok());
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user