Merge branch 'master' into master

This commit is contained in:
JohnTheCoolingFan 2022-05-22 11:07:02 +03:00 committed by GitHub
commit 21b603dcaa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 243 additions and 79 deletions

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

View file

@ -23,8 +23,6 @@ rustdoc-args = ["--cfg", "docsrs"]
[workspace]
members = [
"mlua_derive",
"examples/module",
"tests/module",
]
[features]
@ -62,7 +60,7 @@ 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 }
luau0-src = { version = "0.3", optional = true }
[dev-dependencies]
rustyline = "9.0"

View file

@ -1,5 +1,5 @@
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
unreachable!()
None
}

View file

@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::env;
use std::ops::Bound;
use std::path::PathBuf;

View file

@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::path::PathBuf;
pub fn probe_lua() -> Option<PathBuf> {
@ -22,7 +24,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())

View file

@ -115,6 +115,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");

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"] }

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())
}

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

View file

@ -259,7 +259,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);
}

View file

@ -178,24 +178,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 +196,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.

View file

@ -226,7 +226,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 {
@ -2744,6 +2744,7 @@ impl Lua {
end
"#,
)
.try_cache()
.set_name("_mlua_async_poll")?
.set_environment(env)?
.into_function()

View file

@ -119,7 +119,17 @@ pub enum MetaMethod {
///
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
#[cfg(any(feature = "lua52", feature = "luajit52", feature = "lua-factorio", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua52", feature = "luajit52", feature = "lua-factorio"))))]
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.
@ -205,6 +215,8 @@ impl MetaMethod {
MetaMethod::Pairs => "__pairs",
#[cfg(any(feature = "lua52", feature = "luajit52", feature = "lua-factorio"))]
MetaMethod::IPairs => "__ipairs",
#[cfg(feature = "luau")]
MetaMethod::Iter => "__iter",
#[cfg(feature = "lua54")]
MetaMethod::Close => "__close",
@ -273,6 +285,8 @@ impl From<StdString> for MetaMethod {
"__pairs" => MetaMethod::Pairs,
#[cfg(any(feature = "lua52", feature = "luajit52", feature = "lua-factorio"))]
"__ipairs" => MetaMethod::IPairs,
#[cfg(feature = "luau")]
"__iter" => MetaMethod::Iter,
#[cfg(feature = "lua54")]
"__close" => MetaMethod::Close,

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> {

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)?;

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",
]

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"] }

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"]

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
tests/module/src/lib.rs Normal file
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())
}

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(())
}