mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Add userdata-wrappers feature
This feature allow to opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData` Close #470
This commit is contained in:
+12
-12
@@ -27,8 +27,8 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,send"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -123,8 +123,8 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,send"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
@@ -154,8 +154,8 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: --cfg=force_memory_limit
|
||||
@@ -254,7 +254,7 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
@@ -281,4 +281,4 @@ jobs:
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow"
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
@@ -43,6 +43,7 @@ error-send = []
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.10.0", optional = true, path = "mlua_derive" }
|
||||
|
||||
@@ -57,6 +57,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
||||
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
@@ -183,7 +182,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
|
||||
|
||||
// Push the metatable and register it with no TypeId
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *const c_void);
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
|
||||
T::register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry)?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
|
||||
+250
-64
@@ -21,12 +21,32 @@ use {
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
use std::rc::Rc;
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum UserDataTypeId {
|
||||
enum UserDataTypeId {
|
||||
Shared(TypeId),
|
||||
Unique(usize),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Rc(TypeId),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
RcRefCell(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Arc(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcMutex(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcRwLock(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcParkingLotMutex(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcParkingLotRwLock(TypeId),
|
||||
}
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
@@ -45,31 +65,23 @@ pub struct UserDataRegistry<T> {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
|
||||
|
||||
pub(crate) type_id: UserDataTypeId,
|
||||
type_id: UserDataTypeId,
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> UserDataRegistry<T> {
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(type_id: TypeId) -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
type_id: UserDataTypeId::Shared(type_id),
|
||||
_type: PhantomData,
|
||||
}
|
||||
Self::with_type_id(UserDataTypeId::Shared(type_id))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_unique(ud_ptr: *const c_void) -> Self {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_unique(ud_ptr: *mut c_void) -> Self {
|
||||
Self::with_type_id(UserDataTypeId::Unique(ud_ptr as usize))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn with_type_id(type_id: UserDataTypeId) -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
@@ -81,7 +93,7 @@ impl<T> UserDataRegistry<T> {
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
type_id: UserDataTypeId::Unique(ud_ptr as usize),
|
||||
type_id,
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -91,6 +103,20 @@ impl<T> UserDataRegistry<T> {
|
||||
match self.type_id {
|
||||
UserDataTypeId::Shared(type_id) => Some(type_id),
|
||||
UserDataTypeId::Unique(_) => None,
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
UserDataTypeId::Rc(type_id) => Some(type_id),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
UserDataTypeId::RcRefCell(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::Arc(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcMutex(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcRwLock(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcParkingLotMutex(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcParkingLotRwLock(type_id) => Some(type_id),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,28 +146,102 @@ impl<T> UserDataRegistry<T> {
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
// This branch is for `'static` userdata that share type metatable
|
||||
UserDataTypeId::Shared(target_type_id) => {
|
||||
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
|
||||
Some(self_type_id) if self_type_id == target_type_id => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Shared(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataTypeId::Unique(target_ptr) => {
|
||||
match get_userdata::<UserDataStorage<T>>(state, self_index) {
|
||||
ud if ud as usize == target_ptr => {
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Rc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::RcRefCell(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Arc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -174,28 +274,96 @@ impl<T> UserDataRegistry<T> {
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
// This branch is for `'static` userdata that share type metatable
|
||||
UserDataTypeId::Shared(target_type_id) => {
|
||||
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
|
||||
Some(self_type_id) if self_type_id == target_type_id => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Shared(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataTypeId::Unique(target_ptr) => {
|
||||
match get_userdata::<UserDataStorage<T>>(state, self_index) {
|
||||
ud if ud as usize == target_ptr => {
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Rc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
},
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::RcRefCell(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Arc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
},
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -607,11 +775,14 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
($type:ty => $type_variant:tt) => {
|
||||
lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>()));
|
||||
};
|
||||
|
||||
($type:ty, $type_id:expr) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let mut orig_registry = UserDataRegistry::new(type_id);
|
||||
let mut orig_registry = UserDataRegistry::with_type_id($type_id);
|
||||
T::register(&mut orig_registry);
|
||||
|
||||
// Copy all fields, methods, etc. from the original registry
|
||||
@@ -635,4 +806,19 @@ macro_rules! lua_userdata_impl {
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
lua_userdata_impl!(UserDataProxy<T>, UserDataTypeId::Shared(TypeId::of::<T>()));
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<T> => Rc);
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<RefCell<T>> => RcRefCell);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<T> => Arc);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<Mutex<T>> => ArcMutex);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
|
||||
|
||||
@@ -881,3 +881,160 @@ fn test_nested_userdata_gc() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[test]
|
||||
fn test_userdata_wrappers() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<F: UserDataFields<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;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
// Rc<T>
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud = std::rc::Rc::new(MyUserData(1));
|
||||
globals.set("rc_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_ud.static == "constant")
|
||||
local ok, err = pcall(function() rc_ud.data = 2 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
)
|
||||
assert(rc_ud.data == 1)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
globals.set("rc_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Rc<RefCell<T>>
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud = std::rc::Rc::new(std::cell::RefCell::new(MyUserData(2)));
|
||||
globals.set("rc_refcell_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_refcell_ud.static == "constant")
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.borrow().0, 3);
|
||||
globals.set("rc_refcell_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<T>
|
||||
{
|
||||
let ud = Arc::new(MyUserData(3));
|
||||
globals.set("arc_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_ud.static == "constant")
|
||||
local ok, err = pcall(function() arc_ud.data = 10 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
)
|
||||
assert(arc_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("arc_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<Mutex<T>>
|
||||
{
|
||||
let ud = Arc::new(std::sync::Mutex::new(MyUserData(4)));
|
||||
globals.set("arc_mutex_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_mutex_ud.static == "constant")
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 5)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().unwrap().0, 5);
|
||||
globals.set("arc_mutex_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<RwLock<T>>
|
||||
{
|
||||
let ud = Arc::new(std::sync::RwLock::new(MyUserData(6)));
|
||||
globals.set("arc_rwlock_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_rwlock_ud.static == "constant")
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 7)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().unwrap().0, 7);
|
||||
globals.set("arc_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<parking_lot::Mutex<T>>
|
||||
{
|
||||
let ud = Arc::new(parking_lot::Mutex::new(MyUserData(8)));
|
||||
globals.set("arc_parking_lot_mutex_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_parking_lot_mutex_ud.static == "constant")
|
||||
arc_parking_lot_mutex_ud.data = arc_parking_lot_mutex_ud.data + 1
|
||||
assert(arc_parking_lot_mutex_ud.data == 9)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().0, 9);
|
||||
globals.set("arc_parking_lot_mutex_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<parking_lot::RwLock<T>>
|
||||
{
|
||||
let ud = Arc::new(parking_lot::RwLock::new(MyUserData(10)));
|
||||
globals.set("arc_parking_lot_rwlock_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_parking_lot_rwlock_ud.static == "constant")
|
||||
arc_parking_lot_rwlock_ud.data = arc_parking_lot_rwlock_ud.data + 1
|
||||
assert(arc_parking_lot_rwlock_ud.data == 11)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().0, 11);
|
||||
globals.set("arc_parking_lot_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user