mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03b6dfb2c3 | |||
| cf1cb31150 | |||
| 7d586f52f2 | |||
| e85818e199 | |||
| 16bec29274 | |||
| 5a135a331a | |||
| e7b712e29f | |||
| d5483988d2 | |||
| 0f5c68dcf8 | |||
| 8ab0ccf11c | |||
| 9596f2e9ee | |||
| 1dc32452e6 | |||
| 9785722d61 | |||
| c905a34b1d | |||
| a1089dbf95 | |||
| baf25e263f | |||
| 3abf73dee5 | |||
| 4adebd31f9 | |||
| cea2d7fd15 | |||
| e0224ab159 | |||
| 6dee339783 | |||
| b674d7906d | |||
| 3d7796de55 | |||
| 4306e6e978 | |||
| 68e65a8ffe | |||
| 2efc637ab9 | |||
| 22e748557c | |||
| 77effb5055 | |||
| 1c66a02878 | |||
| 1ac98e7d16 | |||
| bbd2fe06e1 | |||
| d951cb503f | |||
| bbd2488f79 |
@@ -9,7 +9,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
rust: [stable, nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau, luau-jit]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
rust: [nightly]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
target: x86_64-unknown-linux-gnu
|
||||
@@ -222,7 +222,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau]
|
||||
lua: [lua54, lua53, lua52, lua51, luajit, luau, luau-jit]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
@@ -4,3 +4,4 @@ Cargo.lock
|
||||
|
||||
.vscode/
|
||||
.DS_Store
|
||||
.stignore
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
## v0.9.0-beta.3
|
||||
|
||||
- Added `OwnedAnyUserData::take()`
|
||||
- Switch to `DeserializeOwned`
|
||||
- Overwrite error context when called multiple times
|
||||
- New feature flag `luau-jit` to enable (experimental) Luau codegen backend
|
||||
- Set `__name` field in userdata metatable
|
||||
- Added `Value::to_string()` method similar to `luaL_tolstring`
|
||||
- Lua 5.4.6
|
||||
- Application data container now allows to mutably and immutably borrow different types at the same time
|
||||
- Performance optimizations
|
||||
- Support getting and setting environment for Lua functions.
|
||||
- Added `UserDataFields::add_field()` method to add static fields to UserData
|
||||
|
||||
Breaking changes:
|
||||
- Require environment to be a `Table` instead of `Value` in Chunks.
|
||||
- `AsChunk::env()` renamed to `AsChunk::environment()`
|
||||
|
||||
## v0.9.0-beta.2
|
||||
|
||||
New features:
|
||||
|
||||
+7
-8
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.0-beta.2" # remember to update mlua_derive
|
||||
version = "0.9.0-beta.3" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -32,33 +32,32 @@ lua51 = ["ffi/lua51"]
|
||||
luajit = ["ffi/luajit"]
|
||||
luajit52 = ["luajit", "ffi/luajit52"]
|
||||
luau = ["ffi/luau"]
|
||||
luau-jit = ["luau", "ffi/luau-codegen"]
|
||||
vendored = ["ffi/vendored"]
|
||||
module = ["mlua_derive", "ffi/module"]
|
||||
async = ["futures-core", "futures-task", "futures-util"]
|
||||
async = ["futures-util"]
|
||||
send = []
|
||||
serialize = ["serde", "erased-serde", "serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.0-beta.1", optional = true, path = "mlua_derive" }
|
||||
mlua_derive = { version = "=0.9.0-beta.2", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default_features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "1.0"
|
||||
futures-core = { version = "0.3.5", optional = true }
|
||||
futures-task = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3.5", optional = true }
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.3", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.1.0", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.2.0", path = "mlua-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
rustyline = "11.0"
|
||||
criterion = { version = "0.4", features = ["html_reports", "async_tokio"] }
|
||||
criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
trybuild = "1.0"
|
||||
futures = "0.3.5"
|
||||
hyper = { version = "0.14", features = ["client", "server"] }
|
||||
|
||||
@@ -45,6 +45,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `luajit`: activate [LuaJIT] support
|
||||
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
|
||||
* `luau`: activate [Luau] support (auto vendored mode)
|
||||
* `luau-jit`: activate [Luau] support with experimental jit backend. This is unstable feature and not recommended to use.
|
||||
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
|
||||
* `module`: enable module mode (building loadable `cdylib` library for Lua)
|
||||
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.1.1"
|
||||
version = "0.2.0"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/mlua"
|
||||
@@ -26,6 +26,7 @@ lua51 = []
|
||||
luajit = []
|
||||
luajit52 = ["luajit"]
|
||||
luau = ["luau0-src"]
|
||||
luau-codegen = ["luau"]
|
||||
vendored = ["lua-src", "luajit-src"]
|
||||
module = []
|
||||
|
||||
@@ -35,6 +36,6 @@ module = []
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
|
||||
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
|
||||
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
|
||||
luau0-src = { version = "0.5.6", optional = true }
|
||||
luau0-src = { version = "0.5.8", optional = true }
|
||||
|
||||
@@ -5,22 +5,25 @@ use std::path::PathBuf;
|
||||
pub fn probe_lua() -> Option<PathBuf> {
|
||||
#[cfg(feature = "lua54")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua54);
|
||||
|
||||
#[cfg(feature = "lua53")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
|
||||
|
||||
#[cfg(feature = "lua52")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua52);
|
||||
|
||||
#[cfg(feature = "lua51")]
|
||||
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
let artifacts = {
|
||||
let mut builder = luajit_src::Build::new();
|
||||
if cfg!(feature = "luajit52") {
|
||||
builder.lua52compat(true);
|
||||
}
|
||||
builder.build()
|
||||
};
|
||||
let artifacts = luajit_src::Build::new()
|
||||
.lua52compat(cfg!(feature = "luajit52"))
|
||||
.build();
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let artifacts = luau0_src::Build::new().build();
|
||||
let artifacts = luau0_src::Build::new()
|
||||
.enable_codegen(cfg!(feature = "luau-codegen"))
|
||||
.build();
|
||||
|
||||
artifacts.print_cargo_metadata();
|
||||
|
||||
|
||||
+6
-6
@@ -46,7 +46,7 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
// The minimum alignment guaranteed by the architecture. This value is used to
|
||||
// add fast paths for low alignment values.
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(all(any(
|
||||
#[cfg(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
@@ -58,10 +58,10 @@ pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
target_arch = "hexagon",
|
||||
all(target_arch = "riscv32", not(target_os = "espidf")),
|
||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
||||
)))]
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "mips64",
|
||||
@@ -69,14 +69,14 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
target_arch = "sparc64",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "wasm64",
|
||||
)))]
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
// The allocator on the esp-idf platform guarentees 4 byte alignment.
|
||||
#[cfg(all(any(
|
||||
#[cfg(any(
|
||||
all(target_arch = "riscv32", target_os = "espidf"),
|
||||
all(target_arch = "xtensa", target_os = "espidf"),
|
||||
)))]
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 4;
|
||||
|
||||
|
||||
@@ -486,10 +486,10 @@ pub unsafe fn luaL_traceback(
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -503,7 +503,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -512,7 +512,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -205,10 +205,10 @@ pub unsafe fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_in
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -222,7 +222,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -231,7 +231,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,7 +25,8 @@ extern "C" {
|
||||
|
||||
pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
|
||||
pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
#[link_name = "luaL_tolstring"]
|
||||
pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
|
||||
pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int;
|
||||
pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char;
|
||||
pub fn luaL_optlstring(
|
||||
@@ -167,6 +168,11 @@ pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
|
||||
lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
luaL_tolstring_(L, lua::lua_absindex(L, idx), len)
|
||||
}
|
||||
|
||||
// luaL_opt would be implemented here but it is undocumented, so it's omitted
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
@@ -112,7 +112,10 @@ extern "C" {
|
||||
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
||||
pub fn lua_close(L: *mut lua_State);
|
||||
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
|
||||
// Deprecated in Lua 5.4.6
|
||||
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
|
||||
#[cfg(feature = "vendored")]
|
||||
pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int;
|
||||
|
||||
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
|
||||
|
||||
|
||||
@@ -436,10 +436,10 @@ pub unsafe fn luaL_traceback(
|
||||
lua_concat(L, lua_gettop(L) - top);
|
||||
}
|
||||
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char {
|
||||
pub unsafe fn luaL_tolstring(L: *mut lua_State, mut idx: c_int, len: *mut usize) -> *const c_char {
|
||||
idx = lua_absindex(L, idx);
|
||||
if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 {
|
||||
let t = lua_type(L, idx);
|
||||
match t {
|
||||
match lua_type(L, idx) {
|
||||
LUA_TNIL => {
|
||||
lua_pushliteral(L, "nil");
|
||||
}
|
||||
@@ -453,7 +453,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
lua_pushliteral(L, "true");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
t => {
|
||||
let tt = luaL_getmetafield(L, idx, cstr!("__name"));
|
||||
let name = if tt == LUA_TSTRING {
|
||||
lua_tostring(L, -1)
|
||||
@@ -462,7 +462,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) ->
|
||||
};
|
||||
lua_pushfstring(L, cstr!("%s: %p"), name, lua_topointer(L, idx));
|
||||
if tt != LUA_TNIL {
|
||||
lua_replace(L, -2);
|
||||
lua_replace(L, -2); // remove '__name'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,6 +84,11 @@ pub type lua_Alloc = unsafe extern "C" fn(
|
||||
nsize: usize,
|
||||
) -> *mut c_void;
|
||||
|
||||
/// Returns Luau release version (eg. `0.xxx`).
|
||||
pub const fn luau_version() -> Option<&'static str> {
|
||||
option_env!("LUAU_VERSION")
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
//
|
||||
// State manipulation
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//! Contains definitions from `luacodegen.h`.
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use super::lua::lua_State;
|
||||
|
||||
extern "C" {
|
||||
pub fn luau_codegen_supported() -> c_int;
|
||||
pub fn luau_codegen_create(state: *mut lua_State);
|
||||
pub fn luau_codegen_compile(state: *mut lua_State, idx: c_int);
|
||||
}
|
||||
@@ -4,10 +4,12 @@ pub use compat::*;
|
||||
pub use lauxlib::*;
|
||||
pub use lua::*;
|
||||
pub use luacode::*;
|
||||
pub use luacodegen::*;
|
||||
pub use lualib::*;
|
||||
|
||||
pub mod compat;
|
||||
pub mod lauxlib;
|
||||
pub mod lua;
|
||||
pub mod luacode;
|
||||
pub mod luacodegen;
|
||||
pub mod lualib;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua_derive"
|
||||
version = "0.9.0-beta.1"
|
||||
version = "0.9.0-beta.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
edition = "2021"
|
||||
description = "Procedural macros for the mlua crate."
|
||||
|
||||
@@ -84,26 +84,26 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
});
|
||||
|
||||
let wrapped_code = quote! {{
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Value};
|
||||
use ::mlua::{AsChunk, ChunkMode, Lua, Result, Table};
|
||||
use ::std::borrow::Cow;
|
||||
use ::std::io::Result as IoResult;
|
||||
use ::std::sync::Mutex;
|
||||
|
||||
struct InnerChunk<F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(Mutex<Option<F>>);
|
||||
struct InnerChunk<F: for <'a> FnOnce(&'a Lua) -> Result<Table<'a>>>(Mutex<Option<F>>);
|
||||
|
||||
impl<F> AsChunk<'static> for InnerChunk<F>
|
||||
where
|
||||
F: for <'a> FnOnce(&'a Lua) -> Result<Value<'a>>,
|
||||
F: for <'a> FnOnce(&'a Lua) -> Result<Table<'a>>,
|
||||
{
|
||||
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn environment<'lua>(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
|
||||
if #caps_len > 0 {
|
||||
if let Ok(mut make_env) = self.0.lock() {
|
||||
if let Some(make_env) = make_env.take() {
|
||||
return make_env(lua);
|
||||
return make_env(lua).map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::Nil)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn mode(&self) -> Option<ChunkMode> {
|
||||
@@ -115,9 +115,9 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
fn annotate<F: for<'a> FnOnce(&'a Lua) -> Result<Value<'a>>>(f: F) -> F { f }
|
||||
fn annotate<F: for<'a> FnOnce(&'a Lua) -> Result<Table<'a>>>(f: F) -> F { f }
|
||||
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Value> {
|
||||
let make_env = annotate(move |lua: &Lua| -> Result<Table> {
|
||||
let globals = lua.globals();
|
||||
let env = lua.create_table()?;
|
||||
let meta = lua.create_table()?;
|
||||
@@ -128,7 +128,7 @@ pub fn chunk(input: TokenStream) -> TokenStream {
|
||||
#(#caps)*
|
||||
|
||||
env.set_metatable(Some(meta));
|
||||
Ok(Value::Table(env))
|
||||
Ok(env)
|
||||
});
|
||||
|
||||
InnerChunk(Mutex::new(Some(make_env)))
|
||||
|
||||
+15
-11
@@ -5,13 +5,14 @@ use std::io::Result as IoResult;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::error::{Error, ErrorContext, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::table::Table;
|
||||
use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
|
||||
/// Trait for types [loadable by Lua] and convertible to a [`Chunk`]
|
||||
///
|
||||
@@ -26,9 +27,9 @@ pub trait AsChunk<'a> {
|
||||
/// Returns optional chunk [environment]
|
||||
///
|
||||
/// [environment]: https://www.lua.org/manual/5.4/manual.html#2.2
|
||||
fn env<'lua>(&self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
||||
fn environment<'lua>(&self, lua: &'lua Lua) -> Result<Option<Table<'lua>>> {
|
||||
let _lua = lua; // suppress warning
|
||||
Ok(Value::Nil)
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Returns optional chunk mode (text or binary)
|
||||
@@ -103,7 +104,7 @@ impl AsChunk<'static> for PathBuf {
|
||||
pub struct Chunk<'lua, 'a> {
|
||||
pub(crate) lua: &'lua Lua,
|
||||
pub(crate) name: StdString,
|
||||
pub(crate) env: Result<Value<'lua>>,
|
||||
pub(crate) env: Result<Option<Table<'lua>>>,
|
||||
pub(crate) mode: Option<ChunkMode>,
|
||||
pub(crate) source: IoResult<Cow<'a, [u8]>>,
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -254,9 +255,9 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
|
||||
/// Sets the environment of the loaded chunk to the given value.
|
||||
///
|
||||
/// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
|
||||
/// In Lua >=5.2 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
|
||||
@@ -266,7 +267,10 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
/// necessary to populate the environment in order for scripts using custom environments to be
|
||||
/// useful.
|
||||
pub fn set_environment<V: IntoLua<'lua>>(mut self, env: V) -> Self {
|
||||
self.env = env.into_lua(self.lua);
|
||||
self.env = env
|
||||
.into_lua(self.lua)
|
||||
.and_then(|val| self.lua.unpack(val))
|
||||
.context("bad environment value");
|
||||
self
|
||||
}
|
||||
|
||||
@@ -414,7 +418,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
}
|
||||
#[cfg(not(feature = "luau"))]
|
||||
if let Ok(func) = self.lua.load_chunk(None, Value::Nil, None, source.as_ref()) {
|
||||
if let Ok(func) = self.lua.load_chunk(None, None, None, source.as_ref()) {
|
||||
let data = func.dump(false);
|
||||
self.source = Ok(Cow::Owned(data));
|
||||
self.mode = Some(ChunkMode::Binary);
|
||||
@@ -454,7 +458,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
} else {
|
||||
let mut cache = ChunksCache(HashMap::new());
|
||||
cache.0.insert(text_source, binary_source.as_ref().to_vec());
|
||||
self.lua.set_app_data(cache);
|
||||
let _ = self.lua.try_set_app_data(cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-14
@@ -335,6 +335,10 @@ impl StdError for Error {
|
||||
// Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
|
||||
Error::CallbackError { .. } => None,
|
||||
Error::ExternalError(ref err) => err.source(),
|
||||
Error::WithContext { ref cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.source(),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -353,6 +357,10 @@ impl Error {
|
||||
{
|
||||
match self {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
Error::WithContext { cause, .. } => match cause.as_ref() {
|
||||
Error::ExternalError(err) => err.downcast_ref(),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -414,33 +422,35 @@ pub trait ErrorContext: Sealed {
|
||||
|
||||
impl ErrorContext for Error {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
Error::WithContext {
|
||||
context: context.to_string(),
|
||||
cause: Arc::new(self),
|
||||
let context = context.to_string();
|
||||
match self {
|
||||
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
|
||||
_ => Error::WithContext {
|
||||
context,
|
||||
cause: Arc::new(self),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
|
||||
Error::WithContext {
|
||||
context: f(&self).to_string(),
|
||||
cause: Arc::new(self),
|
||||
let context = f(&self).to_string();
|
||||
match self {
|
||||
Error::WithContext { cause, .. } => Error::WithContext { context, cause },
|
||||
_ => Error::WithContext {
|
||||
context,
|
||||
cause: Arc::new(self),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ErrorContext for StdResult<T, Error> {
|
||||
fn context<C: fmt::Display>(self, context: C) -> Self {
|
||||
self.map_err(|err| Error::WithContext {
|
||||
context: context.to_string(),
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
self.map_err(|err| err.context(context))
|
||||
}
|
||||
|
||||
fn with_context<C: fmt::Display>(self, f: impl FnOnce(&Error) -> C) -> Self {
|
||||
self.map_err(|err| Error::WithContext {
|
||||
context: f(&err).to_string(),
|
||||
cause: Arc::new(err),
|
||||
})
|
||||
self.map_err(|err| err.with_context(f))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+131
-10
@@ -7,6 +7,7 @@ use std::slice;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::lua::Lua;
|
||||
use crate::memory::MemoryState;
|
||||
use crate::table::Table;
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard,
|
||||
@@ -16,8 +17,7 @@ use crate::value::{FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
futures_core::future::{Future, LocalBoxFuture},
|
||||
futures_util::{future, TryFutureExt},
|
||||
futures_util::future::{self, Future, LocalBoxFuture, TryFutureExt},
|
||||
};
|
||||
|
||||
/// Handle to an internal Lua function.
|
||||
@@ -45,15 +45,30 @@ impl OwnedFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains information about a function.
|
||||
///
|
||||
/// Please refer to the [`Lua Debug Interface`] for more information.
|
||||
///
|
||||
/// [`Lua Debug Interface`]: https://www.lua.org/manual/5.4/manual.html#4.7
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionInfo {
|
||||
pub name: Option<Vec<u8>>,
|
||||
pub name_what: Option<Vec<u8>>,
|
||||
pub what: Option<Vec<u8>>,
|
||||
/// A (reasonable) name of the function.
|
||||
pub name: Option<String>,
|
||||
/// Explains the `name` field ("global", "local", "method", "field", "upvalue", or "").
|
||||
///
|
||||
/// Always `None` for Luau.
|
||||
pub name_what: Option<String>,
|
||||
/// A string "Lua" if the function is a Lua function, "C" if it is a C function, "main" if it is the main part of a chunk.
|
||||
pub what: Option<String>,
|
||||
/// The source of the chunk that created the function.
|
||||
pub source: Option<Vec<u8>>,
|
||||
/// A "printable" version of source, to be used in error messages.
|
||||
pub short_src: Option<Vec<u8>>,
|
||||
/// The line number where the definition of the function starts.
|
||||
pub line_defined: i32,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
/// The line number where the definition of the function ends.
|
||||
///
|
||||
/// Always `-1` for Luau.
|
||||
pub last_line_defined: i32,
|
||||
}
|
||||
|
||||
@@ -271,10 +286,113 @@ impl<'lua> Function<'lua> {
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("_mlua_bind")
|
||||
.set_name("__mlua_bind")
|
||||
.call((self.clone(), args_wrapper))
|
||||
}
|
||||
|
||||
/// Returns the environment of the Lua function.
|
||||
///
|
||||
/// By default Lua functions shares a global environment.
|
||||
///
|
||||
/// This function always returns `None` for Rust/C functions.
|
||||
pub fn environment(&self) -> Option<Table> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 1);
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
|
||||
|
||||
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
|
||||
return None;
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_getfenv(state, -1);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
for i in 1..=255 {
|
||||
// Traverse upvalues until we find the _ENV one
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => break,
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => break,
|
||||
_ => ffi::lua_pop(state, 1),
|
||||
}
|
||||
}
|
||||
|
||||
if ffi::lua_type(state, -1) != ffi::LUA_TTABLE {
|
||||
return None;
|
||||
}
|
||||
Some(Table(lua.pop_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the environment of the Lua function.
|
||||
///
|
||||
/// The environment is a table that is used as the global environment for the function.
|
||||
/// Returns `true` if environment successfully changed, `false` otherwise.
|
||||
///
|
||||
/// This function does nothing for Rust/C functions.
|
||||
pub fn set_environment(&self, env: Table) -> Result<bool> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
lua.push_ref(&self.0);
|
||||
|
||||
let mut ar: ffi::lua_Debug = mem::zeroed();
|
||||
#[cfg(not(feature = "luau"))]
|
||||
{
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_getinfo(state, cstr!(">S"), &mut ar);
|
||||
}
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_getinfo(state, -1, cstr!("s"), &mut ar);
|
||||
|
||||
if ptr_to_cstr_bytes(ar.what) == Some(b"C") {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
{
|
||||
lua.push_ref(&env.0);
|
||||
ffi::lua_setfenv(state, -2);
|
||||
}
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
for i in 1..=255 {
|
||||
match ffi::lua_getupvalue(state, -1, i) {
|
||||
s if s.is_null() => return Ok(false),
|
||||
s if std::ffi::CStr::from_ptr(s as _).to_bytes() == b"_ENV" => {
|
||||
ffi::lua_pop(state, 1);
|
||||
// Create an anonymous function with the new environment
|
||||
let f_with_env = lua
|
||||
.load("return _ENV")
|
||||
.set_environment(env)
|
||||
.try_cache()
|
||||
.into_function()?;
|
||||
lua.push_ref(&f_with_env.0);
|
||||
ffi::lua_upvaluejoin(state, -2, i, -1, 1);
|
||||
break;
|
||||
}
|
||||
_ => ffi::lua_pop(state, 1),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns information about the function.
|
||||
///
|
||||
/// Corresponds to the `>Sn` what mask for [`lua_getinfo`] when applied to the function.
|
||||
@@ -296,12 +414,13 @@ impl<'lua> Function<'lua> {
|
||||
mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`");
|
||||
|
||||
FunctionInfo {
|
||||
name: ptr_to_cstr_bytes(ar.name).map(|s| s.to_vec()),
|
||||
name: ptr_to_cstr_bytes(ar.name).map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
name_what: ptr_to_cstr_bytes(ar.namewhat).map(|s| s.to_vec()),
|
||||
name_what: ptr_to_cstr_bytes(ar.namewhat)
|
||||
.map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
#[cfg(feature = "luau")]
|
||||
name_what: None,
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| s.to_vec()),
|
||||
what: ptr_to_cstr_bytes(ar.what).map(|s| String::from_utf8_lossy(s).into_owned()),
|
||||
source: ptr_to_cstr_bytes(ar.source).map(|s| s.to_vec()),
|
||||
#[cfg(not(feature = "luau"))]
|
||||
short_src: ptr_to_cstr_bytes(ar.short_src.as_ptr()).map(|s| s.to_vec()),
|
||||
@@ -310,6 +429,8 @@ impl<'lua> Function<'lua> {
|
||||
line_defined: ar.linedefined,
|
||||
#[cfg(not(feature = "luau"))]
|
||||
last_line_defined: ar.lastlinedefined,
|
||||
#[cfg(feature = "luau")]
|
||||
last_line_defined: -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -116,7 +116,7 @@ pub use crate::stdlib::StdLib;
|
||||
pub use crate::string::String;
|
||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||
pub use crate::thread::{Thread, ThreadStatus};
|
||||
pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey};
|
||||
pub use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
|
||||
UserDataRef, UserDataRefMut,
|
||||
@@ -145,7 +145,7 @@ pub use crate::serde::{
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub mod serde;
|
||||
|
||||
#[cfg(any(feature = "mlua_derive"))]
|
||||
#[cfg(feature = "mlua_derive")]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate mlua_derive;
|
||||
@@ -208,7 +208,7 @@ pub use crate::{
|
||||
/// [`AsChunk`]: crate::AsChunk
|
||||
/// [`UserData`]: crate::UserData
|
||||
/// [`IntoLua`]: crate::IntoLua
|
||||
#[cfg(any(feature = "macros"))]
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
pub use mlua_derive::chunk;
|
||||
|
||||
|
||||
+252
-189
@@ -1,5 +1,5 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::any::TypeId;
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
@@ -8,6 +8,7 @@ use std::ops::Deref;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location};
|
||||
use std::ptr::NonNull;
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{mem, ptr, str};
|
||||
@@ -25,16 +26,16 @@ use crate::string::String;
|
||||
use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{
|
||||
Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData, LuaRef, MaybeSend,
|
||||
Number, RegistryKey,
|
||||
AppData, AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer,
|
||||
LightUserData, LuaRef, MaybeSend, Number, RegistryKey,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataCell};
|
||||
use crate::userdata_impl::{UserDataProxy, UserDataRegistrar};
|
||||
use crate::util::{
|
||||
self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable,
|
||||
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry,
|
||||
init_gc_metatable, init_userdata_metatable, pop_error, push_gc_userdata, push_string,
|
||||
push_table, rawset_field, safe_pcall, safe_xpcall, StackGuard, WrappedFailure,
|
||||
self, assert_stack, check_stack, get_destructed_userdata_metatable, get_gc_metatable,
|
||||
get_gc_userdata, get_main_state, get_userdata, init_error_registry, init_gc_metatable,
|
||||
init_userdata_metatable, pop_error, push_gc_userdata, push_string, push_table, rawset_field,
|
||||
safe_pcall, safe_xpcall, short_type_name, StackGuard, WrappedFailure,
|
||||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
|
||||
|
||||
@@ -54,12 +55,8 @@ use crate::{chunk::Compiler, types::VmState};
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
futures_task::noop_waker_ref,
|
||||
futures_util::future::{self, TryFutureExt},
|
||||
std::{
|
||||
future::Future,
|
||||
task::{Context, Poll, Waker},
|
||||
},
|
||||
futures_util::future::{self, Future, TryFutureExt},
|
||||
futures_util::task::{noop_waker_ref, Context, Poll, Waker},
|
||||
};
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -89,10 +86,8 @@ pub(crate) struct ExtraData {
|
||||
// When Lua instance dropped, setting `None` would prevent collecting `RegistryKey`s
|
||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
app_data: RefCell<FxHashMap<TypeId, Box<dyn Any>>>,
|
||||
#[cfg(feature = "send")]
|
||||
app_data: RefCell<FxHashMap<TypeId, Box<dyn Any + Send>>>,
|
||||
// Container to store arbitrary data (extensions)
|
||||
app_data: AppData,
|
||||
|
||||
safe: bool,
|
||||
libs: StdLib,
|
||||
@@ -131,6 +126,8 @@ pub(crate) struct ExtraData {
|
||||
sandboxed: bool,
|
||||
#[cfg(feature = "luau")]
|
||||
compiler: Option<Compiler>,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
enable_jit: bool,
|
||||
}
|
||||
|
||||
/// Mode of the Lua garbage collector (GC).
|
||||
@@ -145,7 +142,7 @@ pub(crate) struct ExtraData {
|
||||
pub enum GCMode {
|
||||
Incremental,
|
||||
/// Requires `feature = "lua54"`
|
||||
#[cfg(any(feature = "lua54"))]
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
Generational,
|
||||
}
|
||||
@@ -395,6 +392,12 @@ impl Lua {
|
||||
ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
// Init Luau code generator (jit)
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_create(state);
|
||||
}
|
||||
|
||||
let lua = Lua::init_from_ptr(state);
|
||||
let extra = lua.extra.get();
|
||||
(*extra).mem_state = NonNull::new(mem_state);
|
||||
@@ -504,7 +507,7 @@ impl Lua {
|
||||
registered_userdata_mt: FxHashMap::default(),
|
||||
last_checked_userdata_mt: (ptr::null(), None),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
app_data: RefCell::new(FxHashMap::default()),
|
||||
app_data: AppData::default(),
|
||||
safe: false,
|
||||
libs: StdLib::NONE,
|
||||
mem_state: None,
|
||||
@@ -532,6 +535,8 @@ impl Lua {
|
||||
sandboxed: false,
|
||||
#[cfg(feature = "luau")]
|
||||
compiler: None,
|
||||
#[cfg(feature = "luau-jit")]
|
||||
enable_jit: true,
|
||||
}));
|
||||
|
||||
// Store it in the registry
|
||||
@@ -881,9 +886,6 @@ impl Lua {
|
||||
{
|
||||
unsafe extern "C" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) {
|
||||
let extra = extra_data(state);
|
||||
if extra.is_null() {
|
||||
return;
|
||||
}
|
||||
if (*extra).hook_thread != state {
|
||||
// Hook was destined for a different thread, ignore
|
||||
ffi::lua_sethook(state, None, 0, 0);
|
||||
@@ -982,9 +984,6 @@ impl Lua {
|
||||
return;
|
||||
}
|
||||
let extra = extra_data(state);
|
||||
if extra.is_null() {
|
||||
return;
|
||||
}
|
||||
let result = callback_error_ext(state, extra, move |_| {
|
||||
let interrupt_cb = (*extra).interrupt_callback.clone();
|
||||
let interrupt_cb =
|
||||
@@ -1270,7 +1269,7 @@ impl Lua {
|
||||
/// Requires `feature = "lua54"`
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5.2
|
||||
#[cfg(any(feature = "lua54"))]
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub fn gc_gen(&self, minor_multiplier: c_int, major_multiplier: c_int) -> GCMode {
|
||||
let state = self.main_state;
|
||||
@@ -1297,6 +1296,16 @@ impl Lua {
|
||||
unsafe { (*self.extra.get()).compiler = Some(compiler) };
|
||||
}
|
||||
|
||||
/// Toggles JIT compilation mode for new chunks of code.
|
||||
///
|
||||
/// By default JIT is enabled. Changing this option does not have any effect on
|
||||
/// already loaded functions.
|
||||
#[cfg(any(feature = "luau-jit", docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau-jit")))]
|
||||
pub fn enable_jit(&self, enable: bool) {
|
||||
unsafe { (*self.extra.get()).enable_jit = enable };
|
||||
}
|
||||
|
||||
/// 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
|
||||
@@ -1310,7 +1319,7 @@ impl Lua {
|
||||
Chunk {
|
||||
lua: self,
|
||||
name: chunk.name().unwrap_or_else(|| caller.to_string()),
|
||||
env: chunk.env(self),
|
||||
env: chunk.environment(self),
|
||||
mode: chunk.mode(),
|
||||
source: chunk.source(),
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -1321,7 +1330,7 @@ impl Lua {
|
||||
pub(crate) fn load_chunk<'lua>(
|
||||
&'lua self,
|
||||
name: Option<&CStr>,
|
||||
env: Value<'lua>,
|
||||
env: Option<Table>,
|
||||
mode: Option<ChunkMode>,
|
||||
source: &[u8],
|
||||
) -> Result<Function<'lua>> {
|
||||
@@ -1344,13 +1353,19 @@ impl Lua {
|
||||
mode_str,
|
||||
) {
|
||||
ffi::LUA_OK => {
|
||||
if env != Value::Nil {
|
||||
self.push_value(env)?;
|
||||
if let Some(env) = env {
|
||||
self.push_ref(&env.0);
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
ffi::lua_setupvalue(state, -2, 1);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
|
||||
ffi::lua_setfenv(state, -2);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if (*self.extra.get()).enable_jit && ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
Ok(Function(self.pop_ref()))
|
||||
}
|
||||
err => Err(pop_error(state, err)),
|
||||
@@ -1671,8 +1686,10 @@ impl Lua {
|
||||
let extra = &mut *self.extra.get();
|
||||
if extra.thread_pool.len() < extra.thread_pool.capacity() {
|
||||
let thread_state = ffi::lua_tothread(extra.ref_thread, thread.0.index);
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, self.state());
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
// Error object is on top, drop it
|
||||
@@ -1840,7 +1857,10 @@ impl Lua {
|
||||
pub fn scope<'lua, 'scope, R>(
|
||||
&'lua self,
|
||||
f: impl FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
|
||||
) -> Result<R> {
|
||||
) -> Result<R>
|
||||
where
|
||||
'lua: 'scope,
|
||||
{
|
||||
f(&Scope::new(self))
|
||||
}
|
||||
|
||||
@@ -2198,51 +2218,45 @@ impl Lua {
|
||||
/// }
|
||||
/// ```
|
||||
#[track_caller]
|
||||
pub fn set_app_data<T: 'static + MaybeSend>(&self, data: T) -> Option<T> {
|
||||
pub fn set_app_data<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
extra
|
||||
.app_data
|
||||
.try_borrow_mut()
|
||||
.expect("cannot borrow mutably app data container")
|
||||
.insert(TypeId::of::<T>(), Box::new(data))
|
||||
.and_then(|data| data.downcast::<T>().ok().map(|data| *data))
|
||||
extra.app_data.insert(data)
|
||||
}
|
||||
|
||||
/// Tries to set or replace an application data object of type `T`.
|
||||
///
|
||||
/// Returns:
|
||||
/// - `Ok(Some(old_data))` if the data object of type `T` was successfully replaced.
|
||||
/// - `Ok(None)` if the data object of type `T` was successfully inserted.
|
||||
/// - `Err(data)` if the data object of type `T` was not inserted because the container is currently borrowed.
|
||||
///
|
||||
/// See [`Lua::set_app_data()`] for examples.
|
||||
pub fn try_set_app_data<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
extra.app_data.try_insert(data)
|
||||
}
|
||||
|
||||
/// Gets a reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the app data container is currently mutably borrowed. Multiple immutable reads can be
|
||||
/// taken out at the same time.
|
||||
/// Panics if the data object of type `T` is currently mutably borrowed. Multiple immutable reads
|
||||
/// can be taken out at the same time.
|
||||
#[track_caller]
|
||||
pub fn app_data_ref<T: 'static>(&self) -> Option<Ref<T>> {
|
||||
pub fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
let app_data = extra
|
||||
.app_data
|
||||
.try_borrow()
|
||||
.expect("cannot borrow app data container");
|
||||
Ref::filter_map(app_data, |data| {
|
||||
data.get(&TypeId::of::<T>())?.downcast_ref::<T>()
|
||||
})
|
||||
.ok()
|
||||
extra.app_data.borrow()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the app data container is currently borrowed.
|
||||
/// Panics if the data object of type `T` is currently borrowed.
|
||||
#[track_caller]
|
||||
pub fn app_data_mut<T: 'static>(&self) -> Option<RefMut<T>> {
|
||||
pub fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
let app_data = extra
|
||||
.app_data
|
||||
.try_borrow_mut()
|
||||
.expect("cannot mutably borrow app data container");
|
||||
RefMut::filter_map(app_data, |data| {
|
||||
data.get_mut(&TypeId::of::<T>())?.downcast_mut::<T>()
|
||||
})
|
||||
.ok()
|
||||
extra.app_data.borrow_mut()
|
||||
}
|
||||
|
||||
/// Removes an application data of type `T`.
|
||||
@@ -2253,12 +2267,7 @@ impl Lua {
|
||||
#[track_caller]
|
||||
pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
|
||||
let extra = unsafe { &*self.extra.get() };
|
||||
extra
|
||||
.app_data
|
||||
.try_borrow_mut()
|
||||
.expect("cannot mutably borrow app data container")
|
||||
.remove(&TypeId::of::<T>())
|
||||
.and_then(|data| data.downcast::<T>().ok().map(|data| *data))
|
||||
extra.app_data.remove()
|
||||
}
|
||||
|
||||
// Uses 2 stack spaces, does not call checkstack
|
||||
@@ -2438,20 +2447,20 @@ impl Lua {
|
||||
// references.
|
||||
pub(crate) unsafe fn pop_ref(&self) -> LuaRef {
|
||||
ffi::lua_xmove(self.state(), self.ref_thread(), 1);
|
||||
let index = ref_stack_pop(&mut *self.extra.get());
|
||||
let index = ref_stack_pop(self.extra.get());
|
||||
LuaRef::new(self, index)
|
||||
}
|
||||
|
||||
// Same as `pop_ref` but assumes the value is already on the reference thread
|
||||
pub(crate) unsafe fn pop_ref_thread(&self) -> LuaRef {
|
||||
let index = ref_stack_pop(&mut *self.extra.get());
|
||||
let index = ref_stack_pop(self.extra.get());
|
||||
LuaRef::new(self, index)
|
||||
}
|
||||
|
||||
pub(crate) fn clone_ref(&self, lref: &LuaRef) -> LuaRef {
|
||||
unsafe {
|
||||
ffi::lua_pushvalue(self.ref_thread(), lref.index);
|
||||
let index = ref_stack_pop(&mut *self.extra.get());
|
||||
let index = ref_stack_pop(self.extra.get());
|
||||
LuaRef::new(self, index)
|
||||
}
|
||||
}
|
||||
@@ -2481,7 +2490,7 @@ impl Lua {
|
||||
|
||||
unsafe fn register_userdata_metatable<'lua, T: 'static>(
|
||||
&'lua self,
|
||||
registry: UserDataRegistrar<'lua, T>,
|
||||
mut registry: UserDataRegistrar<'lua, T>,
|
||||
) -> Result<Integer> {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
@@ -2501,14 +2510,48 @@ impl Lua {
|
||||
self.push_value(Value::Function(self.create_async_callback(m)?))?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let mut has_name = false;
|
||||
for (k, f) in registry.meta_fields {
|
||||
self.push_value(f(self)?)?;
|
||||
has_name = has_name || k == "__name";
|
||||
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name` if not provided
|
||||
if !has_name {
|
||||
let type_name = short_type_name::<T>();
|
||||
push_string(state, type_name.as_bytes(), !self.unlikely_memory_error())?;
|
||||
rawset_field(state, -2, "__name")?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
||||
let mut extra_tables_count = 0;
|
||||
|
||||
let fields_nrec = registry.fields.len();
|
||||
if fields_nrec > 0 {
|
||||
// If __index is a table then update it inplace
|
||||
let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE => {
|
||||
if index_type == ffi::LUA_TNIL {
|
||||
// Create a new table
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, fields_nrec as c_int, true)?;
|
||||
}
|
||||
for (k, f) in registry.fields {
|
||||
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
}
|
||||
_ => {
|
||||
// Propagate fields to the field getters
|
||||
for (k, f) in registry.fields {
|
||||
registry.field_getters.push((k, f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = registry.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
@@ -2538,7 +2581,16 @@ impl Lua {
|
||||
#[cfg(feature = "async")]
|
||||
let methods_nrec = methods_nrec + registry.async_methods.len();
|
||||
if methods_nrec > 0 {
|
||||
push_table(state, 0, methods_nrec as c_int, true)?;
|
||||
// If __index is a table then update it inplace
|
||||
let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
|
||||
match index_type {
|
||||
ffi::LUA_TTABLE => {} // Update the existing table
|
||||
_ => {
|
||||
// Create a new table
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, methods_nrec as c_int, true)?;
|
||||
}
|
||||
}
|
||||
for (k, m) in registry.methods {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
@@ -2548,8 +2600,18 @@ impl Lua {
|
||||
self.push_value(Value::Function(self.create_async_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
match index_type {
|
||||
ffi::LUA_TTABLE => {
|
||||
ffi::lua_pop(state, 1); // All done
|
||||
}
|
||||
ffi::LUA_TNIL => {
|
||||
rawset_field(state, metatable_index, "__index")?; // Set the new table as __index
|
||||
}
|
||||
_ => {
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_userdata_metatable::<UserDataCell<T>>(
|
||||
@@ -2640,23 +2702,20 @@ impl Lua {
|
||||
func: Callback<'lua, 'static>,
|
||||
) -> Result<Function<'lua>> {
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
let extra = match ffi::lua_type(state, ffi::lua_upvalueindex(1)) {
|
||||
// Normal functions can be scoped and therefore destroyed,
|
||||
// so we need to check that the first upvalue is valid
|
||||
let (upvalue, extra) = match ffi::lua_type(state, ffi::lua_upvalueindex(1)) {
|
||||
ffi::LUA_TUSERDATA => {
|
||||
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).extra.get()
|
||||
(upvalue, (*upvalue).extra.get())
|
||||
}
|
||||
_ => ptr::null_mut(),
|
||||
_ => (ptr::null_mut(), ptr::null_mut()),
|
||||
};
|
||||
callback_error_ext(state, extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
if ffi::lua_type(state, upvalue_idx) == ffi::LUA_TNIL {
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
if upvalue.is_null() {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let upvalue = get_userdata::<CallbackUpvalue>(state, upvalue_idx);
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let _guard = StateGuard::new(&lua.0, state);
|
||||
@@ -2721,25 +2780,12 @@ impl Lua {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
let extra = match ffi::lua_type(state, ffi::lua_upvalueindex(1)) {
|
||||
ffi::LUA_TUSERDATA => {
|
||||
let upvalue =
|
||||
get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).extra.get()
|
||||
}
|
||||
_ => ptr::null_mut(),
|
||||
};
|
||||
// Async functions cannot be scoped and therefore destroyed,
|
||||
// so the first upvalue is always valid
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
let extra = (*upvalue).extra.get();
|
||||
callback_error_ext(state, extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
if ffi::lua_type(state, upvalue_idx) == ffi::LUA_TNIL {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, upvalue_idx);
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let _guard = StateGuard::new(&lua.0, state);
|
||||
|
||||
@@ -2767,44 +2813,35 @@ impl Lua {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
let extra = match ffi::lua_type(state, ffi::lua_upvalueindex(1)) {
|
||||
ffi::LUA_TUSERDATA => {
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).extra.get()
|
||||
}
|
||||
_ => ptr::null_mut(),
|
||||
};
|
||||
callback_error_ext(state, extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
if ffi::lua_type(state, upvalue_idx) == ffi::LUA_TNIL {
|
||||
return Err(Error::CallbackDestructed);
|
||||
}
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, upvalue_idx);
|
||||
|
||||
if nargs < ffi::LUA_MINSTACK {
|
||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||
}
|
||||
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
let extra = (*upvalue).extra.get();
|
||||
callback_error_ext(state, extra, |_| {
|
||||
let lua: &Lua = mem::transmute((*extra).inner.assume_init_ref());
|
||||
let _guard = StateGuard::new(&lua.0, state);
|
||||
|
||||
let fut = &mut (*upvalue).data;
|
||||
let mut ctx = Context::from_waker(lua.waker());
|
||||
match fut.as_mut().poll(&mut ctx) {
|
||||
Poll::Pending => {
|
||||
check_stack(state, 1)?;
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
Ok(1)
|
||||
}
|
||||
Poll::Pending => Ok(0),
|
||||
Poll::Ready(results) => {
|
||||
let results = results?;
|
||||
let nresults = results.len() as Integer;
|
||||
let results = lua.create_sequence_from(results)?;
|
||||
check_stack(state, 3)?;
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
lua.push_value(Value::Table(results))?;
|
||||
lua.push_value(Value::Integer(nresults))?;
|
||||
Ok(3)
|
||||
let mut results = results?;
|
||||
let nresults = results.len();
|
||||
lua.push_value(Value::Integer(nresults as _))?;
|
||||
match nresults {
|
||||
0 => Ok(1),
|
||||
1 | 2 => {
|
||||
// Fast path for 1 or 2 results without creating a table
|
||||
for r in results.drain_all() {
|
||||
lua.push_value(r)?;
|
||||
}
|
||||
MultiValue::return_to_pool(results, lua);
|
||||
Ok(nresults as c_int + 1)
|
||||
}
|
||||
_ => {
|
||||
lua.push_value(Value::Table(lua.create_sequence_from(results)?))?;
|
||||
Ok(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -2852,22 +2889,29 @@ impl Lua {
|
||||
LightUserData(&ASYNC_POLL_PENDING as *const u8 as *mut c_void)
|
||||
})?;
|
||||
|
||||
// We set `poll` variable in the env table to be able to destroy upvalues
|
||||
self.load(
|
||||
r#"
|
||||
poll = get_poll(...)
|
||||
local poll, pending, yield, unpack = poll, pending, yield, unpack
|
||||
local poll = get_poll(...)
|
||||
local pending, yield, unpack = pending, yield, unpack
|
||||
while true do
|
||||
local ready, res, nres = poll()
|
||||
if ready then
|
||||
return unpack(res, nres)
|
||||
local nres, res, res2 = poll()
|
||||
if nres ~= nil then
|
||||
if nres == 0 then
|
||||
return
|
||||
elseif nres == 1 then
|
||||
return res
|
||||
elseif nres == 2 then
|
||||
return res, res2
|
||||
else
|
||||
return unpack(res, nres)
|
||||
end
|
||||
end
|
||||
yield(pending)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.try_cache()
|
||||
.set_name("_mlua_async_poll")
|
||||
.set_name("__mlua_async_poll")
|
||||
.set_environment(env)
|
||||
.into_function()
|
||||
}
|
||||
@@ -3013,6 +3057,12 @@ impl LuaInner {
|
||||
self.state.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn main_state(&self) -> *mut ffi::lua_State {
|
||||
self.main_state
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn ref_thread(&self) -> *mut ffi::lua_State {
|
||||
unsafe { (*self.extra.get()).ref_thread }
|
||||
@@ -3068,6 +3118,8 @@ unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
|
||||
unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
|
||||
// `ExtraData` can be null only when Lua state is foreign.
|
||||
// This case in used in `Lua::try_from_ptr()`.
|
||||
ffi::lua_pop(state, 1);
|
||||
return ptr::null_mut();
|
||||
}
|
||||
@@ -3093,72 +3145,71 @@ pub(crate) fn init_metatable_cache(cache: &mut FxHashMap<TypeId, u8>) {
|
||||
|
||||
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
|
||||
// and instead reuses unsed values from previous calls (or allocates new).
|
||||
unsafe fn callback_error_ext<F, R>(state: *mut ffi::lua_State, extra: *mut ExtraData, f: F) -> R
|
||||
unsafe fn callback_error_ext<F, R>(state: *mut ffi::lua_State, mut extra: *mut ExtraData, f: F) -> R
|
||||
where
|
||||
F: FnOnce(c_int) -> Result<R>,
|
||||
{
|
||||
if extra.is_null() {
|
||||
return callback_error(state, f);
|
||||
extra = extra_data(state);
|
||||
}
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
|
||||
let nargs = ffi::lua_gettop(state);
|
||||
|
||||
// We need 2 extra stack spaces to store userdata and error/panic metatable.
|
||||
// Luau workaround can be removed after solving https://github.com/Roblox/luau/issues/446
|
||||
// Also see #142 and #153
|
||||
if !cfg!(feature = "luau") || (*extra).wrapped_failure_pool.is_empty() {
|
||||
let extra_stack = if nargs < 2 { 2 - nargs } else { 1 };
|
||||
ffi::luaL_checkstack(
|
||||
state,
|
||||
extra_stack,
|
||||
cstr!("not enough stack space for callback error handling"),
|
||||
);
|
||||
}
|
||||
|
||||
enum PreallocatedFailure {
|
||||
New(*mut WrappedFailure),
|
||||
Existing(i32),
|
||||
}
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory
|
||||
// to store a wrapped failure (error or panic) *before* we proceed.
|
||||
let prealloc_failure = match (*extra).wrapped_failure_pool.pop() {
|
||||
Some(index) => PreallocatedFailure::Existing(index),
|
||||
None => {
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_rotate(state, 1, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
impl PreallocatedFailure {
|
||||
unsafe fn reserve(state: *mut ffi::lua_State, extra: *mut ExtraData) -> Self {
|
||||
match (*extra).wrapped_failure_pool.pop() {
|
||||
Some(index) => PreallocatedFailure::Existing(index),
|
||||
None => {
|
||||
// We need to check stack for Luau in case when callback is called from interrupt
|
||||
// See https://github.com/Roblox/luau/issues/446 and mlua #142 and #153
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
// Place it to the beginning of the stack
|
||||
let ud = WrappedFailure::new_userdata(state);
|
||||
ffi::lua_insert(state, 1);
|
||||
PreallocatedFailure::New(ud)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let get_wrapped_failure = || match prealloc_failure {
|
||||
PreallocatedFailure::New(ud) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
unsafe fn r#use(
|
||||
&self,
|
||||
state: *mut ffi::lua_State,
|
||||
extra: *mut ExtraData,
|
||||
) -> *mut WrappedFailure {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match *self {
|
||||
PreallocatedFailure::New(ud) => {
|
||||
ffi::lua_settop(state, 1);
|
||||
ud
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
ffi::lua_settop(state, 0);
|
||||
#[cfg(feature = "luau")]
|
||||
ffi::lua_rawcheckstack(state, 2);
|
||||
ffi::lua_pushvalue(ref_thread, index);
|
||||
ffi::lua_xmove(ref_thread, state, 1);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
|
||||
}
|
||||
}
|
||||
}
|
||||
PreallocatedFailure::Existing(index) => {
|
||||
ffi::lua_settop(state, 0);
|
||||
#[cfg(feature = "luau")]
|
||||
assert_stack(state, 2);
|
||||
ffi::lua_pushvalue(ref_thread, index);
|
||||
ffi::lua_xmove(ref_thread, state, 1);
|
||||
ffi::lua_pushnil(ref_thread);
|
||||
ffi::lua_replace(ref_thread, index);
|
||||
(*extra).ref_free.push(index);
|
||||
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
|
||||
}
|
||||
};
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused `WrappedFailure` to the pool
|
||||
match prealloc_failure {
|
||||
unsafe fn release(self, state: *mut ffi::lua_State, extra: *mut ExtraData) {
|
||||
let ref_thread = (*extra).ref_thread;
|
||||
match self {
|
||||
PreallocatedFailure::New(_) => {
|
||||
if (*extra).wrapped_failure_pool.len() < WRAPPED_FAILURE_POOL_SIZE {
|
||||
ffi::lua_rotate(state, 1, -1);
|
||||
ffi::lua_xmove(state, ref_thread, 1);
|
||||
let index = ref_stack_pop(&mut *extra);
|
||||
let index = ref_stack_pop(extra);
|
||||
(*extra).wrapped_failure_pool.push(index);
|
||||
} else {
|
||||
ffi::lua_remove(state, 1);
|
||||
@@ -3174,10 +3225,21 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot shadow Rust errors with Lua ones, so we need to reserve pre-allocated memory
|
||||
// to store a wrapped failure (error or panic) *before* we proceed.
|
||||
let prealloc_failure = PreallocatedFailure::reserve(state, extra);
|
||||
|
||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||
Ok(Ok(r)) => {
|
||||
// Return unused `WrappedFailure` to the pool
|
||||
prealloc_failure.release(state, extra);
|
||||
r
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
let wrapped_error = get_wrapped_failure();
|
||||
let wrapped_error = prealloc_failure.r#use(state, extra);
|
||||
|
||||
// Build `CallbackError` with traceback
|
||||
let traceback = if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
|
||||
@@ -3199,7 +3261,7 @@ where
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
Err(p) => {
|
||||
let wrapped_panic = get_wrapped_failure();
|
||||
let wrapped_panic = prealloc_failure.r#use(state, extra);
|
||||
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
|
||||
get_gc_metatable::<WrappedFailure>(state);
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
@@ -3336,7 +3398,8 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn ref_stack_pop(extra: &mut ExtraData) -> c_int {
|
||||
unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
||||
let extra = &mut *extra;
|
||||
if let Some(free) = extra.ref_free.pop() {
|
||||
ffi::lua_replace(extra.ref_thread, free);
|
||||
return free;
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ impl Lua {
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
if let Some(version) = option_env!("LUAU_VERSION") {
|
||||
if let Some(version) = ffi::luau_version() {
|
||||
globals.raw_set("_VERSION", format!("Luau {version}"))?;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -103,7 +103,12 @@ macro_rules! protect_lua {
|
||||
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
|
||||
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
|
||||
$code;
|
||||
$nresults
|
||||
let nresults = $nresults;
|
||||
if nresults == ::ffi::LUA_MULTRET {
|
||||
ffi::lua_gettop($state_inner)
|
||||
} else {
|
||||
nresults
|
||||
}
|
||||
}
|
||||
|
||||
crate::util::protect_lua_call($state, $nargs, do_call)
|
||||
|
||||
+38
-22
@@ -14,6 +14,7 @@ use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::userdata_impl::UserDataRegistrar;
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
|
||||
take_userdata, StackGuard,
|
||||
@@ -24,7 +25,7 @@ use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Val
|
||||
use crate::userdata::USER_VALUE_MAXSLOT;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_core::future::Future;
|
||||
use std::future::Future;
|
||||
|
||||
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
|
||||
/// callbacks that are not required to be Send or 'static.
|
||||
@@ -32,7 +33,10 @@ use futures_core::future::Future;
|
||||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::scope`]: crate::Lua.html::scope
|
||||
pub struct Scope<'lua, 'scope> {
|
||||
pub struct Scope<'lua, 'scope>
|
||||
where
|
||||
'lua: 'scope,
|
||||
{
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||
@@ -393,7 +397,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(mem::transmute(lua))?)?;
|
||||
lua.push_value(f(lua, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
@@ -734,15 +738,16 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
||||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
meta_fields: Vec<(String, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
|
||||
meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
fn default() -> NonStaticUserDataFields<'lua, T> {
|
||||
NonStaticUserDataFields {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
@@ -751,6 +756,17 @@ impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -796,30 +812,30 @@ impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| {
|
||||
UserDataRegistrar::<()>::check_meta_field(lua, &name2, value.clone())
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
name,
|
||||
Box::new(move |lua, _| UserDataRegistrar::<()>::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+19
-24
@@ -1,21 +1,19 @@
|
||||
//! (De)Serialization support using serde.
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, ser::Serialize};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::lua::Lua;
|
||||
use crate::private::Sealed;
|
||||
use crate::table::Table;
|
||||
use crate::types::LightUserData;
|
||||
use crate::util::check_stack;
|
||||
use crate::value::Value;
|
||||
|
||||
/// Trait for serializing/deserializing Lua values using Serde.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
|
||||
pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
pub trait LuaSerdeExt: Sealed {
|
||||
/// A special value (lightuserdata) to encode/decode optional (none) values.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
@@ -37,7 +35,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn null(&'lua self) -> Value<'lua>;
|
||||
fn null(&self) -> Value;
|
||||
|
||||
/// A metatable attachable to a Lua table to systematically encode it as Array (instead of Map).
|
||||
/// As result, encoded Array will contain only sequence part of the table, with the same length
|
||||
@@ -68,7 +66,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn array_metatable(&'lua self) -> Table<'lua>;
|
||||
fn array_metatable(&self) -> Table;
|
||||
|
||||
/// Converts `T` into a [`Value`] instance.
|
||||
///
|
||||
@@ -101,7 +99,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value<T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
fn to_value<'lua, T: Serialize + ?Sized>(&'lua self, t: &T) -> Result<Value<'lua>>;
|
||||
|
||||
/// Converts `T` into a [`Value`] instance with options.
|
||||
///
|
||||
@@ -126,7 +124,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// "#).exec()
|
||||
/// }
|
||||
/// ```
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized;
|
||||
|
||||
@@ -159,7 +157,7 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
fn from_value<T: DeserializeOwned>(&self, value: Value) -> Result<T>;
|
||||
|
||||
/// Deserializes a [`Value`] into any serde deserializable object with options.
|
||||
///
|
||||
@@ -191,49 +189,46 @@ pub trait LuaSerdeExt<'lua>: Sealed {
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_value_with<T: Deserialize<'lua>>(
|
||||
&'lua self,
|
||||
value: Value<'lua>,
|
||||
options: de::Options,
|
||||
) -> Result<T>;
|
||||
fn from_value_with<T: DeserializeOwned>(&self, value: Value, options: de::Options)
|
||||
-> Result<T>;
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
fn null(&'lua self) -> Value<'lua> {
|
||||
Value::LightUserData(LightUserData(ptr::null_mut()))
|
||||
impl LuaSerdeExt for Lua {
|
||||
fn null(&self) -> Value {
|
||||
Value::NULL
|
||||
}
|
||||
|
||||
fn array_metatable(&'lua self) -> Table<'lua> {
|
||||
fn array_metatable(&self) -> Table {
|
||||
unsafe {
|
||||
push_array_metatable(self.ref_thread());
|
||||
Table(self.pop_ref_thread())
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value<T>(&'lua self, t: &T) -> Result<Value<'lua>>
|
||||
fn to_value<'lua, T>(&'lua self, t: &T) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new(self))
|
||||
}
|
||||
|
||||
fn to_value_with<T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
fn to_value_with<'lua, T>(&'lua self, t: &T, options: ser::Options) -> Result<Value<'lua>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
t.serialize(ser::Serializer::new_with_options(self, options))
|
||||
}
|
||||
|
||||
fn from_value<T>(&'lua self, value: Value<'lua>) -> Result<T>
|
||||
fn from_value<T>(&self, value: Value) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new(value))
|
||||
}
|
||||
|
||||
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
|
||||
fn from_value_with<T>(&self, value: Value, options: de::Options) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new_with_options(value, options))
|
||||
}
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@ use crate::util::{assert_stack, check_stack, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
|
||||
/// Handle to an internal Lua table.
|
||||
#[derive(Clone)]
|
||||
@@ -705,7 +705,7 @@ impl<'lua> Table<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "serialize"))]
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) fn raw_sequence_values_by_len<V: FromLua<'lua>>(
|
||||
self,
|
||||
len: Option<Integer>,
|
||||
|
||||
+11
-8
@@ -27,8 +27,9 @@ use {
|
||||
lua::ASYNC_POLL_PENDING,
|
||||
value::{MultiValue, Value},
|
||||
},
|
||||
futures_core::{future::Future, stream::Stream},
|
||||
futures_util::stream::Stream,
|
||||
std::{
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
pin::Pin,
|
||||
ptr::NonNull,
|
||||
@@ -232,8 +233,10 @@ impl<'lua> Thread<'lua> {
|
||||
lua.push_ref(&self.0);
|
||||
let thread_state = ffi::lua_tothread(state, -1);
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
#[cfg(all(feature = "lua54", feature = "vendored"))]
|
||||
let status = ffi::lua_closethread(thread_state, state);
|
||||
#[cfg(feature = "lua54")]
|
||||
if status != ffi::LUA_OK {
|
||||
return Err(pop_error(thread_state, status));
|
||||
@@ -248,8 +251,8 @@ impl<'lua> Thread<'lua> {
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
{
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(state, thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
// Inherit `LUA_GLOBALSINDEX` from the main thread
|
||||
ffi::lua_xpush(lua.main_state(), thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
|
||||
}
|
||||
|
||||
@@ -357,11 +360,8 @@ impl<'lua> Thread<'lua> {
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index);
|
||||
check_stack(thread, 1)?;
|
||||
check_stack(thread, 3)?;
|
||||
check_stack(state, 3)?;
|
||||
// Inherit `LUA_GLOBALSINDEX` from the caller
|
||||
ffi::lua_xpush(state, thread, ffi::LUA_GLOBALSINDEX);
|
||||
ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX);
|
||||
protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread))
|
||||
}
|
||||
}
|
||||
@@ -397,7 +397,10 @@ impl<'lua, R> Drop for AsyncThread<'lua, R> {
|
||||
#[cfg(feature = "lua54")]
|
||||
if self.thread.status() == ThreadStatus::Error {
|
||||
let thread_state = ffi::lua_tothread(lua.ref_thread(), self.thread.0.index);
|
||||
#[cfg(not(feature = "vendored"))]
|
||||
ffi::lua_resetthread(thread_state);
|
||||
#[cfg(feature = "vendored")]
|
||||
ffi::lua_closethread(thread_state, lua.state());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+151
-2
@@ -1,6 +1,9 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
@@ -8,8 +11,10 @@ use std::{fmt, mem, ptr};
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::ffi::CStr;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
use futures_util::future::LocalBoxFuture;
|
||||
|
||||
use crate::error::Result;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
@@ -286,6 +291,150 @@ impl LuaOwnedRef {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct AppData {
|
||||
#[cfg(not(feature = "send"))]
|
||||
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any>>>>,
|
||||
#[cfg(feature = "send")]
|
||||
container: UnsafeCell<FxHashMap<TypeId, RefCell<Box<dyn Any + Send>>>>,
|
||||
borrow: Cell<usize>,
|
||||
}
|
||||
|
||||
impl AppData {
|
||||
#[track_caller]
|
||||
pub(crate) fn insert<T: MaybeSend + 'static>(&self, data: T) -> Option<T> {
|
||||
match self.try_insert(data) {
|
||||
Ok(data) => data,
|
||||
Err(_) => panic!("cannot mutably borrow app data container"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_insert<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
|
||||
if self.borrow.get() != 0 {
|
||||
return Err(data);
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
Ok(unsafe { &mut *self.container.get() }
|
||||
.insert(TypeId::of::<T>(), RefCell::new(Box::new(data)))
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self) -> Option<AppDataRef<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRef {
|
||||
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow_mut();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRefMut {
|
||||
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn remove<T: 'static>(&self) -> Option<T> {
|
||||
if self.borrow.get() != 0 {
|
||||
panic!("cannot mutably borrow app data container");
|
||||
}
|
||||
// SAFETY: we checked that there are no other references to the container
|
||||
unsafe { &mut *self.container.get() }
|
||||
.remove(&TypeId::of::<T>())?
|
||||
.into_inner()
|
||||
.downcast::<T>()
|
||||
.ok()
|
||||
.map(|data| *data)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`Ref`].
|
||||
pub struct AppDataRef<'a, T: ?Sized + 'a> {
|
||||
data: Ref<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRef<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an app data container.
|
||||
///
|
||||
/// This type is similar to [`RefMut`].
|
||||
pub struct AppDataRefMut<'a, T: ?Sized + 'a> {
|
||||
data: RefMut<'a, T>,
|
||||
borrow: &'a Cell<usize>,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for AppDataRefMut<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.borrow.set(self.borrow.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for AppDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for AppDataRefMut<'_, T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Display> fmt::Display for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + fmt::Debug> fmt::Debug for AppDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(**self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
use super::*;
|
||||
|
||||
+68
-25
@@ -19,13 +19,12 @@ use {
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
use crate::UserDataRegistrar;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
|
||||
@@ -139,7 +138,8 @@ pub enum MetaMethod {
|
||||
/// Requires `feature = "lua54"`
|
||||
///
|
||||
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
||||
#[cfg(any(feature = "lua54"))]
|
||||
#[cfg(feature = "lua54")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
Close,
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ impl PartialEq<MetaMethod> for &str {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<MetaMethod> for String {
|
||||
impl PartialEq<MetaMethod> for StdString {
|
||||
fn eq(&self, other: &MetaMethod) -> bool {
|
||||
self == other.name()
|
||||
}
|
||||
@@ -410,24 +410,26 @@ pub trait UserDataMethods<'lua, T> {
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_callback(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, _name: String, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_meta_callback(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(&mut self, _name: String, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
fn append_methods_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataFields<'lua, T> {
|
||||
/// Add a static field to the `UserData`.
|
||||
///
|
||||
/// Static fields are implemented by updating the `__index` metamethod and returning the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
||||
///
|
||||
/// Static fields are usually shared between all instances of the `UserData` of the same type.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static;
|
||||
|
||||
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
||||
///
|
||||
/// Regular field getters are implemented by overriding the `__index` metamethod and returning the
|
||||
@@ -476,9 +478,21 @@ pub trait UserDataFields<'lua, T> {
|
||||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>;
|
||||
|
||||
/// Add a metamethod value computed from `f`.
|
||||
/// Add a metatable field.
|
||||
///
|
||||
/// This will initialize the metamethod value from `f` on `UserData` creation.
|
||||
/// This will initialize the metatable field with `value` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static;
|
||||
|
||||
/// Add a metatable field computed from `f`.
|
||||
///
|
||||
/// This will initialize the metatable field from `f` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
@@ -494,10 +508,7 @@ pub trait UserDataFields<'lua, T> {
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_getter(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_setter(&mut self, _name: String, _callback: Callback<'lua, 'static>) {}
|
||||
fn append_fields_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
|
||||
}
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
@@ -1044,6 +1055,30 @@ impl<'lua> AnyUserData<'lua> {
|
||||
OwnedAnyUserData(self.0.into_owned())
|
||||
}
|
||||
|
||||
/// Returns a type name of this `UserData` (from `__name` metatable field).
|
||||
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
let protect = !lua.unlikely_memory_error();
|
||||
let name_type = if protect {
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
|
||||
})?
|
||||
} else {
|
||||
ffi::luaL_getmetafield(state, -1, cstr!("__name"))
|
||||
};
|
||||
match name_type {
|
||||
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
|
||||
let other = other.as_ref();
|
||||
// Uses lua_rawequal() under the hood
|
||||
@@ -1152,6 +1187,14 @@ impl OwnedAnyUserData {
|
||||
// Reattach lifetime to &self
|
||||
Ok(unsafe { mem::transmute::<RefMut<T>, RefMut<T>>(t) })
|
||||
}
|
||||
|
||||
/// Takes the value out of this userdata.
|
||||
///
|
||||
/// This is a shortcut for [`AnyUserData::take()`]
|
||||
#[inline]
|
||||
pub fn take<T: 'static>(&self) -> Result<T> {
|
||||
self.to_ref().take()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a `UserData` metatable.
|
||||
@@ -1211,7 +1254,7 @@ impl<'lua, V> Iterator for UserDataMetatablePairs<'lua, V>
|
||||
where
|
||||
V: FromLua<'lua>,
|
||||
{
|
||||
type Item = Result<(String, V)>;
|
||||
type Item = Result<(StdString, V)>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ use crate::userdata::{AnyUserData, MetaMethod};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {futures_core::future::LocalBoxFuture, futures_util::future};
|
||||
use futures_util::future::{self, LocalBoxFuture};
|
||||
|
||||
/// An extension trait for [`AnyUserData`] that provides a variety of convenient functionality.
|
||||
pub trait AnyUserDataExt<'lua>: Sealed {
|
||||
|
||||
+72
-70
@@ -1,4 +1,4 @@
|
||||
use std::any::{self, TypeId};
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::string::String as StdString;
|
||||
@@ -10,8 +10,8 @@ use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{check_stack, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::util::{check_stack, get_userdata, short_type_name, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
@@ -25,13 +25,10 @@ use {
|
||||
|
||||
pub struct UserDataRegistrar<'lua, T: 'static> {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) meta_fields: Vec<(
|
||||
String,
|
||||
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
|
||||
)>,
|
||||
pub(crate) meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
@@ -47,6 +44,7 @@ pub struct UserDataRegistrar<'lua, T: 'static> {
|
||||
impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistrar {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
@@ -360,15 +358,49 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn check_meta_field<V>(
|
||||
lua: &'lua Lua,
|
||||
name: &str,
|
||||
value: V,
|
||||
) -> Result<MultiValue<'lua>>
|
||||
where
|
||||
V: IntoLua<'lua>,
|
||||
{
|
||||
let value = value.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.to_string(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
value.into_lua_multi(lua)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
fn get_function_name<T: 'static>(name: &str) -> StdString {
|
||||
let type_name = any::type_name::<T>().rsplit("::").next().unwrap();
|
||||
format!("{type_name}.{name}",)
|
||||
fn get_function_name<T>(name: &str) -> StdString {
|
||||
format!("{}.{name}", short_type_name::<T>())
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
@@ -409,41 +441,38 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
self.field_setters.push((name.into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, value.clone())),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
|
||||
// Below are internal methods
|
||||
|
||||
fn add_field_getter(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_setter(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.field_setters.push((name, callback));
|
||||
fn append_fields_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
|
||||
self.fields.extend(other.fields);
|
||||
self.field_getters.extend(other.field_getters);
|
||||
self.field_setters.extend(other.field_setters);
|
||||
self.meta_fields.extend(other.meta_fields);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,22 +621,13 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn add_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, name: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(&mut self, meta: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_meta_methods.push((meta, callback))
|
||||
fn append_methods_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
|
||||
self.methods.extend(other.methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_methods.extend(other.async_methods);
|
||||
self.meta_methods.extend(other.meta_methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_meta_methods.extend(other.async_meta_methods);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,31 +647,13 @@ macro_rules! lua_userdata_impl {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
let mut orig_fields = UserDataRegistrar::new();
|
||||
T::add_fields(&mut orig_fields);
|
||||
for (name, callback) in orig_fields.field_getters {
|
||||
fields.add_field_getter(name, callback);
|
||||
}
|
||||
for (name, callback) in orig_fields.field_setters {
|
||||
fields.add_field_setter(name, callback);
|
||||
}
|
||||
fields.append_fields_from(orig_fields);
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = UserDataRegistrar::new();
|
||||
T::add_methods(&mut orig_methods);
|
||||
for (name, callback) in orig_methods.methods {
|
||||
methods.add_callback(name, callback);
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (name, callback) in orig_methods.async_methods {
|
||||
methods.add_async_callback(name, callback);
|
||||
}
|
||||
for (meta, callback) in orig_methods.meta_methods {
|
||||
methods.add_meta_callback(meta, callback);
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (meta, callback) in orig_methods.async_meta_methods {
|
||||
methods.add_async_meta_callback(meta, callback);
|
||||
}
|
||||
methods.append_methods_from(orig_methods);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -13,6 +13,8 @@ use rustc_hash::FxHashMap;
|
||||
use crate::error::{Error, Result};
|
||||
use crate::memory::MemoryState;
|
||||
|
||||
pub(crate) use short_names::short_type_name;
|
||||
|
||||
static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
|
||||
let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
|
||||
crate::lua::init_metatable_cache(&mut map);
|
||||
@@ -400,9 +402,12 @@ unsafe extern "C" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let t = ffi::lua_type(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushboolean(state, (t == ffi::LUA_TFUNCTION) as c_int);
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
@@ -416,14 +421,19 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
||||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Fastpath to return methods table for index access
|
||||
if __index == nil and field_getters == nil then
|
||||
return methods
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
-- Alternatively return a function for index access
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
@@ -458,7 +468,13 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
||||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
@@ -508,6 +524,11 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
ffi::luau_codegen_compile(state, -1);
|
||||
}
|
||||
|
||||
// Store in the registry
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, newindex_key);
|
||||
@@ -1056,3 +1077,5 @@ static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
|
||||
static ERROR_PRINT_BUFFER_KEY: u8 = 0;
|
||||
static USERDATA_METATABLE_INDEX: u8 = 0;
|
||||
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
|
||||
|
||||
mod short_names;
|
||||
@@ -0,0 +1,84 @@
|
||||
//! Mostly copied from bevy_utils
|
||||
//! https://github.com/bevyengine/bevy/blob/main/crates/bevy_utils/src/short_names.rs
|
||||
|
||||
use std::any::type_name;
|
||||
|
||||
/// Returns a short version of a type name `T` without all module paths.
|
||||
///
|
||||
/// The short name of a type is its full name as returned by
|
||||
/// [`std::any::type_name`], but with the prefix of all paths removed. For
|
||||
/// example, the short name of `alloc::vec::Vec<core::option::Option<u32>>`
|
||||
/// would be `Vec<Option<u32>>`.
|
||||
pub(crate) fn short_type_name<T: ?Sized>() -> String {
|
||||
let full_name = type_name::<T>();
|
||||
|
||||
// Generics result in nested paths within <..> blocks.
|
||||
// Consider "core::option::Option<alloc::string::String>".
|
||||
// To tackle this, we parse the string from left to right, collapsing as we go.
|
||||
let mut index: usize = 0;
|
||||
let end_of_string = full_name.len();
|
||||
let mut parsed_name = String::new();
|
||||
|
||||
while index < end_of_string {
|
||||
let rest_of_string = full_name.get(index..end_of_string).unwrap_or_default();
|
||||
|
||||
// Collapse everything up to the next special character,
|
||||
// then skip over it
|
||||
if let Some(special_character_index) = rest_of_string
|
||||
.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
|
||||
{
|
||||
let segment_to_collapse = rest_of_string
|
||||
.get(0..special_character_index)
|
||||
.unwrap_or_default();
|
||||
parsed_name += collapse_type_name(segment_to_collapse);
|
||||
// Insert the special character
|
||||
let special_character =
|
||||
&rest_of_string[special_character_index..=special_character_index];
|
||||
parsed_name.push_str(special_character);
|
||||
|
||||
match special_character {
|
||||
">" | ")" | "]"
|
||||
if rest_of_string[special_character_index + 1..].starts_with("::") =>
|
||||
{
|
||||
parsed_name.push_str("::");
|
||||
// Move the index past the "::"
|
||||
index += special_character_index + 3;
|
||||
}
|
||||
// Move the index just past the special character
|
||||
_ => index += special_character_index + 1,
|
||||
}
|
||||
} else {
|
||||
// If there are no special characters left, we're done!
|
||||
parsed_name += collapse_type_name(rest_of_string);
|
||||
index = end_of_string;
|
||||
}
|
||||
}
|
||||
parsed_name
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn collapse_type_name(string: &str) -> &str {
|
||||
string.rsplit("::").next().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::short_type_name;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn tests() {
|
||||
assert_eq!(short_type_name::<String>(), "String");
|
||||
assert_eq!(short_type_name::<Option<String>>(), "Option<String>");
|
||||
assert_eq!(short_type_name::<(String, &str)>(), "(String, &str)");
|
||||
assert_eq!(short_type_name::<[i32; 3]>(), "[i32; 3]");
|
||||
assert_eq!(
|
||||
short_type_name::<HashMap<String, Option<[i32; 3]>>>(),
|
||||
"HashMap<String, Option<[i32; 3]>>"
|
||||
);
|
||||
assert_eq!(
|
||||
short_type_name::<dyn Fn(i32) -> i32>(),
|
||||
"dyn Fn(i32) -> i32"
|
||||
);
|
||||
}
|
||||
}
|
||||
+50
-7
@@ -3,6 +3,7 @@ use std::collections::HashSet;
|
||||
use std::iter::{self, FromIterator};
|
||||
use std::ops::Index;
|
||||
use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, ptr, slice, str, vec};
|
||||
|
||||
@@ -21,6 +22,7 @@ use crate::table::Table;
|
||||
use crate::thread::Thread;
|
||||
use crate::types::{Integer, LightUserData, Number};
|
||||
use crate::userdata::AnyUserData;
|
||||
use crate::util::{check_stack, StackGuard};
|
||||
|
||||
/// 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
|
||||
@@ -63,6 +65,12 @@ pub enum Value<'lua> {
|
||||
pub use self::Value::Nil;
|
||||
|
||||
impl<'lua> Value<'lua> {
|
||||
/// A special value (lightuserdata) to represent null value.
|
||||
///
|
||||
/// It can be used in Lua tables without downsides of `nil`.
|
||||
pub const NULL: Value<'static> = Value::LightUserData(LightUserData(ptr::null_mut()));
|
||||
|
||||
/// Returns type name of this value.
|
||||
pub const fn type_name(&self) -> &'static str {
|
||||
match *self {
|
||||
Value::Nil => "nil",
|
||||
@@ -123,6 +131,38 @@ impl<'lua> Value<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the value to a string.
|
||||
///
|
||||
/// If the value has a metatable with a `__tostring` method, then it will be called to get the result.
|
||||
pub fn to_string(&self) -> Result<StdString> {
|
||||
match self {
|
||||
Value::Nil => Ok("nil".to_string()),
|
||||
Value::Boolean(b) => Ok(b.to_string()),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => Ok("null".to_string()),
|
||||
Value::LightUserData(ud) => Ok(format!("lightuserdata: {:p}", ud.0)),
|
||||
Value::Integer(i) => Ok(i.to_string()),
|
||||
Value::Number(n) => Ok(n.to_string()),
|
||||
#[cfg(feature = "luau")]
|
||||
Value::Vector(x, y, z) => Ok(format!("vector({x}, {y}, {z})")),
|
||||
Value::String(s) => Ok(s.to_str()?.to_string()),
|
||||
Value::Table(Table(r))
|
||||
| Value::Function(Function(r))
|
||||
| Value::Thread(Thread(r))
|
||||
| Value::UserData(AnyUserData(r)) => unsafe {
|
||||
let state = r.lua.state();
|
||||
let _guard = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
r.lua.push_ref(r);
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::luaL_tolstring(state, -1, ptr::null_mut());
|
||||
})?;
|
||||
Ok(String(r.lua.pop_ref()).to_str()?.to_string())
|
||||
},
|
||||
Value::Error(err) => Ok(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
// Compares two values.
|
||||
// Used to sort values for Debug printing.
|
||||
pub(crate) fn cmp(&self, other: &Self) -> Ordering {
|
||||
@@ -174,7 +214,7 @@ impl<'lua> Value<'lua> {
|
||||
Value::Nil => write!(fmt, "nil"),
|
||||
Value::Boolean(b) => write!(fmt, "{b}"),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => write!(fmt, "null"),
|
||||
Value::LightUserData(ud) => write!(fmt, "<lightuserdata {:?}>", ud.0),
|
||||
Value::LightUserData(ud) => write!(fmt, "lightuserdata: {:?}", ud.0),
|
||||
Value::Integer(i) => write!(fmt, "{i}"),
|
||||
Value::Number(n) => write!(fmt, "{n}"),
|
||||
#[cfg(feature = "luau")]
|
||||
@@ -183,13 +223,16 @@ impl<'lua> Value<'lua> {
|
||||
Value::Table(t) if recursive && !visited.contains(&t.to_pointer()) => {
|
||||
t.fmt_pretty(fmt, ident, visited)
|
||||
}
|
||||
t @ Value::Table(_) => write!(fmt, "<table {:?}>", t.to_pointer()),
|
||||
f @ Value::Function(_) => write!(fmt, "<function {:?}>", f.to_pointer()),
|
||||
t @ Value::Thread(_) => write!(fmt, "<thread {:?}>", t.to_pointer()),
|
||||
// TODO: Show type name for registered userdata
|
||||
u @ Value::UserData(_) => write!(fmt, "<userdata {:?}>", u.to_pointer()),
|
||||
t @ Value::Table(_) => write!(fmt, "table: {:?}", t.to_pointer()),
|
||||
f @ Value::Function(_) => write!(fmt, "function: {:?}", f.to_pointer()),
|
||||
t @ Value::Thread(_) => write!(fmt, "thread: {:?}", t.to_pointer()),
|
||||
u @ Value::UserData(ud) => {
|
||||
let name = ud.type_name().ok().flatten();
|
||||
let name = name.unwrap_or_else(|| "userdata".to_string());
|
||||
write!(fmt, "{name}: {:?}", u.to_pointer())
|
||||
}
|
||||
Value::Error(e) if recursive => write!(fmt, "{e:?}"),
|
||||
Value::Error(_) => write!(fmt, "<error>"),
|
||||
Value::Error(_) => write!(fmt, "error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-1
@@ -1,7 +1,7 @@
|
||||
#![cfg(feature = "async")]
|
||||
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use futures_timer::Delay;
|
||||
@@ -502,3 +502,25 @@ async fn test_owned_async_call() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_terminate() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let mutex = Arc::new(Mutex::new(0u32));
|
||||
let mutex2 = mutex.clone();
|
||||
let func = lua.create_async_function(move |_, ()| {
|
||||
let mutex = mutex2.clone();
|
||||
async move {
|
||||
let _guard = mutex.lock();
|
||||
Delay::new(Duration::from_millis(100)).await;
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
|
||||
let _ = tokio::time::timeout(Duration::from_millis(30), func.call_async::<_, ()>(())).await;
|
||||
lua.gc_collect()?;
|
||||
assert!(mutex.try_lock().is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ fn test_debug_format() -> Result<()> {
|
||||
// Globals
|
||||
let globals = lua.globals();
|
||||
let dump = format!("{globals:#?}");
|
||||
assert!(dump.starts_with("{\n [\"_G\"] = <table"));
|
||||
assert!(dump.starts_with("{\n [\"_G\"] = table:"));
|
||||
|
||||
// TODO: Other cases
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::io;
|
||||
|
||||
use mlua::{Error, ErrorContext, Lua, Result};
|
||||
|
||||
#[test]
|
||||
@@ -29,5 +31,20 @@ fn test_error_context() -> Result<()> {
|
||||
println!("{msg2}");
|
||||
assert!(msg2.contains("error converting Lua nil to String"));
|
||||
|
||||
// Rewrite context message and test `downcast_ref`
|
||||
let func3 = lua.create_function(|_, ()| {
|
||||
Err::<(), _>(Error::external(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"other",
|
||||
)))
|
||||
.context("some context")
|
||||
.context("some new context")
|
||||
})?;
|
||||
let res = func3.call::<_, ()>(()).err().unwrap();
|
||||
let Error::CallbackError { cause, .. } = &res else { unreachable!() };
|
||||
assert!(!res.to_string().contains("some context"));
|
||||
assert!(res.to_string().contains("some new context"));
|
||||
assert!(cause.downcast_ref::<io::Error>().is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+75
-12
@@ -1,4 +1,4 @@
|
||||
use mlua::{Function, Lua, Result, String};
|
||||
use mlua::{Function, Lua, Result, String, Table};
|
||||
|
||||
#[test]
|
||||
fn test_function() -> Result<()> {
|
||||
@@ -114,6 +114,66 @@ fn test_dump() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_environment() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// We must not get or set environment for C functions
|
||||
let rust_func = lua.create_function(|_, ()| Ok("hello"))?;
|
||||
assert_eq!(rust_func.environment(), None);
|
||||
assert_eq!(rust_func.set_environment(lua.globals()).ok(), Some(false));
|
||||
|
||||
// Test getting Lua function environment
|
||||
lua.globals().set("hello", "global")?;
|
||||
let lua_func = lua
|
||||
.load(
|
||||
r#"
|
||||
local t = ""
|
||||
return function()
|
||||
-- two upvalues
|
||||
return t .. hello
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.eval::<Function>()?;
|
||||
let lua_func2 = lua.load("return hello").into_function()?;
|
||||
assert_eq!(lua_func.call::<_, String>(())?, "global");
|
||||
assert_eq!(lua_func.environment(), Some(lua.globals()));
|
||||
|
||||
// Test changing the environment
|
||||
let env = lua.create_table_from([("hello", "local")])?;
|
||||
assert!(lua_func.set_environment(env.clone())?);
|
||||
assert_eq!(lua_func.call::<_, String>(())?, "local");
|
||||
assert_eq!(lua_func2.call::<_, String>(())?, "global");
|
||||
|
||||
// More complex case
|
||||
lua.load(
|
||||
r#"
|
||||
local number = 15
|
||||
function lucky() return tostring("number is "..number) end
|
||||
new_env = {
|
||||
tostring = function() return tostring(number) end,
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
let lucky = lua.globals().get::<_, Function>("lucky")?;
|
||||
assert_eq!(lucky.call::<_, String>(())?, "number is 15");
|
||||
let new_env = lua.globals().get::<_, Table>("new_env")?;
|
||||
lucky.set_environment(new_env)?;
|
||||
assert_eq!(lucky.call::<_, String>(())?, "15");
|
||||
|
||||
// Test inheritance
|
||||
let lua_func2 = lua
|
||||
.load(r#"return function() return (function() return hello end)() end"#)
|
||||
.eval::<Function>()?;
|
||||
assert!(lua_func2.set_environment(env.clone())?);
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(lua_func2.call::<_, String>(())?, "local");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_info() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
@@ -135,34 +195,37 @@ fn test_function_info() -> Result<()> {
|
||||
|
||||
let function1_info = function1.info();
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function1_info.name, Some(b"function1".to_vec()));
|
||||
assert_eq!(function1_info.source, Some(b"source1".to_vec()));
|
||||
assert_eq!(function1_info.name.as_deref(), Some("function1"));
|
||||
assert_eq!(function1_info.source.as_deref(), Some(b"source1".as_ref()));
|
||||
assert_eq!(function1_info.line_defined, 2);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function1_info.last_line_defined, 4);
|
||||
assert_eq!(function1_info.what, Some(b"Lua".to_vec()));
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function1_info.last_line_defined, -1);
|
||||
assert_eq!(function1_info.what.as_deref(), Some("Lua"));
|
||||
|
||||
let function2_info = function2.info();
|
||||
assert_eq!(function2_info.name, None);
|
||||
assert_eq!(function2_info.source, Some(b"source1".to_vec()));
|
||||
assert_eq!(function2_info.source.as_deref(), Some(b"source1".as_ref()));
|
||||
assert_eq!(function2_info.line_defined, 3);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function2_info.last_line_defined, 3);
|
||||
assert_eq!(function2_info.what, Some(b"Lua".to_vec()));
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(function2_info.last_line_defined, -1);
|
||||
assert_eq!(function2_info.what.as_deref(), Some("Lua"));
|
||||
|
||||
let function3_info = function3.info();
|
||||
assert_eq!(function3_info.name, None);
|
||||
assert_eq!(function3_info.source, Some(b"=[C]".to_vec()));
|
||||
assert_eq!(function3_info.source.as_deref(), Some(b"=[C]".as_ref()));
|
||||
assert_eq!(function3_info.line_defined, -1);
|
||||
#[cfg(not(feature = "luau"))]
|
||||
assert_eq!(function3_info.last_line_defined, -1);
|
||||
assert_eq!(function3_info.what, Some(b"C".to_vec()));
|
||||
assert_eq!(function3_info.what.as_deref(), Some("C"));
|
||||
|
||||
let print_info = globals.get::<_, Function>("print")?.info();
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(print_info.name, Some(b"print".to_vec()));
|
||||
assert_eq!(print_info.source, Some(b"=[C]".to_vec()));
|
||||
assert_eq!(print_info.what, Some(b"C".to_vec()));
|
||||
assert_eq!(print_info.name.as_deref(), Some("print"));
|
||||
assert_eq!(print_info.source.as_deref(), Some(b"=[C]".as_ref()));
|
||||
assert_eq!(print_info.what.as_deref(), Some("C"));
|
||||
assert_eq!(print_info.line_defined, -1);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -9,6 +9,16 @@ use std::sync::Arc;
|
||||
|
||||
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
|
||||
|
||||
#[test]
|
||||
fn test_version() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
assert!(lua
|
||||
.globals()
|
||||
.get::<_, String>("_VERSION")?
|
||||
.starts_with("Luau 0."));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_require() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
+37
-9
@@ -887,19 +887,47 @@ fn test_application_data() -> Result<()> {
|
||||
lua.set_app_data("test1");
|
||||
lua.set_app_data(vec!["test2"]);
|
||||
|
||||
// Borrow &str immutably and Vec<&str> mutably
|
||||
let s = lua.app_data_ref::<&str>().unwrap();
|
||||
let mut v = lua.app_data_mut::<Vec<&str>>().unwrap();
|
||||
v.push("test3");
|
||||
|
||||
// Insert of new data or removal should fail now
|
||||
assert!(lua.try_set_app_data::<i32>(123).is_err());
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.set_app_data::<i32>(123))) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.remove_app_data::<i32>())) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
// Check display and debug impls
|
||||
assert_eq!(format!("{s}"), "test1");
|
||||
assert_eq!(format!("{s:?}"), "\"test1\"");
|
||||
|
||||
// Borrowing immutably and mutably of the same type is not allowed
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.app_data_mut::<&str>().unwrap())) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
drop((s, v));
|
||||
|
||||
// Test that application data is accessible from anywhere
|
||||
let f = lua.create_function(|lua, ()| {
|
||||
{
|
||||
let data1 = lua.app_data_ref::<&str>().unwrap();
|
||||
assert_eq!(*data1, "test1");
|
||||
}
|
||||
let mut data2 = lua.app_data_mut::<Vec<&str>>().unwrap();
|
||||
assert_eq!(*data2, vec!["test2"]);
|
||||
data2.push("test3");
|
||||
let mut data1 = lua.app_data_mut::<&str>().unwrap();
|
||||
assert_eq!(*data1, "test1");
|
||||
*data1 = "test4";
|
||||
|
||||
let data2 = lua.app_data_ref::<Vec<&str>>().unwrap();
|
||||
assert_eq!(*data2, vec!["test2", "test3"]);
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
f.call(())?;
|
||||
|
||||
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test1");
|
||||
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test4");
|
||||
assert_eq!(
|
||||
*lua.app_data_ref::<Vec<&str>>().unwrap(),
|
||||
vec!["test2", "test3"]
|
||||
@@ -991,7 +1019,7 @@ fn test_ref_stack_exhaustion() {
|
||||
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
let mut vals = Vec::new();
|
||||
for _ in 0..1000000 {
|
||||
for _ in 0..10000000 {
|
||||
vals.push(lua.create_table()?);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
+32
-27
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
#[cfg(not(feature = "parking_lot"))]
|
||||
@@ -486,6 +487,7 @@ fn test_fields() -> Result<()> {
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0 = val;
|
||||
@@ -497,11 +499,7 @@ fn test_fields() -> Result<()> {
|
||||
fields
|
||||
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
|
||||
|
||||
fields.add_meta_field_with(MetaMethod::Index, |lua| {
|
||||
let index = lua.create_table()?;
|
||||
index.set("f", 321)?;
|
||||
Ok(index)
|
||||
});
|
||||
fields.add_meta_field(MetaMethod::Index, HashMap::from([("f", 321)]));
|
||||
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||
lua.create_function(|lua, (_, field, val): (AnyUserData, String, Value)| {
|
||||
lua.globals().set(field, val)?;
|
||||
@@ -516,6 +514,7 @@ fn test_fields() -> Result<()> {
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.val == 7)
|
||||
ud.val = 10
|
||||
assert(ud.val == 10)
|
||||
@@ -538,30 +537,22 @@ fn test_fields() -> Result<()> {
|
||||
#[test]
|
||||
fn test_metatable() -> Result<()> {
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData(i64);
|
||||
struct MyUserData;
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__type_name", |_| Ok("MyUserData"));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("my_type_name", |_, data: AnyUserData| {
|
||||
let metatable = data.get_metatable()?;
|
||||
metatable.get::<String>("__type_name")
|
||||
metatable.get::<String>("__name")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud:my_type_name() == "MyUserData")
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("ud", MyUserData)?;
|
||||
lua.load(r#"assert(ud:my_type_name() == "MyUserData")"#)
|
||||
.exec()?;
|
||||
|
||||
let ud: AnyUserData = globals.get("ud")?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
@@ -583,10 +574,10 @@ fn test_metatable() -> Result<()> {
|
||||
.map(|kv: Result<(_, Value)>| Ok(kv?.0))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
methods.sort();
|
||||
assert_eq!(methods, vec!["__index", "__type_name"]);
|
||||
assert_eq!(methods, vec!["__index", "__name"]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData2(i64);
|
||||
struct MyUserData2;
|
||||
|
||||
impl UserData for MyUserData2 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
@@ -594,12 +585,25 @@ fn test_metatable() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
match lua.create_userdata(MyUserData2(1)) {
|
||||
match lua.create_userdata(MyUserData2) {
|
||||
Ok(_) => panic!("expected MetaMethodTypeError, got no error"),
|
||||
Err(Error::MetaMethodTypeError { .. }) => {}
|
||||
Err(e) => panic!("expected MetaMethodTypeError, got {:?}", e),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct MyUserData3;
|
||||
|
||||
impl UserData for MyUserData3 {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_meta_field_with("__name", |_| Ok("CustomName"));
|
||||
}
|
||||
}
|
||||
|
||||
let ud = lua.create_userdata(MyUserData3)?;
|
||||
let metatable = ud.get_metatable()?;
|
||||
assert_eq!(metatable.get::<String>("__name")?.to_str()?, "CustomName");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -609,6 +613,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
@@ -626,6 +631,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
globals.set("rc_refcell_ud", ud1.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_refcell_ud.static == "constant")
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 2)
|
||||
"#,
|
||||
@@ -641,6 +647,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
globals.set("arc_mutex_ud", ud2.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_mutex_ud.static == "constant")
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 3)
|
||||
"#,
|
||||
@@ -655,6 +662,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
||||
globals.set("arc_rwlock_ud", ud3.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_rwlock_ud.static == "constant")
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 4)
|
||||
"#,
|
||||
@@ -681,7 +689,7 @@ fn test_userdata_proxy() -> Result<()> {
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_function_get("static_field", |_, _| Ok(123));
|
||||
fields.add_field("static_field", 123);
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
}
|
||||
|
||||
@@ -775,10 +783,7 @@ fn test_userdata_ext() -> Result<()> {
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 123);
|
||||
ud.set("n", 321)?;
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 321);
|
||||
match ud.get::<_, u32>("non-existent") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
assert_eq!(ud.get::<_, Option<u32>>("non-existent")?, None);
|
||||
match ud.set::<_, u32>("non-existent", 123) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
@@ -831,11 +836,11 @@ fn test_owned_userdata() -> Result<()> {
|
||||
|
||||
assert_eq!(*ud.borrow::<&str>()?, "abc");
|
||||
*ud.borrow_mut()? = "cba";
|
||||
assert_eq!(*ud.to_ref().borrow::<&str>()?, "cba");
|
||||
assert!(matches!(
|
||||
ud.borrow::<i64>(),
|
||||
Err(Error::UserDataTypeMismatch)
|
||||
));
|
||||
assert_eq!(ud.take::<&str>()?, "cba");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+73
-1
@@ -1,6 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use mlua::{Lua, MultiValue, Result, Value};
|
||||
use mlua::{Error, LightUserData, Lua, MultiValue, Result, UserData, UserDataMethods, Value};
|
||||
|
||||
#[test]
|
||||
fn test_value_eq() -> Result<()> {
|
||||
@@ -28,6 +31,7 @@ fn test_value_eq() -> Result<()> {
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("null", Value::NULL)?;
|
||||
|
||||
let table1: Value = globals.get("table1")?;
|
||||
let table2: Value = globals.get("table2")?;
|
||||
@@ -41,6 +45,7 @@ fn test_value_eq() -> Result<()> {
|
||||
let func3: Value = globals.get("func3")?;
|
||||
let thread1: Value = globals.get("thread1")?;
|
||||
let thread2: Value = globals.get("thread2")?;
|
||||
let null: Value = globals.get("null")?;
|
||||
|
||||
assert!(table1 != table2);
|
||||
assert!(table1.equals(&table2)?);
|
||||
@@ -54,6 +59,7 @@ fn test_value_eq() -> Result<()> {
|
||||
assert!(!func1.equals(&func3)?);
|
||||
assert!(thread1 == thread2);
|
||||
assert!(thread1.equals(&thread2)?);
|
||||
assert!(null == Value::NULL);
|
||||
|
||||
assert!(!table1.to_pointer().is_null());
|
||||
assert!(!ptr::eq(table1.to_pointer(), table2.to_pointer()));
|
||||
@@ -81,3 +87,69 @@ fn test_multi_value() {
|
||||
multi_value.clear();
|
||||
assert!(multi_value.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_to_string() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
assert_eq!(Value::Nil.to_string()?, "nil");
|
||||
assert_eq!(Value::Boolean(true).to_string()?, "true");
|
||||
assert_eq!(Value::NULL.to_string()?, "null");
|
||||
assert_eq!(
|
||||
Value::LightUserData(LightUserData(0x1 as *const c_void as *mut _)).to_string()?,
|
||||
"lightuserdata: 0x1"
|
||||
);
|
||||
assert_eq!(Value::Integer(1).to_string()?, "1");
|
||||
assert_eq!(Value::Number(34.59).to_string()?, "34.59");
|
||||
#[cfg(feature = "luau")]
|
||||
assert_eq!(
|
||||
Value::Vector(10.0, 11.1, 12.2).to_string()?,
|
||||
"vector(10, 11.1, 12.2)"
|
||||
);
|
||||
assert_eq!(
|
||||
Value::String(lua.create_string("hello")?).to_string()?,
|
||||
"hello"
|
||||
);
|
||||
|
||||
let table: Value = lua.load("{}").eval()?;
|
||||
assert!(table.to_string()?.starts_with("table:"));
|
||||
let table: Value = lua
|
||||
.load("setmetatable({}, {__tostring = function() return 'test table' end})")
|
||||
.eval()?;
|
||||
assert_eq!(table.to_string()?, "test table");
|
||||
|
||||
let func: Value = lua.load("function() end").eval()?;
|
||||
assert!(func.to_string()?.starts_with("function:"));
|
||||
|
||||
let thread: Value = lua.load("coroutine.create(function() end)").eval()?;
|
||||
assert!(thread.to_string()?.starts_with("thread:"));
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, this, ()| Ok(this.clone()));
|
||||
})?;
|
||||
let ud: Value = Value::UserData(lua.create_any_userdata(String::from("string userdata"))?);
|
||||
assert_eq!(ud.to_string()?, "string userdata");
|
||||
|
||||
struct MyUserData;
|
||||
impl UserData for MyUserData {}
|
||||
let ud: Value = Value::UserData(lua.create_userdata(MyUserData)?);
|
||||
assert!(ud.to_string()?.starts_with("MyUserData:"));
|
||||
|
||||
let err = Value::Error(Error::RuntimeError("test error".to_string()));
|
||||
assert_eq!(err.to_string()?, "runtime error: test error");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug_format() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<HashMap<i32, StdString>>(|_| {})?;
|
||||
let ud = lua
|
||||
.create_any_userdata::<HashMap<i32, StdString>>(HashMap::new())
|
||||
.map(Value::UserData)?;
|
||||
assert!(format!("{ud:#?}").starts_with("HashMap<i32, String>:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user