Compare commits

...

29 Commits

Author SHA1 Message Date
Alex Orlenko d9ac7abff2 v0.8.0 2022-06-08 23:57:35 +01:00
Alex Orlenko 48aa97351d Add Sandboxing section to README 2022-06-08 23:56:26 +01:00
Alex Orlenko 4516ca0bb5 Forgotten part of userdata performance optimization for Lua 5.1 2022-06-07 17:59:22 +01:00
Alex Orlenko 9005f32a98 Add ffi::luau::lua_tolightuserdata introduced in Luau 0.528 2022-06-06 22:40:29 +01:00
Alex Orlenko 93d36b9068 More performance optimization (userdata part) 2022-06-06 21:42:55 +01:00
Alex Orlenko f9f32bffce Update bundled luau/jit versions 2022-06-06 15:59:28 +01:00
Alex Orlenko da5010e6de Switch to 2021 edition and bump MSRV to 1.56+ 2022-06-06 15:40:29 +01:00
Alex Orlenko 4afca8f5bb Prepare for v0.8.0 release 2022-06-02 22:44:07 +01:00
Alex Orlenko b065db37c2 Always use Rust allocator for Lua 5.1/u 2022-05-31 02:09:58 +01:00
Alex Orlenko a100ebb3d7 Fix luaL_requiref glb handling for Lua<=5.1 2022-05-30 21:45:25 +01:00
Alex Orlenko a05a58b258 Update integer/number coercion logic 2022-05-30 19:26:12 +01:00
Alex Orlenko 3766cee4dd Experimental optimization to skip oom check 2022-05-30 18:42:34 +01:00
Alex Orlenko 99becdb590 More inline attributes 2022-05-29 20:17:09 +01:00
Alex Orlenko 799b4e9f0d Make Variadic::new and MultiValue::new const 2022-05-28 21:13:49 +01:00
Alex Orlenko 93f0b2a5de Update type of lua_Integer for lua51/52 2022-05-27 12:26:56 +01:00
Alex Orlenko 62db3adde1 Add "send" test 2022-05-26 22:22:23 +01:00
Alex Orlenko afa343ff08 Add [track_caller] attribute when working with app data container 2022-05-26 20:16:09 +01:00
Alex Orlenko 4f06f614a9 v0.8.0-beta.5 2022-05-25 01:47:38 +01:00
Alex Orlenko 30ba616a8a Correctly deserialize newtype struct (#168) 2022-05-24 23:26:17 +01:00
Alex Orlenko 0076aa735a Add Function::coverage for Luau to obtain coverage report 2022-05-24 02:01:46 +01:00
Alex Orlenko bcf2cbea37 Add Value::to_pointer() function.
Closes #165 and #166.
2022-05-18 13:15:08 +01:00
Alex Orlenko 8cd594c609 Fix typo 2022-05-18 12:15:39 +01:00
Alex Orlenko 8d71ea79aa Cache bytecode for internal Lua chunks 2022-05-16 20:29:02 +01:00
Alex Orlenko 2a8c5c7f82 Refactor Function::bind implementation.
Make it possible to bind async function arguments.
Fixes #161
2022-05-15 01:15:31 +01:00
Alex Orlenko 6b2ceb60c4 Fix Send impl 2022-05-12 19:24:12 +01:00
Alex Orlenko 3aac7131ee Add Iter metamethod for Luau (available from 0.526) 2022-05-09 14:34:10 +01:00
Alex Orlenko 1807fa789c Don't require Lua sources for modules in build system 2022-05-07 22:16:54 +01:00
Alex Orlenko 7db7526a08 Update to Luau 0.526 2022-05-07 02:08:50 +01:00
Alex Orlenko bea9b0f8b5 Assert stack for Luau in callback_error_ext when operating on a cached wrapped failure.
Should solve #153.
2022-04-27 00:20:25 +01:00
41 changed files with 763 additions and 235 deletions
+5 -5
View File
@@ -39,7 +39,7 @@ jobs:
build_aarch64_cross_macos:
name: Cross-compile to aarch64-apple-darwin
runs-on: macos-11.0
runs-on: macos-latest
needs: build
strategy:
matrix:
@@ -186,8 +186,8 @@ jobs:
- uses: Swatinem/rust-cache@v1
- name: Run ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }},vendored")
(cd tests/module && cargo test --release --features "${{ matrix.lua }},vendored")
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }},vendored")
shell: bash
test_modules_windows:
@@ -208,8 +208,8 @@ jobs:
pacman -S --noconfirm mingw-w64-x86_64-rust mingw-w64-x86_64-lua mingw-w64-x86_64-luajit mingw-w64-x86_64-pkg-config
- name: Run ${{ matrix.lua }} module tests
run: |
(cd examples/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module && cargo test --release --features "${{ matrix.lua }}")
(cd tests/module && cargo build --release --features "${{ matrix.lua }}")
(cd tests/module/loader && cargo test --release --features "${{ matrix.lua }}")
rustfmt:
name: Rustfmt
+22
View File
@@ -1,3 +1,25 @@
## v0.8.0
Changes since 0.7.4
- Roblox Luau support
- Removed C glue
- Added async support to `__index` and `__newindex` metamethods
- Added `Function::info()` to get information about functions (#149).
- Added `parking_lot` dependency under feature flag (for `UserData`)
- `Hash` implementation for Lua String
- Added `Value::to_pointer()` function
- Performance improvements
Breaking changes:
- Refactored `AsChunk` trait (added implementation for `Path` and `PathBuf`).
## v0.8.0-beta.5
- Lua sources no longer needed to build modules
- Added `__iter` metamethod for Luau
- Added `Value::to_pointer()` function
- Added `Function::coverage` for Luau to obtain coverage report
- Bugfixes and improvements (#153 #161 #168)
## v0.8.0-beta.4
- Removed `&Lua` from `Lua::set_interrupt` as it's not safe (introduced in v0.8.0-beta.3)
+6 -8
View File
@@ -1,12 +1,12 @@
[package]
name = "mlua"
version = "0.8.0-beta.4" # remember to update html_root_url and mlua_derive
version = "0.8.0" # remember to update html_root_url and mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018"
edition = "2021"
repository = "https://github.com/khvzak/mlua"
documentation = "https://docs.rs/mlua"
readme = "README.md"
keywords = ["lua", "luajit", "async", "futures", "scripting"]
keywords = ["lua", "luajit", "luau", "async", "scripting"]
categories = ["api-bindings", "asynchronous"]
license = "MIT"
links = "lua"
@@ -23,8 +23,6 @@ rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
"examples/module",
"tests/module",
]
[features]
@@ -43,7 +41,7 @@ serialize = ["serde", "erased-serde"]
macros = ["mlua_derive/macros"]
[dependencies]
mlua_derive = { version = "=0.8.0-beta.1", optional = true, path = "mlua_derive" }
mlua_derive = { version = "=0.8.0", optional = true, path = "mlua_derive" }
bstr = { version = "0.2", features = ["std"], default_features = false }
once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" }
@@ -59,8 +57,8 @@ parking_lot = { version = "0.12", optional = true }
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
luau0-src = { version = "0.2.2", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.3.2", optional = true }
[dev-dependencies]
rustyline = "9.0"
+11 -3
View File
@@ -9,7 +9,7 @@
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/khvzak/mlua
[MSRV]: https://img.shields.io/badge/rust-1.53+-brightgreen.svg?&logo=rust
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
[Guided Tour](examples/guided_tour.rs)
@@ -106,7 +106,7 @@ Add to `Cargo.toml` :
``` toml
[dependencies]
mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored"] }
mlua = { version = "0.8", features = ["lua54", "vendored"] }
```
`main.rs`
@@ -141,7 +141,7 @@ Add to `Cargo.toml` :
crate-type = ["cdylib"]
[dependencies]
mlua = { version = "0.8.0-beta.4", features = ["lua54", "vendored", "module"] }
mlua = { version = "0.8", features = ["lua54", "vendored", "module"] }
```
`lib.rs` :
@@ -256,6 +256,14 @@ If you encounter them, a bug report would be very welcome:
+ If you detect that, after catching a panic or during a Drop triggered from a panic, a `Lua` or handle method is triggering other bugs or there is a Lua stack space leak, this is a bug. `mlua` instances are supposed to remain fully usable in the face of user generated panics. This guarantee does not extend to panics marked with "mlua internal error" simply because that is already indicative of a separate bug.
## Sandboxing
Please check the [Luau Sandboxing] page if you are interested in running untrusted Lua scripts in controlled environment.
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
[Luau Sandboxing]: https://luau-lang.org/sandbox
## License
This project is licensed under the [MIT license](LICENSE)
+1 -1
View File
@@ -1,5 +1,5 @@
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
unreachable!()
None
}
+2
View File
@@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::env;
use std::ops::Bound;
use std::path::PathBuf;
+2 -1
View File
@@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
@@ -20,7 +22,6 @@ pub fn probe_lua() -> Option<PathBuf> {
#[cfg(feature = "luau")]
let artifacts = luau0_src::Build::new().build();
#[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata();
Some(artifacts.include_dir().to_owned())
+4
View File
@@ -105,6 +105,10 @@ fn main() {
+ "Please, use `pkg-config` or custom mode to link to a Lua dll."
);
#[cfg(all(feature = "luau", feature = "module"))]
compile_error!("Luau does not support module mode");
#[cfg(any(not(feature = "module"), target_os = "windows"))]
find::probe_lua();
println!("cargo:rerun-if-changed=build");
+2 -1
View File
@@ -7,13 +7,14 @@ edition = "2018"
[lib]
crate-type = ["cdylib"]
[workspace]
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../..", features = ["module"] }
-22
View File
@@ -8,32 +8,10 @@ fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
Ok(lua.used_memory())
}
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
Ok(ud.0)
}
#[mlua::lua_module]
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("sum", lua.create_function(sum)?)?;
exports.set("used_memory", lua.create_function(used_memory)?)?;
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
Ok(exports)
}
#[derive(Clone, Copy)]
struct MyUserData(i32);
impl LuaUserData for MyUserData {}
#[mlua::lua_module]
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
Ok(exports)
}
#[mlua::lua_module]
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
Err("custom module error".to_lua_err())
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua_derive"
version = "0.8.0-beta.1"
version = "0.8.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
description = "Procedural macros for the mlua crate."
+57 -9
View File
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::CString;
use std::io::Result as IoResult;
use std::path::{Path, PathBuf};
@@ -394,20 +395,68 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// Compiles the chunk and changes mode to binary.
///
/// It does nothing if the chunk is already binary.
#[cfg(feature = "luau")]
fn compile(&mut self) {
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
let data = self
.compiler
.get_or_insert_with(Default::default)
.compile(source);
self.mode = Some(ChunkMode::Binary);
self.source = Ok(Cow::Owned(data));
#[cfg(feature = "luau")]
{
let data = self
.compiler
.get_or_insert_with(Default::default)
.compile(source);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
#[cfg(not(feature = "luau"))]
if let Ok(func) = self.lua.load_chunk(source.as_ref(), None, None, None) {
let data = func.dump(false);
self.source = Ok(Cow::Owned(data));
self.mode = Some(ChunkMode::Binary);
}
}
}
}
/// Fetches compiled bytecode of this chunk from the cache.
///
/// If not found, compiles the source code and stores it on the cache.
pub(crate) fn try_cache(mut self) -> Self {
struct ChunksCache(HashMap<Vec<u8>, Vec<u8>>);
// Try to fetch compiled chunk from cache
let mut text_source = None;
if let Ok(ref source) = self.source {
if self.detect_mode() == ChunkMode::Text {
if let Some(cache) = self.lua.app_data_ref::<ChunksCache>() {
if let Some(data) = cache.0.get(source.as_ref()) {
self.source = Ok(Cow::Owned(data.clone()));
self.mode = Some(ChunkMode::Binary);
return self;
}
}
text_source = Some(source.as_ref().to_vec());
}
}
// Compile and cache the chunk
if let Some(text_source) = text_source {
self.compile();
if let Ok(ref binary_source) = self.source {
if self.detect_mode() == ChunkMode::Binary {
if let Some(mut cache) = self.lua.app_data_mut::<ChunksCache>() {
cache.0.insert(text_source, binary_source.as_ref().to_vec());
} else {
let mut cache = ChunksCache(HashMap::new());
cache.0.insert(text_source, binary_source.as_ref().to_vec());
self.lua.set_app_data(cache);
}
}
}
}
self
}
fn to_expression(&self) -> Result<Function<'lua>> {
// We assume that mode is Text
let source = self.source.as_ref();
@@ -429,14 +478,13 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
fn detect_mode(&self) -> ChunkMode {
match (self.mode, &self.source) {
(Some(mode), _) => mode,
(None, Ok(source)) if source.len() == 0 => ChunkMode::Text,
(None, Ok(source)) => {
#[cfg(not(feature = "luau"))]
if source.starts_with(ffi::LUA_SIGNATURE) {
return ChunkMode::Binary;
}
#[cfg(feature = "luau")]
if source[0] < b'\n' {
if *source.get(0).unwrap_or(&u8::MAX) < b'\n' {
return ChunkMode::Binary;
}
ChunkMode::Text
+47 -14
View File
@@ -250,18 +250,21 @@ impl<'lua> ToLua<'lua> for &str {
}
impl<'lua> ToLua<'lua> for Cow<'_, str> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> ToLua<'lua> for Box<str> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&*self)?))
}
}
impl<'lua> FromLua<'lua> for Box<str> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(lua
@@ -278,12 +281,14 @@ impl<'lua> FromLua<'lua> for Box<str> {
}
impl<'lua> ToLua<'lua> for CString {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.as_bytes())?))
}
}
impl<'lua> FromLua<'lua> for CString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
let string = lua
@@ -306,24 +311,28 @@ impl<'lua> FromLua<'lua> for CString {
}
impl<'lua> ToLua<'lua> for &CStr {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> ToLua<'lua> for Cow<'_, CStr> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self.to_bytes())?))
}
}
impl<'lua> ToLua<'lua> for BString {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
impl<'lua> FromLua<'lua> for BString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(BString::from(
@@ -340,14 +349,16 @@ impl<'lua> FromLua<'lua> for BString {
}
impl<'lua> ToLua<'lua> for &BStr {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
Ok(Value::String(lua.create_string(self)?))
}
}
macro_rules! lua_convert_int {
($x:ty) => {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.map(Value::Integer)
@@ -362,22 +373,27 @@ macro_rules! lua_convert_int {
}
impl<'lua> FromLua<'lua> for $x {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
(if let Value::Integer(i) = value {
cast(i)
} else if let Some(i) = lua.coerce_integer(value.clone())? {
cast(i)
} else {
cast(lua.coerce_number(value)?.ok_or_else(|| {
Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
message: Some(
"expected number or string coercible to number".to_string(),
),
(match value {
Value::Integer(i) => cast(i),
Value::Number(n) => cast(n),
_ => {
if let Some(i) = lua.coerce_integer(value.clone())? {
cast(i)
} else {
cast(lua.coerce_number(value)?.ok_or_else(|| {
Error::FromLuaConversionError {
from: ty,
to: stringify!($x),
message: Some(
"expected number or string coercible to number".to_string(),
),
}
})?)
}
})?)
}
})
.ok_or_else(|| Error::FromLuaConversionError {
from: ty,
@@ -405,6 +421,7 @@ lua_convert_int!(usize);
macro_rules! lua_convert_float {
($x:ty) => {
impl<'lua> ToLua<'lua> for $x {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
cast(self)
.ok_or_else(|| Error::ToLuaConversionError {
@@ -417,6 +434,7 @@ macro_rules! lua_convert_float {
}
impl<'lua> FromLua<'lua> for $x {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
lua.coerce_number(value)?
@@ -444,6 +462,7 @@ impl<'lua, T> ToLua<'lua> for &[T]
where
T: Clone + ToLua<'lua>,
{
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(
lua.create_sequence_from(self.iter().cloned())?,
@@ -455,6 +474,7 @@ impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
where
T: ToLua<'lua>,
{
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
@@ -464,6 +484,7 @@ impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
where
T: FromLua<'lua>,
{
#[inline]
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
@@ -496,24 +517,28 @@ where
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
}
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Box<[T]> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
}
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Vec<T> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_sequence_from(self)?))
}
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
#[inline]
fn from_lua(value: Value<'lua>, _lua: &'lua Lua) -> Result<Self> {
match value {
#[cfg(feature = "luau")]
@@ -535,6 +560,7 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> {
impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lua>
for HashMap<K, V, S>
{
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
@@ -543,6 +569,7 @@ impl<'lua, K: Eq + Hash + ToLua<'lua>, V: ToLua<'lua>, S: BuildHasher> ToLua<'lu
impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
for HashMap<K, V, S>
{
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
if let Value::Table(table) = value {
table.pairs().collect()
@@ -557,12 +584,14 @@ impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>, S: BuildHasher + Defa
}
impl<'lua, K: Ord + ToLua<'lua>, V: ToLua<'lua>> ToLua<'lua> for BTreeMap<K, V> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(self)?))
}
}
impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<K, V> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
if let Value::Table(table) = value {
table.pairs().collect()
@@ -577,6 +606,7 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap<
}
impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T, S> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
@@ -585,6 +615,7 @@ impl<'lua, T: Eq + Hash + ToLua<'lua>, S: BuildHasher> ToLua<'lua> for HashSet<T
}
impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua> for HashSet<T, S> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
@@ -602,6 +633,7 @@ impl<'lua, T: Eq + Hash + FromLua<'lua>, S: BuildHasher + Default> FromLua<'lua>
}
impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(lua.create_table_from(
self.into_iter().map(|val| (val, true)),
@@ -610,6 +642,7 @@ impl<'lua, T: Ord + ToLua<'lua>> ToLua<'lua> for BTreeSet<T> {
}
impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::Table(table) if table.len()? > 0 => table.sequence_values().collect(),
+2 -3
View File
@@ -568,11 +568,10 @@ pub unsafe fn luaL_requiref(
lua_getfield(L, -1, modname);
}
}
if cfg!(feature = "lua51") && glb != 0 {
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
}
if cfg!(feature = "luajit") && glb == 0 {
} else {
lua_pushnil(L);
lua_setglobal(L, modname);
}
+4 -1
View File
@@ -67,7 +67,10 @@ pub const LUA_MINSTACK: c_int = 20;
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
pub type lua_Integer = isize;
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// Type for native C functions that can be passed to Lua.
pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int;
+4 -1
View File
@@ -69,7 +69,10 @@ pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS;
pub type lua_Number = c_double;
/// A Lua integer, usually equivalent to `i64`
pub type lua_Integer = isize;
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
/// A Lua unsigned integer, equivalent to `u32` in Lua 5.2
pub type lua_Unsigned = c_uint;
+3
View File
@@ -513,6 +513,9 @@ pub unsafe fn luaL_requiref(
if glb != 0 {
lua_pushvalue(L, -1);
lua_setglobal(L, modname);
} else {
lua_pushnil(L);
lua_setglobal(L, modname);
}
lua_replace(L, -2);
}
+6 -1
View File
@@ -133,6 +133,7 @@ extern "C" {
pub fn lua_namecallatom(L: *mut lua_State, atom: *mut c_int) -> *const c_char;
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_tolightuserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void;
pub fn lua_userdatatag(L: *mut lua_State, idx: c_int) -> c_int;
@@ -259,7 +260,11 @@ extern "C" {
pub fn lua_concat(L: *mut lua_State, n: c_int);
// TODO: lua_encodepointer
pub fn lua_clock() -> c_double;
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Udestructor>);
pub fn lua_setuserdatadtor(
L: *mut lua_State,
tag: c_int,
dtor: Option<unsafe extern "C" fn(*mut lua_State, *mut c_void)>,
);
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
}
+90 -25
View File
@@ -1,6 +1,7 @@
use std::mem;
use std::os::raw::c_int;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::slice;
use crate::error::{Error, Result};
use crate::ffi;
@@ -29,6 +30,17 @@ pub struct FunctionInfo {
pub last_line_defined: i32,
}
/// Luau function coverage snapshot.
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CoverageInfo {
pub function: Option<std::string::String>,
pub line_defined: i32,
pub depth: i32,
pub hits: Vec<i32>,
}
impl<'lua> Function<'lua> {
/// Calls the function, passing `args` as function arguments.
///
@@ -178,24 +190,17 @@ impl<'lua> Function<'lua> {
/// # }
/// ```
pub fn bind<A: ToLuaMulti<'lua>>(&self, args: A) -> Result<Function<'lua>> {
unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int {
unsafe extern "C" fn args_wrapper_impl(state: *mut ffi::lua_State) -> c_int {
let nargs = ffi::lua_gettop(state);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int;
ffi::luaL_checkstack(state, nbinds + 2, ptr::null());
ffi::lua_settop(state, nargs + nbinds + 1);
ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
ffi::lua_replace(state, 1);
let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(1)) as c_int;
ffi::luaL_checkstack(state, nbinds, ptr::null());
for i in 0..nbinds {
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3));
ffi::lua_replace(state, i + 2);
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 2));
}
ffi::lua_rotate(state, 1, nbinds);
ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET);
ffi::lua_gettop(state)
nargs + nbinds
}
let lua = self.0.lua;
@@ -203,25 +208,36 @@ impl<'lua> Function<'lua> {
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
if nargs + 2 > ffi::LUA_MAX_UPVALUES {
if nargs + 1 > ffi::LUA_MAX_UPVALUES {
return Err(Error::BindError);
}
unsafe {
let args_wrapper = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, nargs + 5)?;
check_stack(lua.state, nargs + 3)?;
lua.push_ref(&self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args {
lua.push_value(arg)?;
}
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
protect_lua!(lua.state, nargs + 1, 1, fn(state) {
ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state));
})?;
Ok(Function(lua.pop_ref()))
}
Function(lua.pop_ref())
};
lua.load(
r#"
local func, args_wrapper = ...
return function(...)
return func(args_wrapper(...))
end
"#,
)
.try_cache()
.set_name("_mlua_bind")?
.call((self.clone(), args_wrapper))
}
/// Returns information about the function.
@@ -270,9 +286,6 @@ impl<'lua> Function<'lua> {
#[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn dump(&self, strip: bool) -> Vec<u8> {
use std::os::raw::c_void;
use std::slice;
unsafe extern "C" fn writer(
_state: *mut ffi::lua_State,
buf: *const c_void,
@@ -300,6 +313,58 @@ impl<'lua> Function<'lua> {
data
}
/// Retrieves recorded coverage information about this Lua function including inner calls.
///
/// This function takes a callback as an argument and calls it providing [`CoverageInfo`] snapshot
/// per each executed inner function.
///
/// Recording of coverage information is controlled by [`Compiler::set_coverage_level`] option.
///
/// Requires `feature = "luau"`
///
/// [`Compiler::set_coverage_level`]: crate::chunk::Compiler::set_coverage_level
#[cfg(any(feature = "luau", docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub fn coverage<F>(&self, mut func: F)
where
F: FnMut(CoverageInfo),
{
use std::ffi::CStr;
use std::os::raw::c_char;
unsafe extern "C" fn callback<F: FnMut(CoverageInfo)>(
data: *mut c_void,
function: *const c_char,
line_defined: c_int,
depth: c_int,
hits: *const c_int,
size: usize,
) {
let function = if !function.is_null() {
Some(CStr::from_ptr(function).to_string_lossy().to_string())
} else {
None
};
let rust_callback = &mut *(data as *mut F);
rust_callback(CoverageInfo {
function,
line_defined,
depth,
hits: slice::from_raw_parts(hits, size).to_vec(),
});
}
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let func_ptr = &mut func as *mut F as *mut c_void;
ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>);
}
}
}
impl<'lua> PartialEq for Function<'lua> {
+2 -2
View File
@@ -72,7 +72,7 @@
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
// mlua types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/mlua/0.8.0-beta.4")]
#![doc(html_root_url = "https://docs.rs/mlua/0.8.0")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
@@ -128,7 +128,7 @@ pub use crate::hook::HookTriggers;
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub use crate::{chunk::Compiler, types::VmState};
pub use crate::{chunk::Compiler, function::CoverageInfo, types::VmState};
#[cfg(feature = "async")]
pub use crate::thread::AsyncThread;
+146 -85
View File
@@ -8,6 +8,7 @@ use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut};
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::sync::{Arc, Mutex};
use std::{mem, ptr, str};
@@ -95,7 +96,7 @@ pub(crate) struct ExtraData {
app_data: RefCell<HashMap<TypeId, Box<dyn Any + Send>>>,
libs: StdLib,
mem_info: Option<ptr::NonNull<MemoryInfo>>,
mem_info: Option<NonNull<MemoryInfo>>,
ref_thread: *mut ffi::lua_State,
ref_stack_size: c_int,
@@ -226,7 +227,7 @@ const MULTIVALUE_CACHE_SIZE: usize = 32;
/// Requires `feature = "send"`
#[cfg(feature = "send")]
#[cfg_attr(docsrs, doc(cfg(feature = "send")))]
unsafe impl Send for LuaInner {}
unsafe impl Send for Lua {}
#[cfg(not(feature = "module"))]
impl Drop for LuaInner {
@@ -378,25 +379,20 @@ impl Lua {
}
unsafe fn inner_new(libs: StdLib, options: LuaOptions) -> Lua {
#[cfg_attr(
any(feature = "lua51", feature = "luajit", feature = "luau"),
allow(dead_code)
)]
unsafe extern "C" fn allocator(
extra_data: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void {
use std::alloc;
use std::alloc::{self, Layout};
let mem_info = &mut *(extra_data as *mut MemoryInfo);
if nsize == 0 {
// Free memory
if !ptr.is_null() {
let layout =
alloc::Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
let layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
alloc::dealloc(ptr as *mut u8, layout);
mem_info.used_memory -= osize as isize;
}
@@ -413,52 +409,40 @@ impl Lua {
return ptr::null_mut();
}
let new_layout = alloc::Layout::from_size_align_unchecked(nsize, ffi::SYS_MIN_ALIGN);
let new_layout = Layout::from_size_align_unchecked(nsize, ffi::SYS_MIN_ALIGN);
mem_info.used_memory += mem_diff;
if ptr.is_null() {
// Allocate new memory
let new_ptr = alloc::alloc(new_layout) as *mut c_void;
if !new_ptr.is_null() {
mem_info.used_memory += mem_diff;
if new_ptr.is_null() {
alloc::handle_alloc_error(new_layout);
}
return new_ptr;
}
// Reallocate memory
let old_layout = alloc::Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
let old_layout = Layout::from_size_align_unchecked(osize, ffi::SYS_MIN_ALIGN);
let new_ptr = alloc::realloc(ptr as *mut u8, old_layout, nsize) as *mut c_void;
if !new_ptr.is_null() {
mem_info.used_memory += mem_diff;
} else if !ptr.is_null() && nsize < osize {
// Should not happen
if new_ptr.is_null() {
alloc::handle_alloc_error(new_layout);
}
new_ptr
}
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
let mem_info = Box::into_raw(Box::new(MemoryInfo {
used_memory: 0,
memory_limit: 0,
}));
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
let state = ffi::lua_newstate(allocator, mem_info as *mut c_void);
#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
let state = ffi::luaL_newstate();
ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1);
ffi::lua_pop(state, 1);
let lua = Lua::init_from_ptr(state);
let extra = &mut *lua.extra.get();
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
{
extra.mem_info = ptr::NonNull::new(mem_info);
}
extra.mem_info = NonNull::new(mem_info);
mlua_expect!(
load_from_std_lib(state, libs),
@@ -554,7 +538,7 @@ impl Lua {
#[cfg(feature = "async")]
let ref_waker_idx = {
mlua_expect!(
push_gc_userdata::<Option<Waker>>(ref_thread, None),
push_gc_userdata::<Option<Waker>>(ref_thread, None, true),
"Error while creating Waker slot"
);
ffi::lua_gettop(ref_thread)
@@ -594,7 +578,7 @@ impl Lua {
mlua_expect!(
(|state| {
push_gc_userdata(state, Arc::clone(&extra))?;
push_gc_userdata(state, Arc::clone(&extra), true)?;
protect_lua!(state, 1, 0, fn(state) {
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
@@ -1423,19 +1407,16 @@ impl Lua {
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
push_string(self.state, s)?;
let protect = !self.unlikely_memory_error();
push_string(self.state, s, protect)?;
Ok(String(self.pop_ref()))
}
}
/// Creates and returns a new empty table.
pub fn create_table(&self) -> Result<Table> {
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 2)?;
protect_lua!(self.state, 0, 1, fn(state) ffi::lua_newtable(state))?;
Ok(Table(self.pop_ref()))
}
self.create_table_with_capacity(0, 0)
}
/// Creates and returns a new empty table, with the specified capacity.
@@ -1446,7 +1427,9 @@ impl Lua {
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
push_table(self.state, narr, nrec)?;
let protect = !self.unlikely_memory_error();
push_table(self.state, narr, nrec, protect)?;
Ok(Table(self.pop_ref()))
}
}
@@ -1464,11 +1447,16 @@ impl Lua {
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
push_table(self.state, 0, lower_bound as c_int)?;
let protect = !self.unlikely_memory_error();
push_table(self.state, 0, lower_bound as c_int, protect)?;
for (k, v) in iter {
self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?;
protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
if protect {
protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
} else {
ffi::lua_rawset(self.state, -3);
}
}
Ok(Table(self.pop_ref()))
@@ -1487,12 +1475,17 @@ impl Lua {
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
push_table(self.state, lower_bound as c_int, 0)?;
let protect = !self.unlikely_memory_error();
push_table(self.state, lower_bound as c_int, 0, protect)?;
for (i, v) in iter.enumerate() {
self.push_value(v.to_lua(self)?)?;
protect_lua!(self.state, 2, 1, |state| {
ffi::lua_rawseti(state, -2, (i + 1) as Integer);
})?;
if protect {
protect_lua!(self.state, 2, 1, |state| {
ffi::lua_rawseti(state, -2, (i + 1) as Integer);
})?;
} else {
ffi::lua_rawseti(self.state, -2, (i + 1) as Integer);
}
}
Ok(Table(self.pop_ref()))
@@ -1651,7 +1644,11 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
let thread_state = protect_lua!(self.state, 0, 1, |state| ffi::lua_newthread(state))?;
let thread_state = if self.unlikely_memory_error() {
ffi::lua_newthread(self.state)
} else {
protect_lua!(self.state, 0, 1, |state| ffi::lua_newthread(state))?
};
self.push_ref(&func.0);
ffi::lua_xmove(self.state, thread_state, 1);
@@ -1826,9 +1823,13 @@ impl Lua {
check_stack(self.state, 4)?;
self.push_value(v)?;
let res = protect_lua!(self.state, 1, 1, |state| {
ffi::lua_tolstring(state, -1, ptr::null_mut())
})?;
let res = if self.unlikely_memory_error() {
ffi::lua_tolstring(self.state, -1, ptr::null_mut())
} else {
protect_lua!(self.state, 1, 1, |state| {
ffi::lua_tolstring(state, -1, ptr::null_mut())
})?
};
if !res.is_null() {
Some(String(self.pop_ref()))
} else {
@@ -1944,7 +1945,8 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
push_string(self.state, name)?;
let protect = !self.unlikely_memory_error();
push_string(self.state, name, protect)?;
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
self.pop_value()
@@ -2137,6 +2139,7 @@ impl Lua {
/// Ok(())
/// }
/// ```
#[track_caller]
pub fn set_app_data<T: 'static + MaybeSend>(&self, data: T) {
let extra = unsafe { &mut (*self.extra.get()) };
extra
@@ -2147,6 +2150,7 @@ impl Lua {
}
/// Gets a reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
#[track_caller]
pub fn app_data_ref<T: 'static>(&self) -> Option<Ref<T>> {
let extra = unsafe { &(*self.extra.get()) };
let app_data = extra
@@ -2158,6 +2162,7 @@ impl Lua {
}
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
#[track_caller]
pub fn app_data_mut<T: 'static>(&self) -> Option<RefMut<T>> {
let extra = unsafe { &(*self.extra.get()) };
let mut app_data = extra
@@ -2169,6 +2174,7 @@ impl Lua {
}
/// Removes an application data of type `T`.
#[track_caller]
pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
let extra = unsafe { &mut (*self.extra.get()) };
extra
@@ -2228,7 +2234,8 @@ impl Lua {
}
Value::Error(err) => {
push_gc_userdata(self.state, WrappedFailure::Error(err))?;
let protect = !self.unlikely_memory_error();
push_gc_userdata(self.state, WrappedFailure::Error(err), protect)?;
}
}
@@ -2256,15 +2263,29 @@ impl Lua {
ud
}
#[cfg(any(feature = "lua54", feature = "lua53"))]
ffi::LUA_TNUMBER => {
if ffi::lua_isinteger(state, -1) != 0 {
let i = Value::Integer(ffi::lua_tointeger(state, -1));
ffi::lua_pop(state, 1);
i
let v = if ffi::lua_isinteger(state, -1) != 0 {
Value::Integer(ffi::lua_tointeger(state, -1))
} else {
let n = Value::Number(ffi::lua_tonumber(state, -1));
ffi::lua_pop(state, 1);
n
Value::Number(ffi::lua_tonumber(state, -1))
};
ffi::lua_pop(state, 1);
v
}
#[cfg(any(
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
))]
ffi::LUA_TNUMBER => {
let n = ffi::lua_tonumber(state, -1);
ffi::lua_pop(state, 1);
match num_traits::cast(n) {
Some(i) if (n - (i as Number)).abs() < Number::EPSILON => Value::Integer(i),
_ => Value::Number(n),
}
}
@@ -2399,7 +2420,7 @@ impl Lua {
let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len();
#[cfg(feature = "async")]
let metatable_nrec = metatable_nrec + methods.async_meta_methods.len();
push_table(self.state, 0, metatable_nrec as c_int)?;
push_table(self.state, 0, metatable_nrec as c_int, true)?;
for (k, m) in methods.meta_methods {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, k.validate()?.name())?;
@@ -2420,7 +2441,7 @@ impl Lua {
let mut field_getters_index = None;
let field_getters_nrec = fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(self.state, 0, field_getters_nrec as c_int)?;
push_table(self.state, 0, field_getters_nrec as c_int, true)?;
for (k, m) in fields.field_getters {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, &k)?;
@@ -2432,7 +2453,7 @@ impl Lua {
let mut field_setters_index = None;
let field_setters_nrec = fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(self.state, 0, field_setters_nrec as c_int)?;
push_table(self.state, 0, field_setters_nrec as c_int, true)?;
for (k, m) in fields.field_setters {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, &k)?;
@@ -2446,7 +2467,7 @@ impl Lua {
#[cfg(feature = "async")]
let methods_nrec = methods_nrec + methods.async_methods.len();
if methods_nrec > 0 {
push_table(self.state, 0, methods_nrec as c_int)?;
push_table(self.state, 0, methods_nrec as c_int, true)?;
for (k, m) in methods.methods {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, &k)?;
@@ -2577,10 +2598,15 @@ impl Lua {
let func = mem::transmute(func);
let extra = Arc::clone(&self.extra);
push_gc_userdata(self.state, CallbackUpvalue { data: func, extra })?;
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
let protect = !self.unlikely_memory_error();
push_gc_userdata(self.state, CallbackUpvalue { data: func, extra }, protect)?;
if protect {
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
} else {
ffi::lua_pushcclosure(self.state, call_callback, 1);
}
Ok(Function(self.pop_ref()))
}
@@ -2636,10 +2662,15 @@ impl Lua {
let func = &*(*upvalue).data;
let fut = func(lua, args);
let extra = Arc::clone(&(*upvalue).extra);
push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra })?;
protect_lua!(state, 1, 1, fn(state) {
let protect = !lua.unlikely_memory_error();
push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
if protect {
protect_lua!(state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, poll_future, 1);
})?;
} else {
ffi::lua_pushcclosure(state, poll_future, 1);
})?;
}
Ok(1)
})
@@ -2698,10 +2729,16 @@ impl Lua {
let func = mem::transmute(func);
let extra = Arc::clone(&self.extra);
push_gc_userdata(self.state, AsyncCallbackUpvalue { data: func, extra })?;
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
let protect = !self.unlikely_memory_error();
let upvalue = AsyncCallbackUpvalue { data: func, extra };
push_gc_userdata(self.state, upvalue, protect)?;
if protect {
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
} else {
ffi::lua_pushcclosure(self.state, call_callback, 1);
}
Function(self.pop_ref())
};
@@ -2741,6 +2778,7 @@ impl Lua {
end
"#,
)
.try_cache()
.set_name("_mlua_async_poll")?
.set_environment(env)?
.into_function()
@@ -2774,19 +2812,25 @@ impl Lua {
// We push metatable first to ensure having correct metatable with `__gc` method
ffi::lua_pushnil(self.state);
self.push_userdata_metatable::<T>()?;
let protect = !self.unlikely_memory_error();
#[cfg(not(feature = "lua54"))]
push_userdata(self.state, data)?;
push_userdata(self.state, data, protect)?;
#[cfg(feature = "lua54")]
push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int)?;
push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int, protect)?;
ffi::lua_replace(self.state, -3);
ffi::lua_setmetatable(self.state, -2);
// Set empty environment for Lua 5.1
#[cfg(any(feature = "lua51", feature = "luajit"))]
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_newtable(state);
ffi::lua_setuservalue(state, -2);
})?;
if protect {
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_newtable(state);
ffi::lua_setuservalue(state, -2);
})?;
} else {
ffi::lua_newtable(self.state);
ffi::lua_setuservalue(self.state, -2);
}
Ok(AnyUserData(self.pop_ref()))
}
@@ -2844,6 +2888,16 @@ impl Lua {
}
}
}
#[inline]
pub(crate) unsafe fn unlikely_memory_error(&self) -> bool {
let extra = &mut *self.extra.get();
// MemoryInfo is empty in module mode so we cannot predict memory limits
extra
.mem_info
.map(|x| x.as_ref().memory_limit == 0)
.unwrap_or_default()
}
}
struct StateGuard<'a>(&'a mut LuaInner, *mut ffi::lua_State);
@@ -2910,6 +2964,18 @@ where
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_failures_cache.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),
Cached(i32),
@@ -2920,13 +2986,6 @@ where
let prealloc_failure = match extra.wrapped_failures_cache.pop() {
Some(index) => PreallocatedFailure::Cached(index),
None => {
// We need 2 extra stack spaces to store userdata and error/panic metatable.
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"),
);
let ud = WrappedFailure::new_userdata(state);
ffi::lua_rotate(state, 1, 1);
PreallocatedFailure::New(ud)
@@ -2940,6 +2999,8 @@ where
}
PreallocatedFailure::Cached(index) => {
ffi::lua_settop(state, 0);
#[cfg(feature = "luau")]
assert_stack(state, 2);
ffi::lua_pushvalue(extra.ref_thread, index);
ffi::lua_xmove(extra.ref_thread, state, 1);
ffi::lua_pushnil(extra.ref_thread);
+12 -1
View File
@@ -11,6 +11,7 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message.
impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E> {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut result = MultiValue::new_or_cached(lua);
match self {
@@ -25,6 +26,7 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
}
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new_or_cached(lua);
v.push_front(self.to_lua(lua)?);
@@ -33,6 +35,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let res = T::from_lua(values.pop_front().unwrap_or(Nil), lua);
lua.cache_multivalue(values);
@@ -41,12 +44,14 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for T {
}
impl<'lua> ToLuaMulti<'lua> for MultiValue<'lua> {
#[inline]
fn to_lua_multi(self, _: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(self)
}
}
impl<'lua> FromLuaMulti<'lua> for MultiValue<'lua> {
#[inline]
fn from_lua_multi(values: MultiValue<'lua>, _: &'lua Lua) -> Result<Self> {
Ok(values)
}
@@ -83,7 +88,7 @@ pub struct Variadic<T>(Vec<T>);
impl<T> Variadic<T> {
/// Creates an empty `Variadic` wrapper containing no values.
pub fn new() -> Variadic<T> {
pub const fn new() -> Variadic<T> {
Variadic(Vec::new())
}
}
@@ -124,6 +129,7 @@ impl<T> DerefMut for Variadic<T> {
}
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut values = MultiValue::new_or_cached(lua);
values.refill(self.0.into_iter().map(|e| e.to_lua(lua)))?;
@@ -132,6 +138,7 @@ impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for Variadic<T> {
}
impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let res = values
.drain_all()
@@ -146,12 +153,14 @@ impl<'lua, T: FromLua<'lua>> FromLuaMulti<'lua> for Variadic<T> {
macro_rules! impl_tuple {
() => (
impl<'lua> ToLuaMulti<'lua> for () {
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
Ok(MultiValue::new_or_cached(lua))
}
}
impl<'lua> FromLuaMulti<'lua> for () {
#[inline]
fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
lua.cache_multivalue(values);
Ok(())
@@ -166,6 +175,7 @@ macro_rules! impl_tuple {
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let ($($name,)* $last,) = self;
@@ -181,6 +191,7 @@ macro_rules! impl_tuple {
{
#[allow(unused_mut)]
#[allow(non_snake_case)]
#[inline]
fn from_lua_multi(mut values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
$(let $name = values.pop_front().unwrap_or(Nil);)*
let $last = FromLuaMulti::from_lua_multi(values, lua)?;
+1 -1
View File
@@ -21,7 +21,7 @@ pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")]
#[doc(no_inline)]
pub use crate::VmState as LuaVmState;
pub use crate::{CoverageInfo as LuaCoverageInfo, VmState as LuaVmState};
#[cfg(feature = "async")]
#[doc(no_inline)]
+5 -4
View File
@@ -355,13 +355,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
UserDataCell::new(data.clone()),
true,
)?;
ffi::lua_touserdata(lua.state, -1)
};
// Prepare metatable, add meta methods first and then meta fields
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
push_table(lua.state, 0, meta_methods_nrec as c_int)?;
push_table(lua.state, 0, meta_methods_nrec as c_int, true)?;
for (k, m) in ud_methods.meta_methods {
let data = data.clone();
@@ -377,7 +378,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_getters_index = None;
let field_getters_nrec = ud_fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(lua.state, 0, field_getters_nrec as c_int)?;
push_table(lua.state, 0, field_getters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_getters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
@@ -389,7 +390,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_setters_index = None;
let field_setters_nrec = ud_fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(lua.state, 0, field_setters_nrec as c_int)?;
push_table(lua.state, 0, field_setters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_setters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
@@ -402,7 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let methods_nrec = ud_methods.methods.len();
if methods_nrec > 0 {
// Create table used for methods lookup
push_table(lua.state, 0, methods_nrec as c_int)?;
push_table(lua.state, 0, methods_nrec as c_int, true)?;
for (k, m) in ud_methods.methods {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
+9 -2
View File
@@ -320,10 +320,17 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
self.deserialize_map(visitor)
}
#[inline]
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
byte_buf unit unit_struct newtype_struct
identifier ignored_any
byte_buf unit unit_struct identifier ignored_any
}
}
+11 -4
View File
@@ -327,10 +327,17 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
lua.push_ref(&self.table.0);
lua.push_value(value)?;
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
if lua.unlikely_memory_error() {
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
ffi::lua_rawseti(lua.state, -2, len + 1);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
}
}
+7 -1
View File
@@ -198,7 +198,13 @@ impl<'lua> Table<'lua> {
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
if lua.unlikely_memory_error() {
ffi::lua_rawset(lua.state, -3);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
}
}
}
+14
View File
@@ -118,7 +118,17 @@ pub enum MetaMethod {
///
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
#[cfg(any(feature = "lua52", feature = "luajit52", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua52", feature = "luajit52"))))]
IPairs,
/// The `__iter` metamethod.
///
/// Executed before the iteration begins, and should return an iterator function like `next`
/// (or a custom one).
///
/// Requires `feature = "lua"`
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Iter,
/// The `__close` metamethod.
///
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
@@ -203,6 +213,8 @@ impl MetaMethod {
MetaMethod::Pairs => "__pairs",
#[cfg(any(feature = "lua52", feature = "luajit52"))]
MetaMethod::IPairs => "__ipairs",
#[cfg(feature = "luau")]
MetaMethod::Iter => "__iter",
#[cfg(feature = "lua54")]
MetaMethod::Close => "__close",
@@ -270,6 +282,8 @@ impl From<StdString> for MetaMethod {
"__pairs" => MetaMethod::Pairs,
#[cfg(any(feature = "lua52", feature = "luajit52"))]
"__ipairs" => MetaMethod::IPairs,
#[cfg(feature = "luau")]
"__iter" => MetaMethod::Iter,
#[cfg(feature = "lua54")]
"__close" => MetaMethod::Close,
+59 -22
View File
@@ -251,17 +251,33 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
state: *mut ffi::lua_State,
s: &S,
protect: bool,
) -> Result<()> {
let s = s.as_ref();
protect_lua!(state, 0, 1, |state| {
if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
} else {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
Ok(())
}
}
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
pub unsafe fn push_table(
state: *mut ffi::lua_State,
narr: c_int,
nrec: c_int,
protect: bool,
) -> Result<()> {
if protect {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
} else {
ffi::lua_createtable(state, narr, nrec);
Ok(())
}
}
// Uses 4 stack spaces, does not call checkstack.
@@ -281,10 +297,14 @@ where
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(not(feature = "luau"))]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = protect_lua!(state, 0, 1, |state| {
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?
} else {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?;
};
ptr::write(ud, t);
Ok(())
}
@@ -292,7 +312,7 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "luau")]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
unsafe extern "C" fn destructor<T>(ud: *mut c_void) {
let ud = ud as *mut T;
if *(ud.offset(1) as *mut u8) == 0 {
@@ -300,10 +320,14 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
}
}
let ud = protect_lua!(state, 0, 1, |state| {
let size = mem::size_of::<T>() + 1;
let size = mem::size_of::<T>() + 1;
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
})?
} else {
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
})?;
};
ptr::write(ud, t);
*(ud.offset(1) as *mut u8) = 0; // Mark as not destructed
@@ -313,10 +337,19 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub unsafe fn push_userdata_uv<T>(state: *mut ffi::lua_State, t: T, nuvalue: c_int) -> Result<()> {
let ud = protect_lua!(state, 0, 1, |state| {
pub unsafe fn push_userdata_uv<T>(
state: *mut ffi::lua_State,
t: T,
nuvalue: c_int,
protect: bool,
) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
})?;
};
ptr::write(ud, t);
Ok(())
}
@@ -349,8 +382,12 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
// Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 3 stack spaces, does not call checkstack.
pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
push_userdata(state, t)?;
pub unsafe fn push_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
t: T,
protect: bool,
) -> Result<()> {
push_userdata(state, t, protect)?;
get_gc_metatable::<T>(state);
ffi::lua_setmetatable(state, -2);
Ok(())
@@ -505,7 +542,7 @@ pub unsafe fn init_userdata_metatable<T>(
// Push `__index` generator function
init_userdata_metatable_index(state)?;
push_string(state, "__index")?;
push_string(state, "__index", true)?;
let index_type = ffi::lua_rawget(state, -3);
match index_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
@@ -530,7 +567,7 @@ pub unsafe fn init_userdata_metatable<T>(
// Push `__newindex` generator function
init_userdata_metatable_newindex(state)?;
push_string(state, "__newindex")?;
push_string(state, "__newindex", true)?;
let newindex_type = ffi::lua_rawget(state, -3);
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
@@ -758,7 +795,7 @@ pub unsafe fn init_gc_metatable<T: Any>(
) -> Result<()> {
check_stack(state, 6)?;
push_table(state, 0, 3)?;
push_table(state, 0, 3, true)?;
#[cfg(not(feature = "luau"))]
{
@@ -836,7 +873,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
}
}?;
push_string(state, &*err_buf)?;
push_string(state, &*err_buf, true)?;
(*err_buf).clear();
Ok(1)
@@ -857,7 +894,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
callback_error(state, |_| Err(Error::CallbackDestructed))
}
push_table(state, 0, 26)?;
push_table(state, 0, 26, true)?;
ffi::lua_pushcfunction(state, destructed_error);
for &method in &[
"__add",
@@ -914,7 +951,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Create error print buffer
init_gc_metatable::<String>(state, None)?;
push_gc_userdata(state, String::new())?;
push_gc_userdata(state, String::new(), true)?;
protect_lua!(state, 1, 0, fn(state) {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
+27 -2
View File
@@ -1,5 +1,6 @@
use std::iter::{self, FromIterator};
use std::{slice, str, vec};
use std::os::raw::c_void;
use std::{ptr, slice, str, vec};
#[cfg(feature = "serialize")]
use {
@@ -9,6 +10,7 @@ use {
};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
@@ -93,6 +95,29 @@ impl<'lua> Value<'lua> {
_ => Ok(self == other.as_ref()),
}
}
/// Converts the value to a generic C pointer.
///
/// The value can be a userdata, a table, a thread, a string, or a function; otherwise it returns NULL.
/// Different objects will give different pointers.
/// There is no way to convert the pointer back to its original value.
///
/// Typically this function is used only for hashing and debug information.
pub fn to_pointer(&self) -> *const c_void {
unsafe {
match self {
Value::LightUserData(ud) => ud.0,
Value::String(String(v))
| Value::Table(Table(v))
| Value::Function(Function(v))
| Value::Thread(Thread(v))
| Value::UserData(AnyUserData(v)) => v
.lua
.ref_thread_exec(|refthr| ffi::lua_topointer(refthr, v.index)),
_ => ptr::null(),
}
}
}
}
impl<'lua> PartialEq for Value<'lua> {
@@ -171,7 +196,7 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub fn new() -> MultiValue<'lua> {
pub const fn new() -> MultiValue<'lua> {
MultiValue(Vec::new())
}
+4 -1
View File
@@ -75,7 +75,10 @@ async fn test_async_call() -> Result<()> {
async fn test_async_bind_call() -> Result<()> {
let lua = Lua::new();
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move { Ok(a + b) })?;
let sum = lua.create_async_function(|_lua, (a, b): (i64, i64)| async move {
tokio::task::yield_now().await;
Ok(a + b)
})?;
let plus_10 = sum.bind(10)?;
lua.globals().set("plus_10", plus_10)?;
+74 -1
View File
@@ -5,7 +5,7 @@ use std::fs;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use mlua::{Compiler, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
use mlua::{Compiler, CoverageInfo, Error, Lua, Result, Table, ThreadStatus, Value, VmState};
#[test]
fn test_require() -> Result<()> {
@@ -212,3 +212,76 @@ fn test_interrupts() -> Result<()> {
Ok(())
}
#[test]
fn test_coverate() -> Result<()> {
let lua = Lua::new();
lua.set_compiler(Compiler::default().set_coverage_level(1));
let f = lua
.load(
r#"local v = vector(1, 2, 3)
assert(v.x == 1 and v.y == 2 and v.z == 3)
function abc(i)
if i < 5 then
return 0
else
return 1
end
end
(function()
(function() abc(10) end)()
end)()
"#,
)
.into_function()?;
f.call(())?;
let mut report = Vec::new();
f.coverage(|cov| {
report.push(cov);
});
assert_eq!(
report[0],
CoverageInfo {
function: None,
line_defined: 1,
depth: 0,
hits: vec![-1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1],
}
);
assert_eq!(
report[1],
CoverageInfo {
function: Some("abc".into()),
line_defined: 4,
depth: 1,
hits: vec![-1, -1, -1, -1, -1, 1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1],
}
);
assert_eq!(
report[2],
CoverageInfo {
function: None,
line_defined: 12,
depth: 1,
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
}
);
assert_eq!(
report[3],
CoverageInfo {
function: None,
line_defined: 13,
depth: 2,
hits: vec![-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1],
}
);
Ok(())
}
+8 -5
View File
@@ -1,8 +1,11 @@
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
rustflags = [
"-C", "link-arg=-undefined",
"-C", "link-arg=dynamic_lookup",
]
+10 -3
View File
@@ -1,16 +1,23 @@
[package]
name = "module_runner"
name = "rust_module"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
[lib]
crate-type = ["cdylib"]
[workspace]
members = [
"loader",
]
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../.." }
mlua = { path = "../..", features = ["module"] }
+8
View File
@@ -0,0 +1,8 @@
[target.x86_64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
[target.aarch64-apple-darwin]
rustflags = ["-C", "link-args=-rdynamic"]
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-args=-rdynamic"]
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "module_loader"
version = "0.0.0"
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
edition = "2018"
[features]
lua54 = ["mlua/lua54"]
lua53 = ["mlua/lua53"]
lua52 = ["mlua/lua52"]
lua51 = ["mlua/lua51"]
luajit = ["mlua/luajit"]
vendored = ["mlua/vendored"]
[dependencies]
mlua = { path = "../../.." }
+39
View File
@@ -0,0 +1,39 @@
use mlua::prelude::*;
fn sum(_: &Lua, (a, b): (i64, i64)) -> LuaResult<i64> {
Ok(a + b)
}
fn used_memory(lua: &Lua, _: ()) -> LuaResult<usize> {
Ok(lua.used_memory())
}
fn check_userdata(_: &Lua, ud: MyUserData) -> LuaResult<i32> {
Ok(ud.0)
}
#[mlua::lua_module]
fn rust_module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("sum", lua.create_function(sum)?)?;
exports.set("used_memory", lua.create_function(used_memory)?)?;
exports.set("check_userdata", lua.create_function(check_userdata)?)?;
Ok(exports)
}
#[derive(Clone, Copy)]
struct MyUserData(i32);
impl LuaUserData for MyUserData {}
#[mlua::lua_module]
fn rust_module_second(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
exports.set("userdata", lua.create_userdata(MyUserData(123))?)?;
Ok(exports)
}
#[mlua::lua_module]
fn rust_module_error(_: &Lua) -> LuaResult<LuaTable> {
Err("custom module error".to_lua_err())
}
+13
View File
@@ -375,6 +375,19 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[test]
fn test_from_value_newtype_struct() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
#[derive(Deserialize, PartialEq, Debug)]
struct Test(f64);
let got = lua.from_value(Value::Number(123.456))?;
assert_eq!(Test(123.456), got);
Ok(())
}
#[test]
fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
+17 -1
View File
@@ -560,7 +560,12 @@ fn test_num_conversion() -> Result<()> {
assert_eq!(lua.load("1.0").eval::<f64>()?, 1.0);
#[cfg(any(feature = "lua54", feature = "lua53"))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1.0");
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
#[cfg(any(
feature = "lua52",
feature = "lua51",
feature = "luajit",
feature = "luau"
))]
assert_eq!(lua.load("1.0").eval::<String>()?, "1");
assert_eq!(lua.load("1.5").eval::<i64>()?, 1);
@@ -1272,3 +1277,14 @@ fn test_luajit_cdata() {
)
.eval();
}
#[test]
#[cfg(feature = "send")]
fn test_send() {
let lua = Lua::new();
std::thread::spawn(move || {
let _lua = lua;
})
.join()
.unwrap();
}
+12 -4
View File
@@ -1,3 +1,5 @@
use std::ptr;
use mlua::{Lua, Result, Value};
#[test]
@@ -41,17 +43,23 @@ fn test_value_eq() -> Result<()> {
let thread2: Value = globals.get("thread2")?;
assert!(table1 != table2);
assert!(table1.equals(table2)?);
assert!(table1.equals(&table2)?);
assert!(string1 == string2);
assert!(string1.equals(string2)?);
assert!(string1.equals(&string2)?);
assert!(num1 == num2);
assert!(num1.equals(num2)?);
assert!(num1 != num3);
assert!(func1 == func2);
assert!(func1 != func3);
assert!(!func1.equals(func3)?);
assert!(!func1.equals(&func3)?);
assert!(thread1 == thread2);
assert!(thread1.equals(thread2)?);
assert!(thread1.equals(&thread2)?);
assert!(!table1.to_pointer().is_null());
assert!(!ptr::eq(table1.to_pointer(), table2.to_pointer()));
assert!(ptr::eq(string1.to_pointer(), string2.to_pointer()));
assert!(ptr::eq(func1.to_pointer(), func2.to_pointer()));
assert!(num1.to_pointer().is_null());
Ok(())
}