diff --git a/crates/rune/src/runtime/protocol.rs b/crates/rune/src/runtime/protocol.rs index 8912a8a83..915ae7bcf 100644 --- a/crates/rune/src/runtime/protocol.rs +++ b/crates/rune/src/runtime/protocol.rs @@ -81,6 +81,18 @@ impl Protocol { hash: Hash::new(0x418f5becbf885806), }; + /// The function to access a field by name when Protocol::GET fails. + pub const FALLBACK_GET: Protocol = Protocol { + name: "fallback_get", + hash: Hash::new(0x6dda58b140dfeaf9), + }; + + /// The function to set a field by name when Protocol::SET fails. + pub const FALLBACK_SET: Protocol = Protocol { + name: "fallback_set", + hash: Hash::new(0xbe28c02896ca0b64), + }; + /// The function to access a field. pub const GET: Protocol = Protocol { name: "get", diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index c0e1439ca..bb721d080 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -926,12 +926,18 @@ impl Vm { } } target => { - let hash = index.hash(); - - return Ok(match self.call_field_fn(Protocol::GET, target, hash, ())? { - CallResult::Ok(()) => CallResult::Ok(self.stack.pop()?), - CallResult::Unsupported(target) => CallResult::Unsupported(target), - }); + let index = index.clone(); + return Ok( + match self.call_field_fn(Protocol::GET, target, index.hash(), ())? { + CallResult::Ok(()) => return Ok(CallResult::Ok(self.stack.pop()?)), + CallResult::Unsupported(target) => { + match self.call_instance_fn(target, Protocol::FALLBACK_GET, (index,))? { + CallResult::Ok(()) => CallResult::Ok(self.stack.pop()?), + CallResult::Unsupported(target) => CallResult::Unsupported(target), + } + } + }, + ); } } @@ -983,15 +989,33 @@ impl Vm { })); } target => { - let hash = field.hash(); - - match self.call_field_fn(Protocol::SET, target, hash, (value,))? { - CallResult::Ok(()) => { - self.stack.pop()?; - CallResult::Ok(()) - } - result => result, - } + let index = field.clone(); + return Ok( + match self.call_field_fn( + Protocol::SET, + target, + index.hash(), + (value.clone(),), + )? { + CallResult::Ok(()) => { + self.stack.pop()?; + CallResult::Ok(()) + } + CallResult::Unsupported(target) => { + match self.call_instance_fn( + target, + Protocol::FALLBACK_SET, + (index, value), + )? { + CallResult::Ok(()) => { + self.stack.pop()?; + CallResult::Ok(()) + } + CallResult::Unsupported(target) => CallResult::Unsupported(target), + } + } + }, + ); } }) }