1use crate::facades::QuickjsRuntimeFacadeInner;
2use crate::quickjs_utils::objects::construct_object;
3use crate::quickjs_utils::primitives::{from_bool, from_f64, from_i32, from_string_q};
4use crate::quickjs_utils::typedarrays::{
5 detach_array_buffer_buffer_q, get_array_buffer_buffer_copy_q, get_array_buffer_q,
6 new_uint8_array_copy_q, new_uint8_array_q,
7};
8use crate::quickjs_utils::{arrays, errors, functions, get_global_q, json, new_null_ref, objects};
9use crate::quickjsruntimeadapter::{make_cstring, QuickJsRuntimeAdapter};
10use crate::quickjsvalueadapter::{QuickJsValueAdapter, TAG_EXCEPTION};
11use crate::reflection::eventtarget::dispatch_event;
12use crate::reflection::eventtarget::dispatch_static_event;
13use crate::reflection::{new_instance, new_instance3, Proxy};
14use hirofa_utils::auto_id_map::AutoIdMap;
15
16use crate::jsutils::jsproxies::{JsProxy, JsProxyInstanceId};
17use crate::jsutils::{JsError, JsValueType, Script};
18use crate::quickjs_utils::promises::QuickJsPromiseAdapter;
19use crate::values::{
20 CachedJsArrayRef, CachedJsFunctionRef, CachedJsObjectRef, CachedJsPromiseRef, JsValueFacade,
21 TypedArrayType,
22};
23use libquickjs_sys as q;
24use serde_json::Value;
25use std::cell::RefCell;
26use std::collections::HashMap;
27use std::ffi::CString;
28use std::future::Future;
29use std::os::raw::c_void;
30use std::rc::Rc;
31use std::sync::{Arc, Weak};
32
33use crate::jsutils::promises::new_resolving_promise;
34use crate::jsutils::promises::new_resolving_promise_async;
35use string_cache::DefaultAtom;
36
37type ProxyEventListenerMaps = HashMap<
38 String, HashMap<
40 usize, HashMap<
42 String, HashMap<
44 QuickJsValueAdapter, QuickJsValueAdapter, >,
47 >,
48 >,
49>;
50
51type ProxyStaticEventListenerMaps = HashMap<
52 String, HashMap<
54 String, HashMap<
56 QuickJsValueAdapter, QuickJsValueAdapter, >,
59 >,
60>;
61
62pub struct QuickJsRealmAdapter {
63 object_cache: RefCell<AutoIdMap<QuickJsValueAdapter>>,
64 promise_cache: RefCell<AutoIdMap<QuickJsPromiseAdapter>>,
65 pub(crate) proxy_registry: RefCell<HashMap<String, Rc<Proxy>>>, pub(crate) proxy_constructor_refs: RefCell<HashMap<String, QuickJsValueAdapter>>,
67 pub(crate) proxy_event_listeners: RefCell<ProxyEventListenerMaps>,
68 pub(crate) proxy_static_event_listeners: RefCell<ProxyStaticEventListenerMaps>,
69 pub id: String,
70 pub context: *mut q::JSContext,
71}
72
73thread_local! {
74 #[allow(clippy::box_collection)]
75 static ID_REGISTRY: RefCell<HashMap<String, Box<String>>> = RefCell::new(HashMap::new());
76}
77
78impl QuickJsRealmAdapter {
79 pub fn print_stats(&self) {
80 println!(
81 "QuickJsRealmAdapter.object_cache.len = {}",
82 self.object_cache.borrow().len()
83 );
84 println!(
85 "QuickJsRealmAdapter.promise_cache.len = {}",
86 self.promise_cache.borrow().len()
87 );
88
89 println!("-- > QuickJsRealmAdapter.proxy instances");
90 for p in &*self.proxy_registry.borrow() {
91 let prc = p.1.clone();
92 let proxy = &*prc;
93 let mappings = &*proxy.proxy_instance_id_mappings.borrow();
94 println!("---- > {} len:{}", p.0, mappings.len());
95 print!("------ ids: ");
96 for i in mappings {
97 print!("{}, ", i.0);
98 }
99 println!("\n---- < {}", p.0);
100 }
101 println!("-- < QuickJsRealmAdapter.proxy instances");
102
103 let _spsel: &ProxyStaticEventListenerMaps = &self.proxy_static_event_listeners.borrow();
104 let psel: &ProxyEventListenerMaps = &self.proxy_event_listeners.borrow();
105
106 println!("> psel");
107 for a in psel {
108 println!("- psel - {}", a.0);
109 let map = a.1;
110 for b in map {
111 println!("- psel - id {}", b.0);
112 let map_b = b.1;
113 for c in map_b {
114 println!("- psel - id {} - evt {}", b.0, c.0);
115 let map_c = c.1;
116 println!(
117 "- psel - id {} - evt {} - mapC.len={}",
118 b.0,
119 c.0,
120 map_c.len()
121 );
122 for eh in map_c {
123 println!(
125 "- psel - id {} - evt {} - handler:{} options:{}",
126 b.0,
127 c.0,
128 eh.0.to_string().expect("could not toString"),
129 eh.1.to_string().expect("could not toString")
130 );
131 }
132 }
133 }
134 }
135 println!("< psel");
136 }
137
138 pub(crate) fn free(&self) {
139 log::trace!("QuickJsContext:free {}", self.id);
140 {
141 let cache_map = &mut *self.object_cache.borrow_mut();
142 log::trace!(
143 "QuickJsContext:free {}, dropping {} cached objects",
144 self.id,
145 cache_map.len()
146 );
147 cache_map.clear();
148 }
149
150 let mut all_listeners = {
151 let proxy_event_listeners: &mut ProxyEventListenerMaps =
152 &mut self.proxy_event_listeners.borrow_mut();
153 std::mem::take(proxy_event_listeners)
154 };
155 all_listeners.clear();
157
158 let mut all_constructor_refs = {
161 let proxy_constructor_refs = &mut *self.proxy_constructor_refs.borrow_mut();
162 std::mem::take(proxy_constructor_refs)
163 };
164 all_constructor_refs.clear();
165
166 unsafe { q::JS_FreeContext(self.context) };
167
168 log::trace!("after QuickJsContext:free {}", self.id);
169 }
170 pub(crate) fn new(id: String, q_js_rt: &QuickJsRuntimeAdapter) -> Self {
171 let context = unsafe { q::JS_NewContext(q_js_rt.runtime) };
172
173 let mut bx = Box::new(id.clone());
174
175 let ibp: &mut String = &mut bx;
176 let info_ptr = ibp as *mut _ as *mut c_void;
177
178 ID_REGISTRY.with(|rc| {
179 let registry = &mut *rc.borrow_mut();
180 registry.insert(id.clone(), bx);
181 });
182
183 unsafe { q::JS_SetContextOpaque(context, info_ptr) };
184
185 if context.is_null() {
186 panic!("ContextCreationFailed");
187 }
188
189 Self {
190 id,
191 context,
192 object_cache: RefCell::new(AutoIdMap::new_with_max_size(i32::MAX as usize)),
193 promise_cache: RefCell::new(AutoIdMap::new()),
194 proxy_registry: RefCell::new(Default::default()),
195 proxy_constructor_refs: RefCell::new(Default::default()),
196 proxy_event_listeners: RefCell::new(Default::default()),
197 proxy_static_event_listeners: RefCell::new(Default::default()),
198 }
199 }
200 pub unsafe fn get_id(context: *mut q::JSContext) -> &'static str {
204 let info_ptr: *mut c_void = q::JS_GetContextOpaque(context);
205 let info: &mut String = &mut *(info_ptr as *mut String);
206 info
207 }
208 pub fn invoke_function_by_name(
210 &self,
211 namespace: &[&str],
212 func_name: &str,
213 arguments: &[QuickJsValueAdapter],
214 ) -> Result<QuickJsValueAdapter, JsError> {
215 let namespace_ref = unsafe { objects::get_namespace(self.context, namespace, false) }?;
216 functions::invoke_member_function_q(self, &namespace_ref, func_name, arguments)
217 }
218
219 pub fn eval(&self, script: Script) -> Result<QuickJsValueAdapter, JsError> {
221 unsafe { Self::eval_ctx(self.context, script, None) }
222 }
223
224 pub fn eval_this(
225 &self,
226 script: Script,
227 this: QuickJsValueAdapter,
228 ) -> Result<QuickJsValueAdapter, JsError> {
229 unsafe { Self::eval_ctx(self.context, script, Some(this)) }
230 }
231
232 pub unsafe fn eval_ctx(
235 context: *mut q::JSContext,
236 mut script: Script,
237 this_opt: Option<QuickJsValueAdapter>,
238 ) -> Result<QuickJsValueAdapter, JsError> {
239 log::debug!("q_js_rt.eval file {}", script.get_path());
240
241 script = QuickJsRuntimeAdapter::pre_process(script)?;
242
243 let code_str = script.get_runnable_code();
244
245 let filename_c = make_cstring(script.get_path())?;
246 let code_c = make_cstring(code_str)?;
247
248 let value_raw = match this_opt {
249 None => q::JS_Eval(
250 context,
251 code_c.as_ptr(),
252 code_str.len() as _,
253 filename_c.as_ptr(),
254 q::JS_EVAL_TYPE_GLOBAL as i32,
255 ),
256 Some(this) => q::JS_EvalThis(
257 context,
258 this.clone_value_incr_rc(),
259 code_c.as_ptr(),
260 code_str.len() as _,
261 filename_c.as_ptr(),
262 q::JS_EVAL_TYPE_GLOBAL as i32,
263 ),
264 };
265
266 log::trace!("after eval, checking error");
267
268 let ret = QuickJsValueAdapter::new(
270 context,
271 value_raw,
272 false,
273 true,
274 format!("eval result of {}", script.get_path()).as_str(),
275 );
276 if ret.is_exception() {
277 let ex_opt = Self::get_exception(context);
278 if let Some(ex) = ex_opt {
279 log::debug!("eval_ctx failed: {}", ex);
280 Err(ex)
281 } else {
282 Err(JsError::new_str("eval failed and could not get exception"))
283 }
284 } else {
285 Ok(ret)
286 }
287 }
288
289 pub fn eval_module(&self, script: Script) -> Result<QuickJsValueAdapter, JsError> {
291 unsafe { Self::eval_module_ctx(self.context, script) }
292 }
293
294 pub unsafe fn eval_module_ctx(
297 context: *mut q::JSContext,
298 mut script: Script,
299 ) -> Result<QuickJsValueAdapter, JsError> {
300 log::debug!("q_js_rt.eval_module file {}", script.get_path());
301
302 script = QuickJsRuntimeAdapter::pre_process(script)?;
303
304 let code_str = script.get_runnable_code();
305
306 let filename_c = make_cstring(script.get_path())?;
307 let code_c = make_cstring(code_str)?;
308
309 let value_raw = q::JS_Eval(
310 context,
311 code_c.as_ptr(),
312 code_str.len() as _,
313 filename_c.as_ptr(),
314 q::JS_EVAL_TYPE_MODULE as i32,
315 );
316
317 let ret = QuickJsValueAdapter::new(
318 context,
319 value_raw,
320 false,
321 true,
322 format!("eval_module result of {}", script.get_path()).as_str(),
323 );
324
325 log::trace!("evalled module yielded a {}", ret.borrow_value().tag);
326
327 if ret.is_exception() {
330 let ex_opt = Self::get_exception(context);
331 if let Some(ex) = ex_opt {
332 log::debug!("eval_module_ctx failed: {}", ex);
333 Err(ex)
334 } else {
335 Err(JsError::new_str(
336 "eval_module failed and could not get exception",
337 ))
338 }
339 } else {
340 Ok(ret)
341 }
342 }
343 pub fn report_ex(&self, err: &str) -> q::JSValue {
345 unsafe { Self::report_ex_ctx(self.context, err) }
346 }
347 pub unsafe fn report_ex_ctx(context: *mut q::JSContext, err: &str) -> q::JSValue {
351 let c_err = CString::new(err);
352 q::JS_ThrowInternalError(context, c_err.as_ref().ok().unwrap().as_ptr());
353 q::JSValue {
354 u: q::JSValueUnion { int32: 0 },
355 tag: TAG_EXCEPTION,
356 }
357 }
358
359 pub fn get_exception_ctx(&self) -> Option<JsError> {
361 unsafe { errors::get_exception(self.context) }
362 }
363
364 pub unsafe fn get_exception(context: *mut q::JSContext) -> Option<JsError> {
368 errors::get_exception(context)
369 }
370
371 pub fn cache_object(&self, obj: QuickJsValueAdapter) -> i32 {
372 let cache_map = &mut *self.object_cache.borrow_mut();
373 let id = cache_map.insert(obj) as i32;
374 log::trace!("cache_object: id={}, thread={}", id, thread_id::get());
375 id
376 }
377
378 pub fn remove_cached_obj_if_present(&self, id: i32) {
379 log::trace!(
380 "remove_cached_obj_if_present: id={}, thread={}",
381 id,
382 thread_id::get()
383 );
384 let cache_map = &mut *self.object_cache.borrow_mut();
385 if cache_map.contains_key(&(id as usize)) {
386 let _ = cache_map.remove(&(id as usize));
387 }
388 }
389
390 pub fn consume_cached_obj(&self, id: i32) -> QuickJsValueAdapter {
391 log::trace!("consume_cached_obj: id={}, thread={}", id, thread_id::get());
392 let cache_map = &mut *self.object_cache.borrow_mut();
393 cache_map.remove(&(id as usize))
394 }
395
396 pub fn with_cached_obj<C, R>(&self, id: i32, consumer: C) -> R
397 where
398 C: FnOnce(QuickJsValueAdapter) -> R,
399 {
400 log::trace!("with_cached_obj: id={}, thread={}", id, thread_id::get());
401 let clone_ref = {
402 let cache_map = &*self.object_cache.borrow();
403 let opt = cache_map.get(&(id as usize));
404 let cached_ref = opt.expect("no such obj in cache");
405 cached_ref.clone()
406 };
407 consumer(clone_ref)
410 }
411 pub unsafe fn with_context<C, R>(context: *mut q::JSContext, consumer: C) -> R
414 where
415 C: FnOnce(&QuickJsRealmAdapter) -> R,
416 {
417 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
418 let id = QuickJsRealmAdapter::get_id(context);
419 let q_ctx = q_js_rt.get_context(id);
420 consumer(q_ctx)
421 })
422 }
423}
424
425impl Drop for QuickJsRealmAdapter {
426 fn drop(&mut self) {
427 log::trace!("before drop QuickJSContext {}", self.id);
428
429 let id = &self.id;
430 {
431 ID_REGISTRY.with(|rc| {
432 let registry = &mut *rc.borrow_mut();
433 registry.remove(id);
434 });
435 }
436 {
437 let proxies = &mut *self.proxy_registry.borrow_mut();
438 proxies.clear();
439 }
440
441 log::trace!("after drop QuickJSContext {}", self.id);
442 }
443}
444
445impl QuickJsRealmAdapter {
446 pub fn get_realm_id(&self) -> &str {
447 self.id.as_str()
448 }
449
450 pub fn get_runtime_facade_inner(&self) -> Weak<QuickjsRuntimeFacadeInner> {
451 QuickJsRuntimeAdapter::do_with(|rt| {
452 Arc::downgrade(&rt.get_rti_ref().expect("Runtime was dropped"))
453 })
454 }
455
456 pub fn get_script_or_module_name(&self) -> Result<String, JsError> {
457 crate::quickjs_utils::get_script_or_module_name_q(self)
458 }
459
460 pub fn install_proxy(
461 &self,
462 proxy: JsProxy,
463 add_global_var: bool,
464 ) -> Result<QuickJsValueAdapter, JsError> {
465 proxy.install(self, add_global_var)
468 }
469
470 pub fn instantiate_proxy_with_id(
471 &self,
472 namespace: &[&str],
473 class_name: &str,
474 instance_id: usize,
475 ) -> Result<QuickJsValueAdapter, JsError> {
476 let cn = if namespace.is_empty() {
478 class_name.to_string()
479 } else {
480 format!("{}.{}", namespace.join("."), class_name)
481 };
482
483 let proxy_map = self.proxy_registry.borrow();
484 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
485
486 new_instance3(proxy, instance_id, self)
487 }
488
489 pub fn instantiate_proxy(
490 &self,
491 namespace: &[&str],
492 class_name: &str,
493 arguments: &[QuickJsValueAdapter],
494 ) -> Result<(JsProxyInstanceId, QuickJsValueAdapter), JsError> {
495 let cn = if namespace.is_empty() {
497 class_name.to_string()
498 } else {
499 format!("{}.{}", namespace.join("."), class_name)
500 };
501
502 let proxy_map = self.proxy_registry.borrow();
503 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
504
505 let instance_info = new_instance(cn.as_str(), self)?;
506
507 if let Some(constructor) = &proxy.constructor {
508 QuickJsRuntimeAdapter::do_with(|rt| constructor(rt, self, instance_info.0, arguments))?
510 }
511
512 Ok(instance_info)
513 }
514
515 pub fn dispatch_proxy_event(
516 &self,
517 namespace: &[&str],
518 class_name: &str,
519 proxy_instance_id: &usize,
520 event_id: &str,
521 event_obj: &QuickJsValueAdapter,
522 ) -> Result<bool, JsError> {
523 let cn = if namespace.is_empty() {
525 class_name.to_string()
526 } else {
527 format!("{}.{}", namespace.join("."), class_name)
528 };
529
530 let proxy_map = self.proxy_registry.borrow();
531 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
532
533 dispatch_event(self, proxy, *proxy_instance_id, event_id, event_obj.clone())
534 }
535
536 pub fn dispatch_static_proxy_event(
537 &self,
538 namespace: &[&str],
539 class_name: &str,
540 event_id: &str,
541 event_obj: &QuickJsValueAdapter,
542 ) -> Result<bool, JsError> {
543 let cn = if namespace.is_empty() {
545 class_name.to_string()
546 } else {
547 format!("{}.{}", namespace.join("."), class_name)
548 };
549
550 let proxy_map = self.proxy_registry.borrow();
551 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
552
553 dispatch_static_event(
554 self,
555 proxy.get_class_name().as_str(),
556 event_id,
557 event_obj.clone(),
558 )
559 }
560
561 pub fn install_function(
562 &self,
563 namespace: &[&str],
564 name: &str,
565 js_function: fn(
566 &QuickJsRuntimeAdapter,
567 &Self,
568 &QuickJsValueAdapter,
569 &[QuickJsValueAdapter],
570 ) -> Result<QuickJsValueAdapter, JsError>,
571 arg_count: u32,
572 ) -> Result<(), JsError> {
573 let ns = self.get_namespace(namespace)?;
575
576 let func = functions::new_function_q(
577 self,
578 name,
579 move |ctx, this, args| {
580 QuickJsRuntimeAdapter::do_with(|rt| js_function(rt, ctx, this, args))
581 },
582 arg_count,
583 )?;
584 self.set_object_property(&ns, name, &func)?;
585 Ok(())
586 }
587
588 pub fn install_closure<
589 F: Fn(
590 &QuickJsRuntimeAdapter,
591 &Self,
592 &QuickJsValueAdapter,
593 &[QuickJsValueAdapter],
594 ) -> Result<QuickJsValueAdapter, JsError>
595 + 'static,
596 >(
597 &self,
598 namespace: &[&str],
599 name: &str,
600 js_function: F,
601 arg_count: u32,
602 ) -> Result<(), JsError> {
603 let ns = self.get_namespace(namespace)?;
605
606 let func = functions::new_function_q(
607 self,
608 name,
609 move |ctx, this, args| {
610 QuickJsRuntimeAdapter::do_with(|rt| js_function(rt, ctx, this, args))
611 },
612 arg_count,
613 )?;
614 self.set_object_property(&ns, name, &func)?;
615 Ok(())
616 }
617
618 pub fn get_global(&self) -> Result<QuickJsValueAdapter, JsError> {
619 Ok(get_global_q(self))
620 }
621
622 pub fn get_namespace(&self, namespace: &[&str]) -> Result<QuickJsValueAdapter, JsError> {
623 objects::get_namespace_q(self, namespace, true)
624 }
625
626 pub fn invoke_function_on_object_by_name(
627 &self,
628 this_obj: &QuickJsValueAdapter,
629 method_name: &str,
630 args: &[QuickJsValueAdapter],
631 ) -> Result<QuickJsValueAdapter, JsError> {
632 functions::invoke_member_function_q(self, this_obj, method_name, args)
633 }
634
635 pub fn invoke_function(
636 &self,
637 this_obj: Option<&QuickJsValueAdapter>,
638 function_obj: &QuickJsValueAdapter,
639 args: &[&QuickJsValueAdapter],
640 ) -> Result<QuickJsValueAdapter, JsError> {
641 functions::call_function_q_ref_args(self, function_obj, args, this_obj)
642 }
643
644 pub fn create_function<
645 F: Fn(
646 &Self,
647 &QuickJsValueAdapter,
648 &[QuickJsValueAdapter],
649 ) -> Result<QuickJsValueAdapter, JsError>
650 + 'static,
651 >(
652 &self,
653 name: &str,
654 js_function: F,
655 arg_count: u32,
656 ) -> Result<QuickJsValueAdapter, JsError> {
657 functions::new_function_q(self, name, js_function, arg_count)
658 }
659
660 pub fn create_function_async<R, F>(
661 &self,
662 name: &str,
663 js_function: F,
664 arg_count: u32,
665 ) -> Result<QuickJsValueAdapter, JsError>
666 where
667 Self: Sized + 'static,
668 R: Future<Output = Result<JsValueFacade, JsError>> + Send + 'static,
669 F: Fn(JsValueFacade, Vec<JsValueFacade>) -> R + 'static,
670 {
671 self.create_function(
673 name,
674 move |realm, this, args| {
675 let this_fac = realm.to_js_value_facade(this)?;
676 let mut args_fac = vec![];
677 for arg in args {
678 args_fac.push(realm.to_js_value_facade(arg)?);
679 }
680 let fut = js_function(this_fac, args_fac);
681 realm.create_resolving_promise_async(fut, |realm, pres| {
682 realm.from_js_value_facade(pres)
684 })
685 },
686 arg_count,
687 )
688 }
689
690 pub fn create_error(
691 &self,
692 name: &str,
693 message: &str,
694 stack: &str,
695 ) -> Result<QuickJsValueAdapter, JsError> {
696 unsafe { errors::new_error(self.context, name, message, stack) }
697 }
698
699 pub fn delete_object_property(
700 &self,
701 object: &QuickJsValueAdapter,
702 property_name: &str,
703 ) -> Result<(), JsError> {
704 objects::set_property_q(self, object, property_name, &new_null_ref())
706 }
707
708 pub fn set_object_property(
709 &self,
710 object: &QuickJsValueAdapter,
711 property_name: &str,
712 property: &QuickJsValueAdapter,
713 ) -> Result<(), JsError> {
714 objects::set_property_q(self, object, property_name, property)
715 }
716
717 pub fn get_object_property(
718 &self,
719 object: &QuickJsValueAdapter,
720 property_name: &str,
721 ) -> Result<QuickJsValueAdapter, JsError> {
722 objects::get_property_q(self, object, property_name)
723 }
724
725 pub fn create_object(&self) -> Result<QuickJsValueAdapter, JsError> {
726 objects::create_object_q(self)
727 }
728
729 pub fn construct_object(
730 &self,
731 constructor: &QuickJsValueAdapter,
732 args: &[&QuickJsValueAdapter],
733 ) -> Result<QuickJsValueAdapter, JsError> {
734 unsafe { construct_object(self.context, constructor, args) }
736 }
737
738 pub fn get_object_properties(
739 &self,
740 object: &QuickJsValueAdapter,
741 ) -> Result<Vec<String>, JsError> {
742 let props = objects::get_own_property_names_q(self, object)?;
743 let mut ret = vec![];
744 for x in 0..props.len() {
745 let prop = props.get_name(x)?;
746 ret.push(prop);
747 }
748 Ok(ret)
749 }
750
751 pub fn traverse_object<F, R>(
752 &self,
753 object: &QuickJsValueAdapter,
754 visitor: F,
755 ) -> Result<Vec<R>, JsError>
756 where
757 F: Fn(&str, &QuickJsValueAdapter) -> Result<R, JsError>,
758 {
759 objects::traverse_properties_q(self, object, visitor)
760 }
761
762 pub fn traverse_object_mut<F>(
763 &self,
764 object: &QuickJsValueAdapter,
765 visitor: F,
766 ) -> Result<(), JsError>
767 where
768 F: FnMut(&str, &QuickJsValueAdapter) -> Result<(), JsError>,
769 {
770 objects::traverse_properties_q_mut(self, object, visitor)
771 }
772
773 pub fn get_array_element(
774 &self,
775 array: &QuickJsValueAdapter,
776 index: u32,
777 ) -> Result<QuickJsValueAdapter, JsError> {
778 arrays::get_element_q(self, array, index)
779 }
780
781 pub fn push_array_element(
783 &self,
784 array: &QuickJsValueAdapter,
785 element: &QuickJsValueAdapter,
786 ) -> Result<u32, JsError> {
787 let push_func = self.get_object_property(array, "push")?;
788 let res = self.invoke_function(Some(array), &push_func, &[element])?;
789 Ok(res.to_i32() as u32)
790 }
791
792 pub fn set_array_element(
793 &self,
794 array: &QuickJsValueAdapter,
795 index: u32,
796 element: &QuickJsValueAdapter,
797 ) -> Result<(), JsError> {
798 arrays::set_element_q(self, array, index, element)
799 }
800
801 pub fn get_array_length(&self, array: &QuickJsValueAdapter) -> Result<u32, JsError> {
802 arrays::get_length_q(self, array)
803 }
804
805 pub fn create_array(&self) -> Result<QuickJsValueAdapter, JsError> {
806 arrays::create_array_q(self)
807 }
808
809 pub fn traverse_array<F, R>(
810 &self,
811 array: &QuickJsValueAdapter,
812 visitor: F,
813 ) -> Result<Vec<R>, JsError>
814 where
815 F: Fn(u32, &QuickJsValueAdapter) -> Result<R, JsError>,
816 {
817 let mut ret = vec![];
819 for x in 0..arrays::get_length_q(self, array)? {
820 let val = arrays::get_element_q(self, array, x)?;
821 ret.push(visitor(x, &val)?)
822 }
823 Ok(ret)
824 }
825
826 pub fn traverse_array_mut<F>(
827 &self,
828 array: &QuickJsValueAdapter,
829 mut visitor: F,
830 ) -> Result<(), JsError>
831 where
832 F: FnMut(u32, &QuickJsValueAdapter) -> Result<(), JsError>,
833 {
834 for x in 0..arrays::get_length_q(self, array)? {
836 let val = arrays::get_element_q(self, array, x)?;
837 visitor(x, &val)?;
838 }
839 Ok(())
840 }
841
842 pub fn create_null(&self) -> Result<QuickJsValueAdapter, JsError> {
843 Ok(crate::quickjs_utils::new_null_ref())
844 }
845
846 pub fn create_undefined(&self) -> Result<QuickJsValueAdapter, JsError> {
847 Ok(crate::quickjs_utils::new_undefined_ref())
848 }
849
850 pub fn create_i32(&self, val: i32) -> Result<QuickJsValueAdapter, JsError> {
851 Ok(from_i32(val))
852 }
853
854 pub fn create_string(&self, val: &str) -> Result<QuickJsValueAdapter, JsError> {
855 from_string_q(self, val)
856 }
857
858 pub fn create_boolean(&self, val: bool) -> Result<QuickJsValueAdapter, JsError> {
859 Ok(from_bool(val))
860 }
861
862 pub fn create_f64(&self, val: f64) -> Result<QuickJsValueAdapter, JsError> {
863 Ok(from_f64(val))
864 }
865
866 pub fn create_promise(&self) -> Result<QuickJsPromiseAdapter, JsError> {
867 crate::quickjs_utils::promises::new_promise_q(self)
868 }
869
870 pub fn add_promise_reactions(
871 &self,
872 promise: &QuickJsValueAdapter,
873 then: Option<QuickJsValueAdapter>,
874 catch: Option<QuickJsValueAdapter>,
875 finally: Option<QuickJsValueAdapter>,
876 ) -> Result<(), JsError> {
877 crate::quickjs_utils::promises::add_promise_reactions_q(self, promise, then, catch, finally)
878 }
879
880 pub fn cache_promise(&self, promise_ref: QuickJsPromiseAdapter) -> usize {
881 let map = &mut *self.promise_cache.borrow_mut();
882 map.insert(promise_ref)
883 }
884
885 pub fn consume_cached_promise(&self, id: usize) -> Option<QuickJsPromiseAdapter> {
886 let map = &mut *self.promise_cache.borrow_mut();
887 map.remove_opt(&id)
888 }
889
890 pub fn dispose_cached_object(&self, id: i32) {
891 let _ = self.consume_cached_obj(id);
892 }
893
894 pub fn with_cached_object<C, R>(&self, id: i32, consumer: C) -> R
895 where
896 C: FnOnce(&QuickJsValueAdapter) -> R,
897 {
898 self.with_cached_obj(id, |obj| consumer(&obj))
899 }
900
901 pub fn consume_cached_object(&self, id: i32) -> QuickJsValueAdapter {
902 self.consume_cached_obj(id)
903 }
904
905 pub fn is_instance_of(
906 &self,
907 object: &QuickJsValueAdapter,
908 constructor: &QuickJsValueAdapter,
909 ) -> bool {
910 objects::is_instance_of_q(self, object, constructor)
911 }
912
913 pub fn json_stringify(
914 &self,
915 object: &QuickJsValueAdapter,
916 opt_space: Option<&str>,
917 ) -> Result<String, JsError> {
918 let opt_space_jsvr = match opt_space {
919 None => None,
920 Some(s) => Some(self.create_string(s)?),
921 };
922 let res = json::stringify_q(self, object, opt_space_jsvr);
923 match res {
924 Ok(jsvr) => jsvr.to_string(),
925 Err(e) => Err(e),
926 }
927 }
928
929 pub fn json_parse(&self, json_string: &str) -> Result<QuickJsValueAdapter, JsError> {
930 json::parse_q(self, json_string)
931 }
932
933 pub fn create_typed_array_uint8(
934 &self,
935 buffer: Vec<u8>,
936 ) -> Result<QuickJsValueAdapter, JsError> {
937 new_uint8_array_q(self, buffer)
938 }
939
940 pub fn create_typed_array_uint8_copy(
941 &self,
942 buffer: &[u8],
943 ) -> Result<QuickJsValueAdapter, JsError> {
944 new_uint8_array_copy_q(self, buffer)
945 }
946
947 pub fn detach_typed_array_buffer(
948 &self,
949 array: &QuickJsValueAdapter,
950 ) -> Result<Vec<u8>, JsError> {
951 let abuf = get_array_buffer_q(self, array)?;
952 detach_array_buffer_buffer_q(self, &abuf)
953 }
954
955 pub fn copy_typed_array_buffer(&self, array: &QuickJsValueAdapter) -> Result<Vec<u8>, JsError> {
956 let abuf = get_array_buffer_q(self, array)?;
957 get_array_buffer_buffer_copy_q(self, &abuf)
958 }
959
960 pub fn get_proxy_instance_info(
961 &self,
962 obj: &QuickJsValueAdapter,
963 ) -> Result<(String, JsProxyInstanceId), JsError>
964 where
965 Self: Sized,
966 {
967 if let Some((p, i)) =
968 crate::reflection::get_proxy_instance_proxy_and_instance_id_q(self, obj)
969 {
970 Ok((p.get_class_name(), i))
971 } else {
972 Err(JsError::new_str("not a proxy instance"))
973 }
974 }
975
976 pub fn to_js_value_facade(
977 &self,
978 js_value: &QuickJsValueAdapter,
979 ) -> Result<JsValueFacade, JsError>
980 where
981 Self: Sized + 'static,
982 {
983 let res: JsValueFacade = match js_value.get_js_type() {
984 JsValueType::I32 => JsValueFacade::I32 {
985 val: js_value.to_i32(),
986 },
987 JsValueType::F64 => JsValueFacade::F64 {
988 val: js_value.to_f64(),
989 },
990 JsValueType::String => JsValueFacade::String {
991 val: DefaultAtom::from(js_value.to_string()?),
992 },
993 JsValueType::Boolean => JsValueFacade::Boolean {
994 val: js_value.to_bool(),
995 },
996 JsValueType::Object => {
997 if js_value.is_typed_array() {
998 JsValueFacade::TypedArray {
1002 buffer: self.copy_typed_array_buffer(js_value)?,
1003 array_type: TypedArrayType::Uint8,
1004 }
1005 } else {
1006 JsValueFacade::JsObject {
1007 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1008 }
1009 }
1010 }
1011 JsValueType::Function => JsValueFacade::JsFunction {
1012 cached_function: CachedJsFunctionRef {
1013 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1014 },
1015 },
1016 JsValueType::BigInt => {
1017 todo!();
1018 }
1019 JsValueType::Promise => JsValueFacade::JsPromise {
1020 cached_promise: CachedJsPromiseRef {
1021 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1022 },
1023 },
1024 JsValueType::Date => {
1025 todo!();
1026 }
1027 JsValueType::Null => JsValueFacade::Null,
1028 JsValueType::Undefined => JsValueFacade::Undefined,
1029
1030 JsValueType::Array => JsValueFacade::JsArray {
1031 cached_array: CachedJsArrayRef {
1032 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1033 },
1034 },
1035 JsValueType::Error => {
1036 let name = self.get_object_property(js_value, "name")?.to_string()?;
1037 let message = self.get_object_property(js_value, "message")?.to_string()?;
1038 let stack = self.get_object_property(js_value, "stack")?.to_string()?;
1039
1040 #[cfg(feature = "typescript")]
1041 let stack = crate::typescript::unmap_stack_trace(stack.as_str());
1042
1043 JsValueFacade::JsError {
1044 val: JsError::new(name, message, stack),
1045 }
1046 }
1047 };
1048 Ok(res)
1049 }
1050
1051 #[allow(clippy::wrong_self_convention)]
1054 pub fn from_js_value_facade(
1055 &self,
1056 value_facade: JsValueFacade,
1057 ) -> Result<QuickJsValueAdapter, JsError>
1058 where
1059 Self: Sized + 'static,
1060 {
1061 match value_facade {
1062 JsValueFacade::I32 { val } => self.create_i32(val),
1063 JsValueFacade::F64 { val } => self.create_f64(val),
1064 JsValueFacade::String { val } => self.create_string(&val),
1065 JsValueFacade::Boolean { val } => self.create_boolean(val),
1066 JsValueFacade::JsObject { cached_object } => {
1067 self.with_cached_object(cached_object.id, |obj| Ok(obj.clone()))
1069 }
1070 JsValueFacade::JsPromise { cached_promise } => {
1071 self.with_cached_object(cached_promise.cached_object.id, |obj| Ok(obj.clone()))
1073 }
1074 JsValueFacade::JsArray { cached_array } => {
1075 self.with_cached_object(cached_array.cached_object.id, |obj| Ok(obj.clone()))
1077 }
1078 JsValueFacade::JsFunction { cached_function } => {
1079 self.with_cached_object(cached_function.cached_object.id, |obj| Ok(obj.clone()))
1081 }
1082 JsValueFacade::Object { val } => {
1083 let obj = self.create_object()?;
1084 for entry in val {
1085 let prop = self.from_js_value_facade(entry.1)?;
1086 self.set_object_property(&obj, entry.0.as_str(), &prop)?;
1087 }
1088 Ok(obj)
1089 }
1090 JsValueFacade::Array { val } => {
1091 let obj = self.create_array()?;
1092 for (x, entry) in val.into_iter().enumerate() {
1093 let prop = self.from_js_value_facade(entry)?;
1094 self.set_array_element(&obj, x as u32, &prop)?;
1095 }
1096 Ok(obj)
1097 }
1098 JsValueFacade::Promise { producer } => {
1099 let producer = &mut *producer.lock("from_js_value_facade").unwrap();
1100 if producer.is_some() {
1101 self.create_resolving_promise_async(producer.take().unwrap(), |realm, jsvf| {
1102 realm.from_js_value_facade(jsvf)
1103 })
1104 } else {
1105 self.create_null()
1106 }
1107 }
1108 JsValueFacade::Function {
1109 name,
1110 arg_count,
1111 func,
1112 } => {
1113 self.create_function(
1116 name.as_str(),
1117 move |realm, _this, args| {
1118 let mut esvf_args = vec![];
1119 for arg in args {
1120 esvf_args.push(realm.to_js_value_facade(arg)?);
1121 }
1122 let esvf_res: Result<JsValueFacade, JsError> = func(esvf_args.as_slice());
1123
1124 match esvf_res {
1125 Ok(jsvf) => realm.from_js_value_facade(jsvf),
1127 Err(err) => Err(err),
1128 }
1129 },
1130 arg_count,
1131 )
1132 }
1133 JsValueFacade::Null => self.create_null(),
1134 JsValueFacade::Undefined => self.create_undefined(),
1135 JsValueFacade::JsError { val } => {
1136 self.create_error(val.get_name(), val.get_message(), val.get_stack())
1137 }
1138 JsValueFacade::ProxyInstance {
1139 instance_id,
1140 namespace,
1141 class_name,
1142 } => self.instantiate_proxy_with_id(namespace, class_name, instance_id),
1143 JsValueFacade::TypedArray { buffer, array_type } => match array_type {
1144 TypedArrayType::Uint8 => self.create_typed_array_uint8(buffer),
1145 },
1146 JsValueFacade::JsonStr { json } => self.json_parse(json.as_str()),
1147 JsValueFacade::SerdeValue { value } => self.serde_value_to_value_adapter(value),
1148 }
1149 }
1150
1151 pub fn value_adapter_to_serde_value(
1152 &self,
1153 value_adapter: &QuickJsValueAdapter,
1154 ) -> Result<serde_json::Value, JsError> {
1155 match value_adapter.get_js_type() {
1156 JsValueType::I32 => Ok(Value::from(value_adapter.to_i32())),
1157 JsValueType::F64 => Ok(Value::from(value_adapter.to_f64())),
1158 JsValueType::String => Ok(Value::from(value_adapter.to_string()?)),
1159 JsValueType::Boolean => Ok(Value::from(value_adapter.to_bool())),
1160 JsValueType::Object => {
1161 let mut map: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
1162 self.traverse_object_mut(value_adapter, |k, v| {
1163 map.insert(k.to_string(), self.value_adapter_to_serde_value(v)?);
1164 Ok(())
1165 })?;
1166 let obj_val = serde_json::Value::Object(map);
1167 Ok(obj_val)
1168 }
1169 JsValueType::Array => {
1170 let mut arr: Vec<serde_json::Value> = vec![];
1171 self.traverse_array_mut(value_adapter, |_i, v| {
1172 arr.push(self.value_adapter_to_serde_value(v)?);
1173 Ok(())
1174 })?;
1175 let arr_val = serde_json::Value::Array(arr);
1176 Ok(arr_val)
1177 }
1178 JsValueType::Null => Ok(serde_json::Value::Null),
1179 JsValueType::Undefined => Ok(serde_json::Value::Null),
1180 JsValueType::Function => Ok(serde_json::Value::Null),
1181 JsValueType::BigInt => Ok(serde_json::Value::Null),
1182 JsValueType::Promise => Ok(serde_json::Value::Null),
1183 JsValueType::Date => Ok(serde_json::Value::Null),
1184 JsValueType::Error => Ok(serde_json::Value::Null),
1185 }
1186 }
1187
1188 pub fn serde_value_to_value_adapter(
1189 &self,
1190 value: Value,
1191 ) -> Result<QuickJsValueAdapter, JsError> {
1192 match value {
1193 Value::Null => self.create_null(),
1194 Value::Bool(b) => self.create_boolean(b),
1195 Value::Number(n) => {
1196 if n.is_i64() {
1197 let i = n.as_i64().unwrap();
1198 if i <= i32::MAX as i64 {
1199 self.create_i32(i as i32)
1200 } else {
1201 self.create_f64(i as f64)
1202 }
1203 } else if n.is_u64() {
1204 let i = n.as_u64().unwrap();
1205 if i <= i32::MAX as u64 {
1206 self.create_i32(i as i32)
1207 } else {
1208 self.create_f64(i as f64)
1209 }
1210 } else {
1211 let i = n.as_f64().unwrap();
1213 self.create_f64(i)
1214 }
1215 }
1216 Value::String(s) => self.create_string(s.as_str()),
1217 Value::Array(a) => {
1218 let arr = self.create_array()?;
1219 for (x, aval) in (0_u32..).zip(a.into_iter()) {
1220 let entry = self.serde_value_to_value_adapter(aval)?;
1221 self.set_array_element(&arr, x, &entry)?;
1222 }
1223 Ok(arr)
1224 }
1225 Value::Object(o) => {
1226 let obj = self.create_object()?;
1227 for oval in o {
1228 let entry = self.serde_value_to_value_adapter(oval.1)?;
1229 self.set_object_property(&obj, oval.0.as_str(), &entry)?;
1230 }
1231 Ok(obj)
1232 }
1233 }
1234 }
1235 pub fn create_resolving_promise_async<P, R: Send + 'static, M>(
1238 &self,
1239 producer: P,
1240 mapper: M,
1241 ) -> Result<QuickJsValueAdapter, JsError>
1242 where
1243 P: Future<Output = Result<R, JsError>> + Send + 'static,
1244 M: FnOnce(&QuickJsRealmAdapter, R) -> Result<QuickJsValueAdapter, JsError> + Send + 'static,
1245 Self: Sized + 'static,
1246 {
1247 new_resolving_promise_async(self, producer, mapper)
1248 }
1249 pub fn create_resolving_promise<P, R: Send + 'static, M>(
1253 &self,
1254 producer: P,
1255 mapper: M,
1256 ) -> Result<QuickJsValueAdapter, JsError>
1257 where
1258 P: FnOnce() -> Result<R, JsError> + Send + 'static,
1259 M: FnOnce(&QuickJsRealmAdapter, R) -> Result<QuickJsValueAdapter, JsError> + Send + 'static,
1260 Self: Sized + 'static,
1261 {
1262 new_resolving_promise(self, producer, mapper)
1263 }
1264}
1265
1266#[cfg(test)]
1267pub mod tests {
1268 use crate::builder::QuickJsRuntimeBuilder;
1269 use crate::facades::tests::init_test_rt;
1270 use crate::jsutils::Script;
1271 use crate::quickjs_utils;
1272 use crate::quickjs_utils::primitives::to_i32;
1273 use crate::quickjs_utils::{functions, get_global_q, objects};
1274
1275 #[test]
1276 fn test_eval() {
1277 let rt = init_test_rt();
1278 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1279 let q_ctx = q_js_rt.get_main_realm();
1280 let res = q_ctx.eval(Script::new("test_eval.es", "(1 + 1);"));
1281
1282 match res {
1283 Ok(res) => {
1284 log::info!("script ran ok: {:?}", res);
1285 assert!(res.is_i32());
1286 assert_eq!(to_i32(&res).ok().expect("conversion failed"), 2);
1287 }
1288 Err(e) => {
1289 log::error!("script failed: {}", e);
1290 panic!("script failed");
1291 }
1292 }
1293 });
1294 }
1295
1296 #[test]
1297 fn test_multi_ctx() {
1298 let rt = QuickJsRuntimeBuilder::new().build();
1299 rt.create_context("a").ok().expect("could not create ctx a");
1300 rt.create_context("b").ok().expect("could not create ctx b");
1301
1302 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1303 let ctx_a = q_js_rt.get_context("a");
1304 let ctx_b = q_js_rt.get_context("b");
1305 ctx_a
1306 .eval(Script::new("a.es", "this.a = 1"))
1307 .ok()
1308 .expect("script failed");
1309 ctx_b
1310 .eval(Script::new("a.es", "this.b = 1"))
1311 .ok()
1312 .expect("script failed");
1313 let v = ctx_a
1314 .eval(Script::new("a2.es", "this.a;"))
1315 .ok()
1316 .expect("script failed");
1317 assert!(v.is_i32());
1318 let v2 = ctx_b
1319 .eval(Script::new("b2.es", "this.a;"))
1320 .ok()
1321 .expect("script failed");
1322 assert!(v2.is_null_or_undefined());
1323 let v3 = ctx_a
1324 .eval(Script::new("a2.es", "this.b;"))
1325 .ok()
1326 .expect("script failed");
1327 assert!(v3.is_null_or_undefined());
1328 let v4 = ctx_b
1329 .eval(Script::new("b2.es", "this.b;"))
1330 .ok()
1331 .expect("script failed");
1332 assert!(v4.is_i32());
1333 });
1334 let _ = rt.drop_context("b");
1335
1336 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1337 q_js_rt.gc();
1338 let ctx_a = q_js_rt.get_context("a");
1339 let v = ctx_a
1340 .eval(Script::new("a2.es", "this.a;"))
1341 .ok()
1342 .expect("script failed");
1343 assert!(v.is_i32());
1344 q_js_rt.gc();
1345 });
1346
1347 rt.create_context("c")
1348 .ok()
1349 .expect("could not create context c");
1350
1351 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1352 let c_ctx = q_js_rt.get_context("c");
1353 let func = functions::new_function_q(
1354 c_ctx,
1355 "test",
1356 |_q_ctx, _this, _args| Ok(quickjs_utils::new_null_ref()),
1357 1,
1358 )
1359 .ok()
1360 .unwrap();
1361 let global = get_global_q(c_ctx);
1362 objects::set_property_q(c_ctx, &global, "test_func", &func)
1363 .ok()
1364 .expect("could not set prop");
1365 q_js_rt.gc();
1366 });
1367 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1368 q_js_rt.gc();
1369 let ctx_a = q_js_rt.get_context("a");
1370 let v = ctx_a
1371 .eval(Script::new("a2.es", "this.a;"))
1372 .ok()
1373 .expect("script failed");
1374 assert!(v.is_i32());
1375 q_js_rt.gc();
1376 });
1377 let _ = rt.drop_context("c");
1378 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1379 q_js_rt.gc();
1380 let ctx_a = q_js_rt.get_context("a");
1381 let v = ctx_a
1382 .eval(Script::new("a2.es", "this.a;"))
1383 .ok()
1384 .expect("script failed");
1385 assert!(v.is_i32());
1386 q_js_rt.gc();
1387 });
1388 }
1389}