1use emscripten_val_sys::val;
2use std::ffi::{CStr, CString};
3
4use crate::externs::*;
5use crate::id::JsType;
6
7#[allow(non_camel_case_types)]
9pub type EM_VAL = val::EM_VAL;
10
11#[macro_export]
14macro_rules! argv {
15 ($($rest:expr),*) => {{
16 &[$(&Val::from($rest)),*]
17 }};
18}
19
20#[repr(C)]
22#[derive(Eq)]
23pub struct Val {
24 handle: EM_VAL,
25}
26
27impl Val {
28 fn id() -> val::TYPEID {
29 extern "C" {
30 fn EmvalType() -> val::TYPEID;
31 }
32 unsafe { EmvalType() }
33 }
34 pub fn global(name: &str) -> Self {
36 let name = CString::new(name).unwrap();
37 Self {
38 handle: unsafe { val::_emval_get_global(name.as_ptr()) },
39 }
40 }
41
42 pub fn take_ownership(v: val::EM_VAL) -> Self {
44 Self { handle: v }
45 }
46
47 pub fn from_val(v: &Val) -> Self {
49 let handle = v.as_handle();
50 if v.uses_ref_count() {
51 unsafe {
52 val::_emval_incref(handle);
53 }
54 }
55 Self { handle }
56 }
57
58 pub fn undefined() -> Self {
60 Self {
61 handle: val::_EMVAL_UNDEFINED as EM_VAL,
62 }
63 }
64
65 pub fn object() -> Self {
67 Self {
68 handle: unsafe { val::_emval_new_object() },
69 }
70 }
71
72 pub fn null() -> Self {
74 Self {
75 handle: val::_EMVAL_NULL as EM_VAL,
76 }
77 }
78
79 pub fn array() -> Self {
81 Self {
82 handle: unsafe { val::_emval_new_array() },
83 }
84 }
85
86 #[allow(clippy::should_implement_trait)]
88 pub fn from_str(s: &str) -> Self {
89 let s = CString::new(s).unwrap();
90 Self {
91 handle: unsafe { val::_emval_new_cstring(s.as_ptr() as _) },
92 }
93 }
94
95 pub fn module_property(s: &str) -> Self {
97 let s = CString::new(s).unwrap();
98 Self {
99 handle: unsafe { val::_emval_get_module_property(s.as_ptr() as _) },
100 }
101 }
102
103 pub fn from_array<T: Clone + Into<Val>>(arr: &[T]) -> Self {
105 let v = Val::array();
106 for elem in arr {
107 v.call("push", argv![elem.clone().into()]);
108 }
109 v
110 }
111
112 pub fn as_handle(&self) -> EM_VAL {
114 self.handle
115 }
116
117 pub fn call(&self, f: &str, args: &[&Val]) -> Val {
119 unsafe {
120 let typeids = vec![Val::id(); args.len() + 1];
121 let f = CString::new(f).unwrap();
122 let caller = val::_emval_create_invoker(
123 typeids.len() as u32,
124 typeids.as_ptr() as _,
125 val::EM_INVOKER_KIND_METHOD
126 );
127
128 for arg in args {
129 val::_emval_incref(arg.handle);
130 }
131
132 let ret = val::_emval_invoke(
133 caller,
134 self.handle,
135 f.as_ptr() as _,
136 std::ptr::null_mut(),
137 *(args.as_ptr() as *const *const ()) as _,
138 );
139
140 let ret_wire = crate::id::GenericWireType(ret);
142 let ret_handle = ret_wire.0 as usize as EM_VAL;
143
144 let handle_value = ret_handle as usize;
146 if handle_value <= val::_EMVAL_LAST_RESERVED_HANDLE as usize {
147 Val { handle: ret_handle }
149 } else {
150 Val::take_ownership(ret_handle)
152 }
153 }
154 }
155
156 pub fn get<T: Clone + Into<Val>>(&self, prop: &T) -> Val {
158 let prop: Val = prop.clone().into();
159 Val {
160 handle: unsafe { val::_emval_get_property(self.handle, prop.handle) },
161 }
162 }
163
164 pub fn set<T: Clone + Into<Val>, U: Clone + Into<Val>>(&self, prop: &T, val: &U) {
166 let prop: Val = prop.clone().into();
167 let val: Val = val.clone().into();
168 unsafe { val::_emval_set_property(self.handle, prop.handle, val.handle) };
169 }
170
171 pub fn from_<T: JsType>(v: T) -> Self {
173 unsafe {
174 let handle = match T::signature() {
180 'p' => {
181 let boxed = Box::new(v);
182 let mut ptr: *mut T = Box::into_raw(boxed);
183 val::_emval_take_value(T::id(), (&mut ptr as *mut *mut T) as _)
184 }
185 _ => val::_emval_take_value(T::id(), (&v as *const T) as _),
186 };
187 Self { handle }
188 }
189 }
190
191 pub fn as_<T: JsType>(&self) -> T {
193 unsafe {
194 T::from_generic_wire_type(crate::id::GenericWireType(val::_emval_as(
195 self.handle,
196 T::id(),
197 std::ptr::null_mut(),
198 )))
199 }
200 }
201
202 pub fn as_i32(&self) -> i32 {
204 unsafe { val::_emval_as(self.handle, i32::id(), std::ptr::null_mut()) as i32 }
205 }
206
207 fn uses_ref_count(&self) -> bool {
209 self.handle > val::_EMVAL_LAST_RESERVED_HANDLE as EM_VAL
210 }
211
212 pub fn release_ownership(&mut self) -> EM_VAL {
214 let h = self.handle;
215 self.handle = std::ptr::null_mut();
216 h
217 }
218
219 pub fn has_own_property(&self, key: &str) -> bool {
221 Val::global("Object")
222 .get(&"prototype")
223 .get(&"hasOwnProperty")
224 .call("call", argv![self.clone(), key])
225 .as_::<bool>()
226 }
227
228 pub fn is_null(&self) -> bool {
230 self.handle == val::_EMVAL_NULL as EM_VAL
231 }
232
233 pub fn is_undefined(&self) -> bool {
235 self.handle == val::_EMVAL_UNDEFINED as EM_VAL
236 }
237
238 pub fn is_true(&self) -> bool {
240 self.handle == val::_EMVAL_TRUE as EM_VAL
241 }
242
243 pub fn is_false(&self) -> bool {
245 self.handle == val::_EMVAL_FALSE as EM_VAL
246 }
247
248 pub fn is_number(&self) -> bool {
250 unsafe { val::_emval_is_number(self.handle) }
251 }
252
253 pub fn is_string(&self) -> bool {
255 unsafe { val::_emval_is_string(self.handle) }
256 }
257
258 pub fn instanceof(&self, v: &Val) -> bool {
260 unsafe { val::_emval_instanceof(self.as_handle(), v.as_handle()) }
261 }
262
263 pub fn is_array(&self) -> bool {
265 self.instanceof(&Val::global("Array"))
266 }
267
268 pub fn is_in(&self, v: &Val) -> bool {
270 unsafe { val::_emval_in(self.as_handle(), v.as_handle()) }
271 }
272
273 pub fn type_of(&self) -> Val {
275 Val {
276 handle: unsafe { val::_emval_typeof(self.handle) },
277 }
278 }
279
280 pub fn throw(&self) -> bool {
282 unsafe { val::_emval_throw(self.as_handle()) }
283 }
284
285 pub fn await_(&self) -> Val {
287 Val {
288 handle: unsafe { val::_emval_await(self.handle) },
289 }
290 }
291
292 pub fn delete<T: Clone + Into<Val>>(&self, prop: &T) -> bool {
294 unsafe { val::_emval_delete(self.as_handle(), prop.clone().into().as_handle()) }
295 }
296
297 pub fn new(&self, args: &[&Val]) -> Val {
299 unsafe {
300 let typeids = vec![Val::id(); args.len() + 1];
301 let caller = val::_emval_create_invoker(
302 typeids.len() as u32,
303 typeids.as_ptr() as _,
304 val::EM_INVOKER_KIND_CONSTRUCTOR
305 );
306 for arg in args {
307 val::_emval_incref(arg.handle);
308 }
309
310 let ret = val::_emval_invoke(
311 caller,
312 self.handle,
313 std::ptr::null_mut(),
314 std::ptr::null_mut(),
315 *(args.as_ptr() as *const *const ()) as _,
316 );
317
318 let ret_wire = crate::id::GenericWireType(ret);
320 let ret_handle = ret_wire.0 as usize as EM_VAL;
321
322 let handle_value = ret_handle as usize;
324 if handle_value <= val::_EMVAL_LAST_RESERVED_HANDLE as usize {
325 Val { handle: ret_handle }
327 } else {
328 Val::take_ownership(ret_handle)
330 }
331 }
332 }
333
334 fn gt<T: Clone + Into<Val>>(&self, v: &T) -> bool {
335 unsafe { val::_emval_greater_than(self.handle, v.clone().into().handle) }
336 }
337
338 fn lt<T: Clone + Into<Val>>(&self, v: &T) -> bool {
339 unsafe { val::_emval_less_than(self.handle, v.clone().into().handle) }
340 }
341
342 fn equals<T: Clone + Into<Val>>(&self, v: &T) -> bool {
343 unsafe { val::_emval_equals(self.handle, v.clone().into().handle) }
344 }
345
346 pub fn strictly_equals<T: Clone + Into<Val>>(&self, v: &T) -> bool {
348 unsafe { val::_emval_strictly_equals(self.handle, v.clone().into().handle) }
349 }
350
351 pub fn not(&self) -> bool {
353 unsafe { val::_emval_not(self.handle) }
354 }
355
356 pub fn add_event_listener<F: (FnMut(&Val) -> Val) + 'static>(&self, ev: &str, f: F) {
359 unsafe {
360 let a: *mut Box<dyn FnMut(&Val) -> Val> = Box::into_raw(Box::new(Box::new(f)));
361 let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
362 let ev = CString::new(ev).unwrap();
363 _emval_add_event_listener(self.handle, ev.as_ptr() as _, data as _);
364 }
365 }
366
367 pub fn from_fn0<F: (FnMut() -> Val) + 'static>(f: F) -> Val {
369 unsafe {
370 let a: *mut Box<dyn FnMut() -> Val> = Box::into_raw(Box::new(Box::new(f)));
371 let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
372 Self {
373 handle: _emval_take_fn(0, data as _),
374 }
375 }
376 }
377
378 pub fn from_fn1<F: (FnMut(&Val) -> Val) + 'static>(f: F) -> Val {
380 unsafe {
381 #[allow(clippy::type_complexity)]
382 let a: *mut Box<dyn FnMut(&Val) -> Val> = Box::into_raw(Box::new(Box::new(f)));
383 let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
384 Self {
385 handle: _emval_take_fn(1, data as _),
386 }
387 }
388 }
389
390 pub fn from_fn2<F: (FnMut(&Val, &Val) -> Val) + 'static>(f: F) -> Val {
392 unsafe {
393 #[allow(clippy::type_complexity)]
394 let a: *mut Box<dyn FnMut(&Val, &Val) -> Val> = Box::into_raw(Box::new(Box::new(f)));
395 let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
396 Self {
397 handle: _emval_take_fn(2, data as _),
398 }
399 }
400 }
401
402 pub fn from_fn3<F: (FnMut(&Val, &Val, &Val) -> Val) + 'static>(f: F) -> Val {
404 unsafe {
405 #[allow(clippy::type_complexity)]
406 let a: *mut Box<dyn FnMut(&Val, &Val, &Val) -> Val> =
407 Box::into_raw(Box::new(Box::new(f)));
408 let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
409 Self {
410 handle: _emval_take_fn(3, data as _),
411 }
412 }
413 }
414
415 pub fn from_fn4<F: (FnMut(&Val, &Val, &Val, &Val) -> Val) + 'static>(f: F) -> Val {
417 unsafe {
418 #[allow(clippy::type_complexity)]
419 let a: *mut Box<dyn FnMut(&Val, &Val, &Val, &Val) -> Val> =
420 Box::into_raw(Box::new(Box::new(f)));
421 let data: *mut std::os::raw::c_void = a as *mut std::os::raw::c_void;
422 Self {
423 handle: _emval_take_fn(4, data as _),
424 }
425 }
426 }
427
428 pub fn as_string(&self) -> String {
430 unsafe {
431 let ptr = _emval_as_str(self.handle);
432 let ret = CStr::from_ptr(ptr).to_string_lossy().to_string();
433 free(ptr as _);
434 ret
435 }
436 }
437
438 pub fn to_vec<T: JsType>(&self) -> Vec<T> {
440 let len = self.get(&"length").as_::<u32>();
441 let mut v: Vec<T> = vec![];
442 for i in 0..len {
443 v.push(self.get(&i).as_::<T>());
444 }
445 v
446 }
447}
448
449use std::cmp::Ordering;
450
451impl Default for Val {
452 fn default() -> Val {
453 Val::null()
454 }
455}
456
457impl Drop for Val {
458 fn drop(&mut self) {
459 if self.uses_ref_count() {
460 unsafe {
461 val::_emval_decref(self.as_handle());
462 }
463 self.handle = std::ptr::null_mut();
464 }
465 }
466}
467
468impl Clone for Val {
469 fn clone(&self) -> Self {
470 if self.uses_ref_count() {
471 unsafe {
472 val::_emval_incref(self.handle);
473 }
474 }
475 Self {
476 handle: self.handle,
477 }
478 }
479}
480
481impl<T: JsType> From<T> for Val {
482 fn from(v: T) -> Self {
483 Val::from_(v)
484 }
485}
486
487impl From<()> for Val {
488 fn from(_: ()) -> Self {
489 Val::null()
490 }
491}
492
493impl From<&Val> for Val {
494 fn from(item: &Val) -> Self {
495 Val::from_val(item)
496 }
497}
498
499impl From<&str> for Val {
500 fn from(item: &str) -> Self {
501 Val::from_str(item)
502 }
503}
504
505impl From<String> for Val {
506 fn from(item: String) -> Self {
507 Val::from_str(&item)
508 }
509}
510
511impl PartialEq for Val {
512 fn eq(&self, other: &Val) -> bool {
513 self.equals(other)
514 }
515}
516
517impl PartialOrd for Val {
518 fn partial_cmp(&self, other: &Val) -> Option<Ordering> {
519 if self.equals(other) {
520 Some(Ordering::Equal)
521 } else if self.gt(other) {
522 Some(Ordering::Greater)
523 } else if self.lt(other) {
524 Some(Ordering::Less)
525 } else {
526 None
527 }
528 }
529}