soroban_wasmi/
externref.rs1use crate::{
2 collections::arena::ArenaIndex,
3 core::UntypedVal,
4 reftype::Transposer,
5 store::Stored,
6 AsContextMut,
7 StoreContext,
8};
9use core::{any::Any, num::NonZeroU32};
10use std::boxed::Box;
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
14pub struct ExternObjectIdx(NonZeroU32);
15
16impl ArenaIndex for ExternObjectIdx {
17 fn into_usize(self) -> usize {
18 self.0.get().wrapping_sub(1) as usize
19 }
20
21 fn from_usize(index: usize) -> Self {
22 index
23 .try_into()
24 .ok()
25 .map(|index: u32| index.wrapping_add(1))
26 .and_then(NonZeroU32::new)
27 .map(Self)
28 .unwrap_or_else(|| panic!("out of bounds extern object index {index}"))
29 }
30}
31
32#[derive(Debug)]
34pub struct ExternObjectEntity {
35 inner: Box<dyn 'static + Any + Send + Sync>,
36}
37
38impl ExternObjectEntity {
39 pub fn new<T>(object: T) -> Self
41 where
42 T: 'static + Any + Send + Sync,
43 {
44 Self {
45 inner: Box::new(object),
46 }
47 }
48
49 pub fn data(&self) -> &dyn Any {
51 &*self.inner
52 }
53}
54
55#[derive(Debug, Copy, Clone)]
57#[repr(transparent)]
58pub struct ExternObject(Stored<ExternObjectIdx>);
59
60impl ExternObject {
61 pub(crate) fn from_inner(stored: Stored<ExternObjectIdx>) -> Self {
63 Self(stored)
64 }
65
66 pub(crate) fn as_inner(&self) -> &Stored<ExternObjectIdx> {
68 &self.0
69 }
70
71 pub fn new<T>(mut ctx: impl AsContextMut, object: T) -> Self
73 where
74 T: 'static + Any + Send + Sync,
75 {
76 ctx.as_context_mut()
77 .store
78 .inner
79 .alloc_extern_object(ExternObjectEntity::new(object))
80 }
81
82 pub fn data<'a, T: 'a>(&self, ctx: impl Into<StoreContext<'a, T>>) -> &'a dyn Any {
88 ctx.into().store.inner.resolve_external_object(self).data()
89 }
90}
91
92#[derive(Debug, Default, Copy, Clone)]
94#[repr(transparent)]
95pub struct ExternRef {
96 inner: Option<ExternObject>,
97}
98
99#[test]
100fn externref_sizeof() {
101 use core::mem::size_of;
107 assert_eq!(size_of::<ExternRef>(), size_of::<u64>());
108 assert_eq!(size_of::<ExternRef>(), size_of::<UntypedVal>());
109 assert_eq!(size_of::<ExternRef>(), size_of::<ExternObject>());
110}
111
112#[test]
113fn externref_null_to_zero() {
114 assert_eq!(UntypedVal::from(ExternRef::null()), UntypedVal::from(0));
115 assert!(ExternRef::from(UntypedVal::from(0)).is_null());
116}
117
118impl From<UntypedVal> for ExternRef {
119 fn from(untyped: UntypedVal) -> Self {
120 unsafe { <Transposer<Self>>::from(untyped).reftype }.canonicalize()
126 }
127}
128
129impl From<ExternRef> for UntypedVal {
130 fn from(externref: ExternRef) -> Self {
131 let externref = externref.canonicalize();
132 Self::from(unsafe { <Transposer<ExternRef>>::new(externref).value })
138 }
139}
140
141impl ExternRef {
142 pub fn new<T>(ctx: impl AsContextMut, object: impl Into<Option<T>>) -> Self
144 where
145 T: 'static + Any + Send + Sync,
146 {
147 object
148 .into()
149 .map(|object| ExternObject::new(ctx, object))
150 .map(Self::from_object)
151 .unwrap_or_else(Self::null)
152 .canonicalize()
153 }
154
155 fn canonicalize(self) -> Self {
165 if self.is_null() {
166 return unsafe { <Transposer<Self>>::null().reftype };
169 }
170 self
171 }
172
173 fn from_object(object: ExternObject) -> Self {
175 Self {
176 inner: Some(object),
177 }
178 }
179
180 pub fn null() -> Self {
182 Self { inner: None }.canonicalize()
183 }
184
185 pub fn is_null(&self) -> bool {
187 self.inner.is_none()
188 }
189
190 pub fn data<'a, T: 'a>(&self, ctx: impl Into<StoreContext<'a, T>>) -> Option<&'a dyn Any> {
196 self.inner.map(|object| object.data(ctx))
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use crate::{Engine, Store};
204
205 #[test]
206 fn it_works() {
207 let engine = Engine::default();
208 let mut store = <Store<()>>::new(&engine, ());
209 let value = 42_i32;
210 let obj = ExternObject::new::<i32>(&mut store, value);
211 assert_eq!(obj.data(&store).downcast_ref::<i32>(), Some(&value),);
212 }
213}