From 1147ab593f278718f950215ae82ff4d7f65949d8 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 30 Jul 2018 16:28:35 +0200 Subject: [PATCH] feat: Allow OpaqueValue to be converted into its inner value The deref coercion enables cheap access to Userdata values as well as strings --- examples/marshalling.rs | 7 ++++++ vm/src/api/mod.rs | 54 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/examples/marshalling.rs b/examples/marshalling.rs index bb85b102d7..e2b86f6be9 100644 --- a/examples/marshalling.rs +++ b/examples/marshalling.rs @@ -376,10 +376,17 @@ fn marshal_userdata() -> Result<()> { hwnd "#; + // `UserdataValue` lets us extract a `Clone` of its inner userdata value let (UserdataValue(handle), _) = compiler.run_expr::>(&vm, "test", script)?; assert_eq!(*handle.id, 0); assert_eq!(&*handle.metadata, "Window1"); + + // If cloning would be expansive we can instate use `OpaqueValue` to get a smart pointer to the + // userdata which implements `Deref` for easy access + let (handle, _) = compiler.run_expr::>(&vm, "test", script)?; + assert_eq!(*handle.id, 0); + assert_eq!(&*handle.metadata, "Window1"); Ok(()) } diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index 74e7d39749..e7777dec82 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -552,12 +552,18 @@ where T: ?Sized + VmType, { type Type = T::Type; + fn make_type(vm: &Thread) -> ArcType { T::make_type(vm) } + fn make_forall_type(vm: &Thread) -> ArcType { T::make_forall_type(vm) } + + fn extra_args() -> VmIndex { + T::extra_args() + } } impl<'vm, 'value, T> Getable<'vm, 'value> for UserdataValue @@ -1332,13 +1338,42 @@ where } } -/// Type which represents an array in gluon -/// Type implementing both `Pushable` and `Getable` of values of `V`. -/// The actual value, `V` is not accessible directly but is only intended to be transferred between -/// two different threads. +/// Type implementing both `Pushable` and `Getable` of values of `V` regardless of wheter `V` +/// implements the traits. +/// The actual value, `V` is only accessible directly either by `Deref` if it is `Userdata` or a +/// string or by the `to_value` method if it implements `Getable`. +/// +/// When the value is not accessible the value can only be transferred back into gluon again +/// without inspecting the value itself two different threads. pub struct OpaqueValue(RootedValue, PhantomData) where - T: Deref; + T: Deref, + V: ?Sized; + +impl Deref for OpaqueValue +where + T: Deref, + V: vm::Userdata, +{ + type Target = V; + + fn deref(&self) -> &V { + // The value is rooted by self + unsafe { <&V>::from_value_unsafe(self.vm(), self.get_variant()) } + } +} + +impl Deref for OpaqueValue +where + T: Deref, +{ + type Target = str; + + fn deref(&self) -> &str { + // The value is rooted by self + unsafe { <&str>::from_value_unsafe(self.vm(), self.get_variant()) } + } +} #[cfg(feature = "serde")] impl<'de, V> Deserialize<'de> for OpaqueValue { @@ -1372,6 +1407,7 @@ where impl OpaqueValue where T: Deref, + V: ?Sized, { pub fn from_value(value: RootedValue) -> Self { OpaqueValue(value, PhantomData) @@ -1381,6 +1417,14 @@ where self.0.vm() } + /// Converts the value into its Rust representation + pub fn to_value<'vm>(&'vm self) -> V + where + V: Getable<'vm, 'vm>, + { + V::from_value(self.vm(), self.get_variant()) + } + pub fn into_inner(self) -> RootedValue { self.0 }