nanvm_lib/js/
any.rs

1use crate::mem::{block::Block, manager::Dealloc, optional_ref::OptionalRef};
2
3use super::{
4    any_cast::AnyCast,
5    any_internal::AnyInternal,
6    bitset::{ref_type, REF_SUBSET_SUPERPOSITION},
7    js_array::JsArrayRef,
8    js_object::JsObjectRef,
9    null::Null,
10    ref_cast::RefCast,
11    type_::Type,
12};
13
14pub type Any<D> = OptionalRef<AnyInternal<D>>;
15
16impl<D: Dealloc> Any<D> {
17    #[inline(always)]
18    unsafe fn u64(&self) -> u64 {
19        self.internal().0
20    }
21    #[inline(always)]
22    pub fn is<T: AnyCast<D>>(&self) -> bool {
23        unsafe { T::has_same_type(self.u64()) }
24    }
25    /// `T` should have the same allocator as `Any`.
26    ///
27    /// ```
28    /// use nanvm_lib::{js::{any::Any, js_string::JsStringRef}, mem::{manager::Dealloc, ref_::Ref}};
29    /// fn dummy<A: Dealloc>(s: JsStringRef<A>) -> Any<A> {
30    ///     Any::move_from(s)
31    /// }
32    /// ```
33    ///
34    /// ```compile_fail
35    /// use nanvm_lib::{js::{any::Any, js_string::JsStringRef}, mem::{manager::Dealloc, ref_::Ref}};
36    /// fn dummy<A: Dealloc, B: Dealloc>(s: JsStringRef<A>) -> Any<B> {
37    ///     Any::move_from(s)
38    /// }
39    /// ```
40    pub fn move_from<T: AnyCast<D>>(t: T) -> Self {
41        t.move_to_any()
42    }
43    #[allow(clippy::result_unit_err)]
44    pub fn try_move<T: AnyCast<D>>(self) -> Result<T, ()> {
45        if self.is::<T>() {
46            return Ok(unsafe { T::from_any_internal(self.move_to_internal().0) });
47        }
48        Err(())
49    }
50    //
51    pub fn get_type(&self) -> Type {
52        if self.is_ref() {
53            match ref_type(unsafe { self.internal().0 }) {
54                0b00 => Type::String,
55                0b01 => Type::Object,
56                0b10 => Type::Array,
57                0b11 => Type::Bigint,
58                _ => unreachable!(),
59            }
60        } else if self.is::<f64>() {
61            Type::Number
62        } else if self.is::<Null>() {
63            Type::Null
64        } else {
65            Type::Bool
66        }
67    }
68    /// `T` should have the same allocator as `Any`.
69    ///
70    /// ```
71    /// use nanvm_lib::{js::{any::Any, js_string::JsStringRef}, mem::{manager::Dealloc, ref_::Ref}};
72    /// fn dummy<A: Dealloc>(a: Any<A>) -> JsStringRef<A> {
73    ///     a.try_move().unwrap()
74    /// }
75    /// ```
76    ///
77    /// ```compile_fail
78    /// use nanvm_lib::{js::{any::Any, js_string::JsStringRef}, mem::{manager::Dealloc, ref_::Ref}};
79    /// fn dummy<A: Dealloc, B: Dealloc>(a: Any<A>) -> JsStringRef, B> {
80    ///     a.try_move().unwrap()
81    /// }
82    /// ```
83    #[allow(clippy::result_unit_err)]
84    #[inline(always)]
85    pub fn try_ref<T: RefCast<D>>(&self) -> Result<&Block<T, D>, ()> {
86        let v = unsafe { self.u64() };
87        if T::REF_SUBSET.has(v) {
88            let p = (v & REF_SUBSET_SUPERPOSITION) as *const Block<T, D>;
89            return Ok(unsafe { &*p });
90        }
91        Err(())
92    }
93
94    /// Iterate trough children of this `Any`. Any type other than array / object yields nothing.
95    /// For an object, first argument of `f` is the key string. For an array, it is the index.
96    pub fn for_each<E>(
97        &self,
98        mut f: impl FnMut(/*k*/ Any<D>, /*v*/ &Any<D>) -> Result<(), E>,
99    ) -> Result<(), E> {
100        match self.get_type() {
101            Type::Object => {
102                let o = self.clone().try_move::<JsObjectRef<D>>().unwrap();
103                for (k, v) in o.items() {
104                    f(k.clone().move_to_any(), v)?;
105                }
106            }
107            Type::Array => {
108                let o = self.clone().try_move::<JsArrayRef<D>>().unwrap();
109                for (k, v) in o.items().iter().enumerate() {
110                    f((k as f64).move_to_any(), v)?;
111                }
112            }
113            _ => {}
114        };
115        Ok(())
116    }
117}
118
119#[cfg(test)]
120mod test {
121    use std::{collections::HashSet, rc::Rc};
122
123    use wasm_bindgen_test::wasm_bindgen_test;
124
125    use crate::{
126        js::{
127            js_array::{new_array, JsArrayRef},
128            js_bigint::{new_bigint, JsBigintRef, Sign},
129            js_object::{new_object, JsObjectRef},
130            js_string::{new_string, JsString, JsStringRef},
131            null::Null,
132        },
133        mem::global::Global,
134    };
135
136    use super::*;
137
138    #[test]
139    #[wasm_bindgen_test]
140    fn test_unsized() {
141        let _x: Rc<[u8]> = Rc::new([1, 3]);
142        // let _y: Rc<(u8, [u8])> = Rc::new((5, [1, 3]));
143        // let r = Vec::default();
144        // let n = 4 + 4;
145        // let _y: Rc<[u8]> = Rc::new([5; n]);
146    }
147
148    #[test]
149    #[wasm_bindgen_test]
150    fn test_number() {
151        type A = Any<Global>;
152        assert_eq!(A::move_from(1.0).try_move(), Ok(1.0));
153        let x: A = A::move_from(-1.0);
154        assert_eq!(x.try_move(), Ok(-1.0));
155        assert_eq!(A::move_from(f64::INFINITY).try_move(), Ok(f64::INFINITY));
156        assert_eq!(
157            A::move_from(f64::NEG_INFINITY).try_move(),
158            Ok(f64::NEG_INFINITY)
159        );
160        assert!(A::move_from(f64::NAN).try_move::<f64>().unwrap().is_nan());
161        //
162        assert_eq!(A::move_from(true).try_move::<f64>(), Err(()));
163        assert_eq!(A::move_from(Null()).try_move::<f64>(), Err(()));
164    }
165
166    #[test]
167    #[wasm_bindgen_test]
168    fn test_bool() {
169        type A = Any<Global>;
170        assert_eq!(A::move_from(true).try_move(), Ok(true));
171        assert_eq!(A::move_from(false).try_move(), Ok(false));
172        //
173        assert_eq!(A::move_from(15.0).try_move::<bool>(), Err(()));
174        assert_eq!(A::move_from(Null()).try_move::<bool>(), Err(()));
175    }
176
177    #[test]
178    #[wasm_bindgen_test]
179    fn test_null() {
180        type A = Any<Global>;
181        assert!(A::move_from(Null()).is::<Null>());
182        //
183        assert!(!A::move_from(-15.7).is::<Null>());
184        assert!(!A::move_from(false).is::<Null>());
185    }
186
187    #[test]
188    #[wasm_bindgen_test]
189    fn test_type() {
190        type A = Any<Global>;
191        assert_eq!(A::move_from(15.0).get_type(), Type::Number);
192        assert_eq!(A::move_from(true).get_type(), Type::Bool);
193        assert_eq!(A::move_from(Null()).get_type(), Type::Null);
194    }
195
196    #[test]
197    #[wasm_bindgen_test]
198    fn test_string() {
199        type A = Any<Global>;
200        type StringRef = JsStringRef<Global>;
201        let sm = new_string(Global(), []);
202        let s = sm.to_ref();
203        assert!(A::move_from(s.clone()).is::<StringRef>());
204        let v = s.items();
205        assert!(v.is_empty());
206
207        //
208        assert!(!A::move_from(15.0).is::<StringRef>());
209        assert!(!A::move_from(true).is::<StringRef>());
210        assert!(!A::move_from(Null()).is::<StringRef>());
211
212        let s = new_string(Global(), [0x20, 0x21]).to_ref();
213        assert!(A::move_from(s.clone()).is::<StringRef>());
214        let v = s.items();
215        assert_eq!(v, [0x20, 0x21]);
216        let u = A::move_from(s);
217        {
218            let s = u.try_ref::<JsString>().unwrap();
219            let items = s.object().items();
220            assert_eq!(items, [0x20, 0x21]);
221        }
222        let s = u.try_move::<StringRef>().unwrap();
223        let items = s.items();
224        assert_eq!(items, [0x20, 0x21]);
225    }
226
227    #[test]
228    #[wasm_bindgen_test]
229    fn test_object() {
230        type A = Any<Global>;
231        type ObjectRef = JsObjectRef<Global>;
232        assert!(!A::move_from(Null()).is::<ObjectRef>());
233
234        let o: ObjectRef = new_object(Global(), []).to_ref();
235        assert!(A::move_from(o.clone()).is::<ObjectRef>());
236        let v = o.items();
237        assert!(v.is_empty());
238        //
239        assert!(!A::move_from(15.0).is::<ObjectRef>());
240        assert!(!A::move_from(true).is::<ObjectRef>());
241
242        let o: ObjectRef = new_object(Global(), []).to_ref();
243        let u = A::move_from(o);
244        assert_eq!(u.get_type(), Type::Object);
245        {
246            let o = u.try_move::<ObjectRef>().unwrap();
247            let items = o.items();
248            assert!(items.is_empty());
249        }
250    }
251
252    #[test]
253    #[wasm_bindgen_test]
254    fn test_array() {
255        type A = Any<Global>;
256        type ArrayRef = JsArrayRef<Global>;
257        assert!(!A::move_from(Null()).is::<ArrayRef>());
258
259        let o: ArrayRef = new_array(Global(), []).to_ref();
260        assert!(A::move_from(o.clone()).is::<ArrayRef>());
261        let v = o.items();
262        assert!(v.is_empty());
263        //
264        assert!(!A::move_from(15.0).is::<ArrayRef>());
265        assert!(!A::move_from(true).is::<ArrayRef>());
266
267        let o: ArrayRef = new_array(Global(), []).to_ref();
268        let u = A::move_from(o);
269        assert_eq!(u.get_type(), Type::Array);
270        {
271            let o = u.try_move::<ArrayRef>().unwrap();
272            let items = o.items();
273            assert!(items.is_empty());
274        }
275    }
276
277    #[test]
278    #[wasm_bindgen_test]
279    fn test_bigint() {
280        type A = Any<Global>;
281        type BigintRef = JsBigintRef<Global>;
282        assert!(!A::move_from(Null()).is::<BigintRef>());
283
284        let o: BigintRef = new_bigint(Global(), Sign::Positive, []).to_ref();
285        assert!(A::move_from(o.clone()).is::<BigintRef>());
286        let v = o.items();
287        assert!(v.is_empty());
288        //
289        assert!(!A::move_from(15.0).is::<BigintRef>());
290        assert!(!A::move_from(true).is::<BigintRef>());
291
292        let o: BigintRef = new_bigint(Global(), Sign::Positive, []).to_ref();
293        let u = A::move_from(o);
294        assert_eq!(u.get_type(), Type::Bigint);
295        {
296            let o = u.try_move::<BigintRef>().unwrap();
297            let items = o.items();
298            assert!(items.is_empty());
299        }
300    }
301
302    #[test]
303    #[wasm_bindgen_test]
304    fn test_eq() {
305        let mut v = HashSet::<Any<Global>>::new();
306        v.insert(Any::move_from(1.0));
307    }
308}