Skip to content

Commit d366205

Browse files
committed
Fix calls to callable table and userdata fields
Close #746
1 parent bca1afe commit d366205

6 files changed

Lines changed: 46 additions & 6 deletions

File tree

‎src/table.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,8 @@ impl ObjectLike for Table {
13071307
fn call_function<R: FromLuaMulti>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R> {
13081308
match self.get(name)? {
13091309
Value::Function(func) => func.call(args),
1310+
Value::Table(table) => table.call(args),
1311+
Value::UserData(ud) => ud.call(args),
13101312
val => {
13111313
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
13121314
Err(Error::runtime(msg))
@@ -1322,6 +1324,8 @@ impl ObjectLike for Table {
13221324
{
13231325
match self.get(name) {
13241326
Ok(Value::Function(func)) => func.call_async(args),
1327+
Ok(Value::Table(table)) => table.call_async(args),
1328+
Ok(Value::UserData(ud)) => ud.call_async(args),
13251329
Ok(val) => {
13261330
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
13271331
AsyncCallFuture::error(Error::RuntimeError(msg))

‎src/traits.rs‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,14 @@ pub trait ObjectLike: Sealed {
169169
where
170170
R: FromLuaMulti;
171171

172-
/// Gets the function associated to key `name` from the object and calls it,
172+
/// Gets the callable value associated to key `name` from the object and calls it,
173173
/// passing the object itself along with `args` as function arguments.
174174
fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
175175
where
176176
R: FromLuaMulti;
177177

178-
/// Gets the function associated to key `name` from the object and asynchronously calls it,
179-
/// passing the object itself along with `args` as function arguments.
178+
/// Gets the callable value associated to key `name` from the object and asynchronously calls
179+
/// it, passing the object itself along with `args` as function arguments.
180180
///
181181
/// This might invoke the `__index` metamethod.
182182
#[cfg(feature = "async")]
@@ -185,16 +185,16 @@ pub trait ObjectLike: Sealed {
185185
where
186186
R: FromLuaMulti;
187187

188-
/// Gets the function associated to key `name` from the object and calls it,
188+
/// Gets the callable value associated to key `name` from the object and calls it,
189189
/// passing `args` as function arguments.
190190
///
191191
/// This might invoke the `__index` metamethod.
192192
fn call_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
193193
where
194194
R: FromLuaMulti;
195195

196-
/// Gets the function associated to key `name` from the object and asynchronously calls it,
197-
/// passing `args` as function arguments.
196+
/// Gets the callable value associated to key `name` from the object and asynchronously calls
197+
/// it, passing `args` as function arguments.
198198
///
199199
/// This might invoke the `__index` metamethod.
200200
#[cfg(feature = "async")]

‎src/userdata/object.rs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ impl ObjectLike for AnyUserData {
6363
{
6464
match self.get(name)? {
6565
Value::Function(func) => func.call(args),
66+
Value::Table(table) => table.call(args),
67+
Value::UserData(ud) => ud.call(args),
6668
val => {
6769
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
6870
Err(Error::RuntimeError(msg))
@@ -77,6 +79,8 @@ impl ObjectLike for AnyUserData {
7779
{
7880
match self.get(name) {
7981
Ok(Value::Function(func)) => func.call_async(args),
82+
Ok(Value::Table(table)) => table.call_async(args),
83+
Ok(Value::UserData(ud)) => ud.call_async(args),
8084
Ok(val) => {
8185
let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
8286
AsyncCallFuture::error(Error::RuntimeError(msg))

‎tests/async.rs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,13 @@ async fn test_async_table_object_like() -> Result<()> {
411411
)?;
412412
table.set_metatable(Some(metatable))?;
413413
assert_eq!(table.call_async::<i64>(()).await.unwrap(), 15);
414+
lua.globals().set("callable_table", &table)?;
415+
assert_eq!(
416+
lua.globals()
417+
.call_async_function::<i64>("callable_table", ())
418+
.await?,
419+
15
420+
);
414421

415422
match table.call_async_method::<()>("non_existent", ()).await {
416423
Err(Error::RuntimeError(err)) => {
@@ -465,6 +472,10 @@ async fn test_async_userdata() -> Result<()> {
465472
struct MyUserdata(u64);
466473

467474
impl UserData for MyUserdata {
475+
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
476+
fields.add_field_method_get("callable_userdata", |_, this| Ok(MyUserdata(this.0)));
477+
}
478+
468479
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
469480
methods.add_async_method("get_value", |_, data, ()| async move {
470481
sleep_ms(10).await;
@@ -558,6 +569,13 @@ async fn test_async_userdata() -> Result<()> {
558569
let n: u64 = userdata.call_async_method("get_value", ()).await?;
559570
assert_eq!(n, 24);
560571
userdata.call_async_function::<()>("sleep", 15).await?;
572+
#[cfg(not(any(feature = "lua51", feature = "luau")))]
573+
assert_eq!(
574+
userdata
575+
.call_async_function::<String>("callable_userdata", ())
576+
.await?,
577+
"elapsed:24ms"
578+
);
561579

562580
#[cfg(not(any(feature = "lua51", feature = "luau")))]
563581
assert_eq!(userdata.call_async::<String>(()).await?, "elapsed:24ms");

‎tests/table.rs‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,13 @@ fn test_table_object_like() -> Result<()> {
702702
assert_eq!(table.call_method::<String>("method", "a")?, "method_1");
703703
assert_eq!(table.to_string()?, "table object");
704704

705+
assert_eq!(lua.globals().call_function::<String>("table", "b")?, "call_2");
706+
let callable = lua
707+
.load("return setmetatable({}, {__call = function(_, receiver, key) return receiver[key] end})")
708+
.eval::<Table>()?;
709+
table.set("callable", callable)?;
710+
assert_eq!(table.call_method::<i32>("callable", "a")?, 1);
711+
705712
match table.call_method::<()>("non_existent", ()) {
706713
Err(Error::RuntimeError(err)) => {
707714
assert!(err.contains("attempt to call a nil value (function 'non_existent')"))
@@ -712,6 +719,11 @@ fn test_table_object_like() -> Result<()> {
712719
// Test calling non-callable table
713720
let table2 = lua.create_table()?;
714721
assert!(matches!(table2.call::<()>(()), Err(Error::RuntimeError(_))));
722+
table.set("non_callable", table2)?;
723+
assert!(matches!(
724+
table.call_function::<()>("non_callable", ()),
725+
Err(Error::RuntimeError(_))
726+
));
715727

716728
Ok(())
717729
}

‎tests/userdata.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,6 +976,7 @@ fn test_userdata_object_like() -> Result<()> {
976976

977977
impl UserData for MyUserData {
978978
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
979+
fields.add_field_method_get("callable_userdata", |_, this| Ok(*this));
979980
fields.add_field_method_get("n", |_, this| Ok(this.0));
980981
fields.add_field_method_set("n", |_, this, val| {
981982
this.0 = val;
@@ -1004,6 +1005,7 @@ fn test_userdata_object_like() -> Result<()> {
10041005
}
10051006

10061007
assert_eq!(ud.call::<LuaString>(())?, "called");
1008+
assert_eq!(ud.call_function::<String>("callable_userdata", ())?, "called");
10071009

10081010
ud.call_method::<()>("add", 2)?;
10091011
assert_eq!(ud.get::<u32>("n")?, 323);

0 commit comments

Comments
 (0)