emlite/
lib.rs

1pub mod env;
2use crate::env::*;
3use std::ffi::CStr;
4
5/// Runs JS eval
6#[macro_export]
7macro_rules! eval {
8    ($src: literal) => {{
9        $crate::Val::global("eval").invoke(&[$crate::Val::from_str($src)])
10    }};
11    ($src: literal $(, $arg:expr)* $(,)?) => {{
12        $crate::Val::global("eval").invoke(
13            &[$crate::Val::from_str(&format!($src, $( $arg ),*)) ]
14        )
15    }};
16}
17
18/// A helper macro which packs values into a slice of Val
19#[macro_export]
20macro_rules! argv {
21    ($($rest:expr),*) => {{
22        [$($crate::Val::from($rest)),*]
23    }};
24}
25
26/// A wrapper around a javascript handle
27#[derive(Debug)]
28pub struct Val {
29    inner: Handle,
30}
31
32impl Val {
33    /// Takes the ownership of a handle
34    pub fn take_ownership(handle: Handle) -> Val {
35        Val {
36            inner: handle,
37        }
38    }
39
40    /// Creates a Val object from another
41    pub fn from_val(v: Val) -> Self {
42        unsafe {
43            emlite_val_inc_ref(v.inner);
44        }
45        Val {
46            inner: v.inner,
47        }
48    }
49
50    /// Returns the globalThis object
51    pub fn global_this() -> Val {
52        Val::take_ownership(unsafe { emlite_val_global_this() })
53    }
54
55    /// Gets the property `prop`
56    pub fn get<T: Into<Val>>(&self, prop: T) -> Val {
57        let h = unsafe { emlite_val_get(self.as_handle(), prop.into().as_handle()) };
58        Val::take_ownership(h)
59    }
60
61    /// Gets a global object by `name`
62    pub fn global(name: &str) -> Val {
63        Val::global_this().get(name)
64    }
65
66    /// Gets a js null Val
67    pub fn null() -> Val {
68        Val::take_ownership(unsafe { emlite_val_null() })
69    }
70
71    /// Gets a js undefined Val
72    pub fn undefined() -> Val {
73        Val::take_ownership(unsafe { emlite_val_undefined() })
74    }
75
76    /// Gets a new js object
77    pub fn object() -> Val {
78        Val::take_ownership(unsafe { emlite_val_new_object() })
79    }
80
81    /// Gets a new js array
82    pub fn array() -> Val {
83        Val::take_ownership(unsafe { emlite_val_new_array() })
84    }
85
86    /// Creates a Val from an i32
87    pub fn from_i32(i: i32) -> Val {
88        Val::take_ownership(unsafe { emlite_val_make_int(i) })
89    }
90
91    /// Creates a Val from an f64
92    pub fn from_f64(f: f64) -> Val {
93        Val::take_ownership(unsafe { emlite_val_make_double(f) })
94    }
95
96    /// Creates a Val from str
97    #[allow(clippy::should_implement_trait)]
98    pub fn from_str(s: &str) -> Val {
99        Val::take_ownership(unsafe { emlite_val_make_str(s.as_ptr() as _, s.len()) })
100    }
101
102    /// Returns the raw js handle
103    #[inline(always)]
104    pub fn as_handle(&self) -> Handle {
105        self.inner
106    }
107
108    /// Set the underlying js object property `prop` to `val`
109    pub fn set<K: Into<Val>, V: Into<Val>>(&self, prop: K, val: V) {
110        unsafe {
111            emlite_val_set(
112                self.as_handle(),
113                prop.into().as_handle(),
114                val.into().as_handle(),
115            )
116        };
117    }
118
119    /// Checks whether a property `prop` exists
120    pub fn has<T: Into<Val>>(&self, prop: T) -> bool {
121        unsafe { emlite_val_has(self.as_handle(), prop.into().as_handle()) }
122    }
123
124    /// Checks whether a non-inherited property `prop` exists
125    pub fn has_own_property(&self, prop: &str) -> bool {
126        unsafe { emlite_val_obj_has_own_prop(self.as_handle(), prop.as_ptr() as _, prop.len()) }
127    }
128
129    /// Gets the typeof the underlying js object
130    pub fn type_of(&self) -> String {
131        unsafe {
132            let ptr = emlite_val_typeof(self.as_handle());
133            String::from_utf8_lossy(CStr::from_ptr(ptr).to_bytes()).to_string()
134        }
135    }
136
137    /// Gets the element at index `idx`. Assumes the underlying js type is indexable
138    pub fn at<T: Into<Val>>(&self, idx: T) -> Val {
139        Val::take_ownership(unsafe { emlite_val_get(self.as_handle(), idx.into().as_handle()) })
140    }
141
142    /// Gets the underlying i32 value of a js object
143    pub fn as_i32(&self) -> i32 {
144        unsafe { emlite_val_get_value_int(self.as_handle()) as i32 }
145    }
146
147    /// Gets the underlying boolean value of a js object
148    pub fn as_bool(&self) -> bool {
149        self.as_handle() > 3
150    }
151
152    /// Gets the underlying f64 value of a js object
153    pub fn as_f64(&self) -> f64 {
154        unsafe { emlite_val_get_value_double(self.as_handle()) as _ }
155    }
156
157    /// Gets the underlying string value of a js object
158    pub fn as_string(&self) -> String {
159        unsafe {
160            let ptr = emlite_val_get_value_string(self.as_handle());
161            String::from_utf8_lossy(CStr::from_ptr(ptr).to_bytes()).to_string()
162        }
163    }
164
165    /// Converts the underlying js array to an Vec of i32
166    pub fn to_vec_i32(&self) -> Vec<i32> {
167        let len = self.get("length").as_i32();
168        let mut v: Vec<i32> = vec![];
169        for i in 0..len {
170            v.push(self.at::<i32>(i as _).as_i32());
171        }
172        v
173    }
174
175    /// Converts the underlying js array to an Vec of f64
176    pub fn to_vec_f64(&self) -> Vec<f64> {
177        let len = self.get("length").as_i32();
178        let mut v: Vec<f64> = vec![];
179        for i in 0..len {
180            v.push(self.at::<f64>(i as _).as_f64());
181        }
182        v
183    }
184
185    /// Calls the method `f` with `args`, can return an undefined js value
186    pub fn call(&self, f: &str, args: &[Val]) -> Val {
187        unsafe {
188            let arr = Val::take_ownership(emlite_val_new_array());
189            for arg in args {
190                emlite_val_push(arr.as_handle(), arg.as_handle());
191            }
192            Val::take_ownership(emlite_val_obj_call(
193                self.as_handle(),
194                f.as_ptr() as _,
195                f.len(),
196                arr.as_handle(),
197            ))
198        }
199    }
200
201    /// Calls the object's constructor with `args` constructing a new object
202    pub fn new(&self, args: &[Val]) -> Val {
203        unsafe {
204            let arr = Val::take_ownership(emlite_val_new_array());
205            for arg in args {
206                emlite_val_push(arr.as_handle(), arg.as_handle());
207            }
208            Val::take_ownership(emlite_val_construct_new(self.as_handle(), arr.as_handle()))
209        }
210    }
211
212    /// Invokes the function object with `args`, can return an undefined js value
213    pub fn invoke(&self, args: &[Val]) -> Val {
214        unsafe {
215            let arr = Val::take_ownership(emlite_val_new_array());
216            for arg in args {
217                emlite_val_push(arr.as_handle(), arg.as_handle());
218            }
219            Val::take_ownership(emlite_val_func_call(self.as_handle(), arr.as_handle()))
220        }
221    }
222
223    /// Creates js function from a function pointer and returns its handle wrapped in a Val object
224    pub fn make_fn_raw(f: fn(Handle, Handle) -> Handle, data: Handle) -> Val {
225        let idx: u32 = f as usize as u32;
226        unsafe { Val::take_ownership(emlite_val_make_callback(idx, data)) }
227    }
228
229    /// Creates a js function from a Rust closure and returns a Val
230    pub fn make_fn<F: FnMut(&[Val]) -> Val>(cb: F) -> Val {
231        fn shim(args: Handle, data: Handle) -> Handle {
232            let v = Val::take_ownership(args);
233            let sz = v.get("length").as_i32() as usize;
234            let mut vals: Vec<Val> = Vec::with_capacity(sz);
235            for i in 0..sz {
236                vals.push(v.at(i as i32));
237            }
238            let func0 = Val::take_ownership(data);
239            let a = func0.as_i32() as usize as *mut Box<dyn FnMut(&[Val]) -> Val>;
240            let f: &mut (dyn FnMut(&[Val]) -> Val) = unsafe { &mut **a };
241            std::mem::forget(func0);
242            f(&vals).as_handle()
243        }
244        let a: *mut Box<dyn FnMut(&[Val]) -> Val> = Box::into_raw(Box::new(Box::new(cb)));
245        let data = Val::from_i32(a as Handle as _);
246        unsafe {
247            emlite_val_inc_ref(data.as_handle());
248        }
249        Self::make_fn_raw(shim, data.as_handle())
250    }
251
252    /// Awaits the invoked function object
253    pub fn await_(&self) -> Val {
254        eval!(
255            r#"
256            (async () => {{
257                let obj = EMLITE_VALMAP.toValue({});
258                let ret = await obj;
259                return EMLITE_VALMAP.toHandle(ret);
260            }})()
261        "#,
262            self.as_handle()
263        )
264    }
265
266    /// Decrements the refcount of the underlying handle
267    pub fn delete(v: Val) {
268        unsafe {
269            emlite_val_dec_ref(v.as_handle());
270        }
271    }
272
273    /// Throws a js object represented by Val
274    pub fn throw(v: Val) {
275        unsafe {
276            emlite_val_throw(v.as_handle());
277        }
278    }
279
280    /// Checks whether this Val is an instanceof `v`
281    pub fn instanceof(&self, v: Val) -> bool {
282        unsafe { emlite_val_instanceof(self.as_handle(), v.as_handle()) }
283    }
284}
285
286impl From<i32> for Val {
287    fn from(v: i32) -> Self {
288        Val::from_i32(v)
289    }
290}
291
292impl From<f64> for Val {
293    fn from(v: f64) -> Self {
294        Val::from_f64(v)
295    }
296}
297
298impl From<()> for Val {
299    fn from(_: ()) -> Self {
300        Val::undefined()
301    }
302}
303
304impl From<&str> for Val {
305    fn from(item: &str) -> Self {
306        Val::from_str(item)
307    }
308}
309
310impl From<String> for Val {
311    fn from(item: String) -> Self {
312        Val::from_str(&item)
313    }
314}
315
316impl Drop for Val {
317    fn drop(&mut self) {
318        unsafe { emlite_val_dec_ref(self.as_handle()) }
319    }
320}
321
322impl Clone for Val {
323    fn clone(&self) -> Val {
324        unsafe { emlite_val_inc_ref(self.as_handle()); }
325        Val::take_ownership(self.as_handle())
326    }
327}
328
329use std::ops::{Deref, DerefMut};
330
331/// A console wrapper
332#[derive(Clone, Debug)]
333pub struct Console {
334    val: Val,
335}
336
337impl Console {
338    /// Gets the console
339    pub fn get() -> Console {
340        Console {
341            val: Val::global("console"),
342        }
343    }
344
345    /// Logs into the console
346    pub fn log(&self, args: &[Val]) {
347        self.val.call("log", args);
348    }
349
350    /// console.warn
351    pub fn warn(&self, args: &[Val]) {
352        self.val.call("warn", args);
353    }
354
355    /// console.info
356    pub fn info(&self, args: &[Val]) {
357        self.val.call("info", args);
358    }
359
360    /// Returns the underlying handle of the console
361    pub fn as_handle(&self) -> Handle {
362        self.val.as_handle()
363    }
364}
365
366impl Deref for Console {
367    type Target = Val;
368
369    fn deref(&self) -> &Self::Target {
370        &self.val
371    }
372}
373
374impl DerefMut for Console {
375    fn deref_mut(&mut self) -> &mut Self::Target {
376        &mut self.val
377    }
378}
379
380impl Into<Val> for Console {
381    fn into(self) -> Val {
382        Val::take_ownership(self.inner)
383    }
384}
385
386use std::cmp::Ordering;
387use std::ops::Not;
388
389impl PartialEq for Val {
390    fn eq(&self, other: &Val) -> bool {
391        unsafe { emlite_val_strictly_equals(self.as_handle(), other.as_handle()) }
392    }
393}
394
395impl PartialOrd for Val {
396    fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
397        unsafe {
398            if emlite_val_strictly_equals(self.as_handle(), other.as_handle()) {
399                Some(Ordering::Equal)
400            } else if emlite_val_gt(self.as_handle(), other.as_handle()) {
401                Some(Ordering::Greater)
402            } else if emlite_val_lt(self.as_handle(), other.as_handle()) {
403                Some(Ordering::Less)
404            } else {
405                None
406            }
407        }
408    }
409}
410
411impl Not for Val {
412    type Output = bool;
413
414    fn not(self) -> Self::Output {
415        unsafe { emlite_val_not(self.as_handle()) }
416    }
417}