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 #[cfg(feature = "bellard")]
355 u: q::JSValueUnion { uint64: 0 },
356 #[cfg(feature = "quickjs-ng")]
357 u: q::JSValueUnion { int32: 0 },
358 tag: TAG_EXCEPTION,
359 }
360 }
361
362 pub fn get_exception_ctx(&self) -> Option<JsError> {
364 unsafe { errors::get_exception(self.context) }
365 }
366
367 pub unsafe fn get_exception(context: *mut q::JSContext) -> Option<JsError> {
371 errors::get_exception(context)
372 }
373
374 pub fn cache_object(&self, obj: QuickJsValueAdapter) -> i32 {
375 let cache_map = &mut *self.object_cache.borrow_mut();
376 let id = cache_map.insert(obj) as i32;
377 log::trace!("cache_object: id={}, thread={}", id, thread_id::get());
378 id
379 }
380
381 pub fn remove_cached_obj_if_present(&self, id: i32) {
382 log::trace!(
383 "remove_cached_obj_if_present: id={}, thread={}",
384 id,
385 thread_id::get()
386 );
387 let cache_map = &mut *self.object_cache.borrow_mut();
388 if cache_map.contains_key(&(id as usize)) {
389 let _ = cache_map.remove(&(id as usize));
390 }
391 }
392
393 pub fn consume_cached_obj(&self, id: i32) -> QuickJsValueAdapter {
394 log::trace!("consume_cached_obj: id={}, thread={}", id, thread_id::get());
395 let cache_map = &mut *self.object_cache.borrow_mut();
396 cache_map.remove(&(id as usize))
397 }
398
399 pub fn with_cached_obj<C, R>(&self, id: i32, consumer: C) -> R
400 where
401 C: FnOnce(QuickJsValueAdapter) -> R,
402 {
403 log::trace!("with_cached_obj: id={}, thread={}", id, thread_id::get());
404 let clone_ref = {
405 let cache_map = &*self.object_cache.borrow();
406 let opt = cache_map.get(&(id as usize));
407 let cached_ref = opt.expect("no such obj in cache");
408 cached_ref.clone()
409 };
410 consumer(clone_ref)
413 }
414 pub unsafe fn with_context<C, R>(context: *mut q::JSContext, consumer: C) -> R
417 where
418 C: FnOnce(&QuickJsRealmAdapter) -> R,
419 {
420 QuickJsRuntimeAdapter::do_with(|q_js_rt| {
421 let id = QuickJsRealmAdapter::get_id(context);
422 let q_ctx = q_js_rt.get_context(id);
423 consumer(q_ctx)
424 })
425 }
426}
427
428impl Drop for QuickJsRealmAdapter {
429 fn drop(&mut self) {
430 log::trace!("before drop QuickJSContext {}", self.id);
431
432 let id = &self.id;
433 {
434 ID_REGISTRY.with(|rc| {
435 let registry = &mut *rc.borrow_mut();
436 registry.remove(id);
437 });
438 }
439 {
440 let proxies = &mut *self.proxy_registry.borrow_mut();
441 proxies.clear();
442 }
443
444 log::trace!("after drop QuickJSContext {}", self.id);
445 }
446}
447
448impl QuickJsRealmAdapter {
449 pub fn get_realm_id(&self) -> &str {
450 self.id.as_str()
451 }
452
453 pub fn get_runtime_facade_inner(&self) -> Weak<QuickjsRuntimeFacadeInner> {
454 QuickJsRuntimeAdapter::do_with(|rt| {
455 Arc::downgrade(&rt.get_rti_ref().expect("Runtime was dropped"))
456 })
457 }
458
459 pub fn get_script_or_module_name(&self) -> Result<String, JsError> {
460 crate::quickjs_utils::get_script_or_module_name_q(self)
461 }
462
463 pub fn install_proxy(
464 &self,
465 proxy: JsProxy,
466 add_global_var: bool,
467 ) -> Result<QuickJsValueAdapter, JsError> {
468 proxy.install(self, add_global_var)
471 }
472
473 pub fn instantiate_proxy_with_id(
474 &self,
475 namespace: &[&str],
476 class_name: &str,
477 instance_id: usize,
478 ) -> Result<QuickJsValueAdapter, JsError> {
479 let cn = if namespace.is_empty() {
481 class_name.to_string()
482 } else {
483 format!("{}.{}", namespace.join("."), class_name)
484 };
485
486 let proxy_map = self.proxy_registry.borrow();
487 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
488
489 new_instance3(proxy, instance_id, self)
490 }
491
492 pub fn instantiate_proxy(
493 &self,
494 namespace: &[&str],
495 class_name: &str,
496 arguments: &[QuickJsValueAdapter],
497 ) -> Result<(JsProxyInstanceId, QuickJsValueAdapter), JsError> {
498 let cn = if namespace.is_empty() {
500 class_name.to_string()
501 } else {
502 format!("{}.{}", namespace.join("."), class_name)
503 };
504
505 let proxy_map = self.proxy_registry.borrow();
506 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
507
508 let instance_info = new_instance(cn.as_str(), self)?;
509
510 if let Some(constructor) = &proxy.constructor {
511 QuickJsRuntimeAdapter::do_with(|rt| constructor(rt, self, instance_info.0, arguments))?
513 }
514
515 Ok(instance_info)
516 }
517
518 pub fn dispatch_proxy_event(
519 &self,
520 namespace: &[&str],
521 class_name: &str,
522 proxy_instance_id: &usize,
523 event_id: &str,
524 event_obj: &QuickJsValueAdapter,
525 ) -> Result<bool, JsError> {
526 let cn = if namespace.is_empty() {
528 class_name.to_string()
529 } else {
530 format!("{}.{}", namespace.join("."), class_name)
531 };
532
533 let proxy_map = self.proxy_registry.borrow();
534 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
535
536 dispatch_event(self, proxy, *proxy_instance_id, event_id, event_obj.clone())
537 }
538
539 pub fn dispatch_static_proxy_event(
540 &self,
541 namespace: &[&str],
542 class_name: &str,
543 event_id: &str,
544 event_obj: &QuickJsValueAdapter,
545 ) -> Result<bool, JsError> {
546 let cn = if namespace.is_empty() {
548 class_name.to_string()
549 } else {
550 format!("{}.{}", namespace.join("."), class_name)
551 };
552
553 let proxy_map = self.proxy_registry.borrow();
554 let proxy = proxy_map.get(cn.as_str()).expect("class not found");
555
556 dispatch_static_event(
557 self,
558 proxy.get_class_name().as_str(),
559 event_id,
560 event_obj.clone(),
561 )
562 }
563
564 pub fn install_function(
565 &self,
566 namespace: &[&str],
567 name: &str,
568 js_function: fn(
569 &QuickJsRuntimeAdapter,
570 &Self,
571 &QuickJsValueAdapter,
572 &[QuickJsValueAdapter],
573 ) -> Result<QuickJsValueAdapter, JsError>,
574 arg_count: u32,
575 ) -> Result<(), JsError> {
576 let ns = self.get_namespace(namespace)?;
578
579 let func = functions::new_function_q(
580 self,
581 name,
582 move |ctx, this, args| {
583 QuickJsRuntimeAdapter::do_with(|rt| js_function(rt, ctx, this, args))
584 },
585 arg_count,
586 )?;
587 self.set_object_property(&ns, name, &func)?;
588 Ok(())
589 }
590
591 pub fn install_closure<
592 F: Fn(
593 &QuickJsRuntimeAdapter,
594 &Self,
595 &QuickJsValueAdapter,
596 &[QuickJsValueAdapter],
597 ) -> Result<QuickJsValueAdapter, JsError>
598 + 'static,
599 >(
600 &self,
601 namespace: &[&str],
602 name: &str,
603 js_function: F,
604 arg_count: u32,
605 ) -> Result<(), JsError> {
606 let ns = self.get_namespace(namespace)?;
608
609 let func = functions::new_function_q(
610 self,
611 name,
612 move |ctx, this, args| {
613 QuickJsRuntimeAdapter::do_with(|rt| js_function(rt, ctx, this, args))
614 },
615 arg_count,
616 )?;
617 self.set_object_property(&ns, name, &func)?;
618 Ok(())
619 }
620
621 pub fn get_global(&self) -> Result<QuickJsValueAdapter, JsError> {
622 Ok(get_global_q(self))
623 }
624
625 pub fn get_namespace(&self, namespace: &[&str]) -> Result<QuickJsValueAdapter, JsError> {
626 objects::get_namespace_q(self, namespace, true)
627 }
628
629 pub fn invoke_function_on_object_by_name(
630 &self,
631 this_obj: &QuickJsValueAdapter,
632 method_name: &str,
633 args: &[QuickJsValueAdapter],
634 ) -> Result<QuickJsValueAdapter, JsError> {
635 functions::invoke_member_function_q(self, this_obj, method_name, args)
636 }
637
638 pub fn invoke_function(
639 &self,
640 this_obj: Option<&QuickJsValueAdapter>,
641 function_obj: &QuickJsValueAdapter,
642 args: &[&QuickJsValueAdapter],
643 ) -> Result<QuickJsValueAdapter, JsError> {
644 functions::call_function_q_ref_args(self, function_obj, args, this_obj)
645 }
646
647 pub fn create_function<
648 F: Fn(
649 &Self,
650 &QuickJsValueAdapter,
651 &[QuickJsValueAdapter],
652 ) -> Result<QuickJsValueAdapter, JsError>
653 + 'static,
654 >(
655 &self,
656 name: &str,
657 js_function: F,
658 arg_count: u32,
659 ) -> Result<QuickJsValueAdapter, JsError> {
660 functions::new_function_q(self, name, js_function, arg_count)
661 }
662
663 pub fn create_function_async<R, F>(
664 &self,
665 name: &str,
666 js_function: F,
667 arg_count: u32,
668 ) -> Result<QuickJsValueAdapter, JsError>
669 where
670 Self: Sized + 'static,
671 R: Future<Output = Result<JsValueFacade, JsError>> + Send + 'static,
672 F: Fn(JsValueFacade, Vec<JsValueFacade>) -> R + 'static,
673 {
674 self.create_function(
676 name,
677 move |realm, this, args| {
678 let this_fac = realm.to_js_value_facade(this)?;
679 let mut args_fac = vec![];
680 for arg in args {
681 args_fac.push(realm.to_js_value_facade(arg)?);
682 }
683 let fut = js_function(this_fac, args_fac);
684 realm.create_resolving_promise_async(fut, |realm, pres| {
685 realm.from_js_value_facade(pres)
687 })
688 },
689 arg_count,
690 )
691 }
692
693 pub fn create_error(
694 &self,
695 name: &str,
696 message: &str,
697 stack: &str,
698 ) -> Result<QuickJsValueAdapter, JsError> {
699 unsafe { errors::new_error(self.context, name, message, stack) }
700 }
701
702 pub fn delete_object_property(
703 &self,
704 object: &QuickJsValueAdapter,
705 property_name: &str,
706 ) -> Result<(), JsError> {
707 objects::set_property_q(self, object, property_name, &new_null_ref())
709 }
710
711 pub fn set_object_property(
712 &self,
713 object: &QuickJsValueAdapter,
714 property_name: &str,
715 property: &QuickJsValueAdapter,
716 ) -> Result<(), JsError> {
717 objects::set_property_q(self, object, property_name, property)
718 }
719
720 pub fn get_object_property(
721 &self,
722 object: &QuickJsValueAdapter,
723 property_name: &str,
724 ) -> Result<QuickJsValueAdapter, JsError> {
725 objects::get_property_q(self, object, property_name)
726 }
727
728 pub fn create_object(&self) -> Result<QuickJsValueAdapter, JsError> {
729 objects::create_object_q(self)
730 }
731
732 pub fn construct_object(
733 &self,
734 constructor: &QuickJsValueAdapter,
735 args: &[&QuickJsValueAdapter],
736 ) -> Result<QuickJsValueAdapter, JsError> {
737 unsafe { construct_object(self.context, constructor, args) }
739 }
740
741 pub fn get_object_properties(
742 &self,
743 object: &QuickJsValueAdapter,
744 ) -> Result<Vec<String>, JsError> {
745 let props = objects::get_own_property_names_q(self, object)?;
746 let mut ret = vec![];
747 for x in 0..props.len() {
748 let prop = props.get_name(x)?;
749 ret.push(prop);
750 }
751 Ok(ret)
752 }
753
754 pub fn traverse_object<F, R>(
755 &self,
756 object: &QuickJsValueAdapter,
757 visitor: F,
758 ) -> Result<Vec<R>, JsError>
759 where
760 F: Fn(&str, &QuickJsValueAdapter) -> Result<R, JsError>,
761 {
762 objects::traverse_properties_q(self, object, visitor)
763 }
764
765 pub fn traverse_object_mut<F>(
766 &self,
767 object: &QuickJsValueAdapter,
768 visitor: F,
769 ) -> Result<(), JsError>
770 where
771 F: FnMut(&str, &QuickJsValueAdapter) -> Result<(), JsError>,
772 {
773 objects::traverse_properties_q_mut(self, object, visitor)
774 }
775
776 pub fn get_array_element(
777 &self,
778 array: &QuickJsValueAdapter,
779 index: u32,
780 ) -> Result<QuickJsValueAdapter, JsError> {
781 arrays::get_element_q(self, array, index)
782 }
783
784 pub fn push_array_element(
786 &self,
787 array: &QuickJsValueAdapter,
788 element: &QuickJsValueAdapter,
789 ) -> Result<u32, JsError> {
790 let push_func = self.get_object_property(array, "push")?;
791 let res = self.invoke_function(Some(array), &push_func, &[element])?;
792 Ok(res.to_i32() as u32)
793 }
794
795 pub fn set_array_element(
796 &self,
797 array: &QuickJsValueAdapter,
798 index: u32,
799 element: &QuickJsValueAdapter,
800 ) -> Result<(), JsError> {
801 arrays::set_element_q(self, array, index, element)
802 }
803
804 pub fn get_array_length(&self, array: &QuickJsValueAdapter) -> Result<u32, JsError> {
805 arrays::get_length_q(self, array)
806 }
807
808 pub fn create_array(&self) -> Result<QuickJsValueAdapter, JsError> {
809 arrays::create_array_q(self)
810 }
811
812 pub fn traverse_array<F, R>(
813 &self,
814 array: &QuickJsValueAdapter,
815 visitor: F,
816 ) -> Result<Vec<R>, JsError>
817 where
818 F: Fn(u32, &QuickJsValueAdapter) -> Result<R, JsError>,
819 {
820 let mut ret = vec![];
822 for x in 0..arrays::get_length_q(self, array)? {
823 let val = arrays::get_element_q(self, array, x)?;
824 ret.push(visitor(x, &val)?)
825 }
826 Ok(ret)
827 }
828
829 pub fn traverse_array_mut<F>(
830 &self,
831 array: &QuickJsValueAdapter,
832 mut visitor: F,
833 ) -> Result<(), JsError>
834 where
835 F: FnMut(u32, &QuickJsValueAdapter) -> Result<(), JsError>,
836 {
837 for x in 0..arrays::get_length_q(self, array)? {
839 let val = arrays::get_element_q(self, array, x)?;
840 visitor(x, &val)?;
841 }
842 Ok(())
843 }
844
845 pub fn create_null(&self) -> Result<QuickJsValueAdapter, JsError> {
846 Ok(crate::quickjs_utils::new_null_ref())
847 }
848
849 pub fn create_undefined(&self) -> Result<QuickJsValueAdapter, JsError> {
850 Ok(crate::quickjs_utils::new_undefined_ref())
851 }
852
853 pub fn create_i32(&self, val: i32) -> Result<QuickJsValueAdapter, JsError> {
854 Ok(from_i32(val))
855 }
856
857 pub fn create_string(&self, val: &str) -> Result<QuickJsValueAdapter, JsError> {
858 from_string_q(self, val)
859 }
860
861 pub fn create_boolean(&self, val: bool) -> Result<QuickJsValueAdapter, JsError> {
862 Ok(from_bool(val))
863 }
864
865 pub fn create_f64(&self, val: f64) -> Result<QuickJsValueAdapter, JsError> {
866 Ok(from_f64(val))
867 }
868
869 pub fn create_promise(&self) -> Result<QuickJsPromiseAdapter, JsError> {
870 crate::quickjs_utils::promises::new_promise_q(self)
871 }
872
873 pub fn add_promise_reactions(
874 &self,
875 promise: &QuickJsValueAdapter,
876 then: Option<QuickJsValueAdapter>,
877 catch: Option<QuickJsValueAdapter>,
878 finally: Option<QuickJsValueAdapter>,
879 ) -> Result<(), JsError> {
880 crate::quickjs_utils::promises::add_promise_reactions_q(self, promise, then, catch, finally)
881 }
882
883 pub fn cache_promise(&self, promise_ref: QuickJsPromiseAdapter) -> usize {
884 let map = &mut *self.promise_cache.borrow_mut();
885 map.insert(promise_ref)
886 }
887
888 pub fn consume_cached_promise(&self, id: usize) -> Option<QuickJsPromiseAdapter> {
889 let map = &mut *self.promise_cache.borrow_mut();
890 map.remove_opt(&id)
891 }
892
893 pub fn dispose_cached_object(&self, id: i32) {
894 let _ = self.consume_cached_obj(id);
895 }
896
897 pub fn with_cached_object<C, R>(&self, id: i32, consumer: C) -> R
898 where
899 C: FnOnce(&QuickJsValueAdapter) -> R,
900 {
901 self.with_cached_obj(id, |obj| consumer(&obj))
902 }
903
904 pub fn consume_cached_object(&self, id: i32) -> QuickJsValueAdapter {
905 self.consume_cached_obj(id)
906 }
907
908 pub fn is_instance_of(
909 &self,
910 object: &QuickJsValueAdapter,
911 constructor: &QuickJsValueAdapter,
912 ) -> bool {
913 objects::is_instance_of_q(self, object, constructor)
914 }
915
916 pub fn json_stringify(
917 &self,
918 object: &QuickJsValueAdapter,
919 opt_space: Option<&str>,
920 ) -> Result<String, JsError> {
921 let opt_space_jsvr = match opt_space {
922 None => None,
923 Some(s) => Some(self.create_string(s)?),
924 };
925 let res = json::stringify_q(self, object, opt_space_jsvr);
926 match res {
927 Ok(jsvr) => jsvr.to_string(),
928 Err(e) => Err(e),
929 }
930 }
931
932 pub fn json_parse(&self, json_string: &str) -> Result<QuickJsValueAdapter, JsError> {
933 json::parse_q(self, json_string)
934 }
935
936 pub fn create_typed_array_uint8(
937 &self,
938 buffer: Vec<u8>,
939 ) -> Result<QuickJsValueAdapter, JsError> {
940 new_uint8_array_q(self, buffer)
941 }
942
943 pub fn create_typed_array_uint8_copy(
944 &self,
945 buffer: &[u8],
946 ) -> Result<QuickJsValueAdapter, JsError> {
947 new_uint8_array_copy_q(self, buffer)
948 }
949
950 pub fn detach_typed_array_buffer(
951 &self,
952 array: &QuickJsValueAdapter,
953 ) -> Result<Vec<u8>, JsError> {
954 let abuf = get_array_buffer_q(self, array)?;
955 detach_array_buffer_buffer_q(self, &abuf)
956 }
957
958 pub fn copy_typed_array_buffer(&self, array: &QuickJsValueAdapter) -> Result<Vec<u8>, JsError> {
959 let abuf = get_array_buffer_q(self, array)?;
960 get_array_buffer_buffer_copy_q(self, &abuf)
961 }
962
963 pub fn get_proxy_instance_info(
964 &self,
965 obj: &QuickJsValueAdapter,
966 ) -> Result<(String, JsProxyInstanceId), JsError>
967 where
968 Self: Sized,
969 {
970 if let Some((p, i)) =
971 crate::reflection::get_proxy_instance_proxy_and_instance_id_q(self, obj)
972 {
973 Ok((p.get_class_name(), i))
974 } else {
975 Err(JsError::new_str("not a proxy instance"))
976 }
977 }
978
979 pub fn to_js_value_facade(
980 &self,
981 js_value: &QuickJsValueAdapter,
982 ) -> Result<JsValueFacade, JsError>
983 where
984 Self: Sized + 'static,
985 {
986 let res: JsValueFacade = match js_value.get_js_type() {
987 JsValueType::I32 => JsValueFacade::I32 {
988 val: js_value.to_i32(),
989 },
990 JsValueType::F64 => JsValueFacade::F64 {
991 val: js_value.to_f64(),
992 },
993 JsValueType::String => JsValueFacade::String {
994 val: DefaultAtom::from(js_value.to_string()?),
995 },
996 JsValueType::Boolean => JsValueFacade::Boolean {
997 val: js_value.to_bool(),
998 },
999 JsValueType::Object => {
1000 if js_value.is_typed_array() {
1001 JsValueFacade::TypedArray {
1005 buffer: self.copy_typed_array_buffer(js_value)?,
1006 array_type: TypedArrayType::Uint8,
1007 }
1008 } else {
1009 JsValueFacade::JsObject {
1010 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1011 }
1012 }
1013 }
1014 JsValueType::Function => JsValueFacade::JsFunction {
1015 cached_function: CachedJsFunctionRef {
1016 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1017 },
1018 },
1019 JsValueType::BigInt => {
1020 todo!();
1021 }
1022 JsValueType::Promise => JsValueFacade::JsPromise {
1023 cached_promise: CachedJsPromiseRef {
1024 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1025 },
1026 },
1027 JsValueType::Date => {
1028 todo!();
1029 }
1030 JsValueType::Null => JsValueFacade::Null,
1031 JsValueType::Undefined => JsValueFacade::Undefined,
1032
1033 JsValueType::Array => JsValueFacade::JsArray {
1034 cached_array: CachedJsArrayRef {
1035 cached_object: CachedJsObjectRef::new(self, js_value.clone()),
1036 },
1037 },
1038 JsValueType::Error => {
1039 let name = self.get_object_property(js_value, "name")?.to_string()?;
1040 let message = self.get_object_property(js_value, "message")?.to_string()?;
1041 let stack = self.get_object_property(js_value, "stack")?.to_string()?;
1042
1043 #[cfg(feature = "typescript")]
1044 let stack = crate::typescript::unmap_stack_trace(stack.as_str());
1045
1046 JsValueFacade::JsError {
1047 val: JsError::new(name, message, stack),
1048 }
1049 }
1050 };
1051 Ok(res)
1052 }
1053
1054 #[allow(clippy::wrong_self_convention)]
1057 pub fn from_js_value_facade(
1058 &self,
1059 value_facade: JsValueFacade,
1060 ) -> Result<QuickJsValueAdapter, JsError>
1061 where
1062 Self: Sized + 'static,
1063 {
1064 match value_facade {
1065 JsValueFacade::I32 { val } => self.create_i32(val),
1066 JsValueFacade::F64 { val } => self.create_f64(val),
1067 JsValueFacade::String { val } => self.create_string(&val),
1068 JsValueFacade::Boolean { val } => self.create_boolean(val),
1069 JsValueFacade::JsObject { cached_object } => {
1070 self.with_cached_object(cached_object.id, |obj| Ok(obj.clone()))
1072 }
1073 JsValueFacade::JsPromise { cached_promise } => {
1074 self.with_cached_object(cached_promise.cached_object.id, |obj| Ok(obj.clone()))
1076 }
1077 JsValueFacade::JsArray { cached_array } => {
1078 self.with_cached_object(cached_array.cached_object.id, |obj| Ok(obj.clone()))
1080 }
1081 JsValueFacade::JsFunction { cached_function } => {
1082 self.with_cached_object(cached_function.cached_object.id, |obj| Ok(obj.clone()))
1084 }
1085 JsValueFacade::Object { val } => {
1086 let obj = self.create_object()?;
1087 for entry in val {
1088 let prop = self.from_js_value_facade(entry.1)?;
1089 self.set_object_property(&obj, entry.0.as_str(), &prop)?;
1090 }
1091 Ok(obj)
1092 }
1093 JsValueFacade::Array { val } => {
1094 let obj = self.create_array()?;
1095 for (x, entry) in val.into_iter().enumerate() {
1096 let prop = self.from_js_value_facade(entry)?;
1097 self.set_array_element(&obj, x as u32, &prop)?;
1098 }
1099 Ok(obj)
1100 }
1101 JsValueFacade::Promise { producer } => {
1102 let producer = &mut *producer.lock("from_js_value_facade").unwrap();
1103 if producer.is_some() {
1104 self.create_resolving_promise_async(producer.take().unwrap(), |realm, jsvf| {
1105 realm.from_js_value_facade(jsvf)
1106 })
1107 } else {
1108 self.create_null()
1109 }
1110 }
1111 JsValueFacade::Function {
1112 name,
1113 arg_count,
1114 func,
1115 } => {
1116 self.create_function(
1119 name.as_str(),
1120 move |realm, _this, args| {
1121 let mut esvf_args = vec![];
1122 for arg in args {
1123 esvf_args.push(realm.to_js_value_facade(arg)?);
1124 }
1125 let esvf_res: Result<JsValueFacade, JsError> = func(esvf_args.as_slice());
1126
1127 match esvf_res {
1128 Ok(jsvf) => realm.from_js_value_facade(jsvf),
1130 Err(err) => Err(err),
1131 }
1132 },
1133 arg_count,
1134 )
1135 }
1136 JsValueFacade::Null => self.create_null(),
1137 JsValueFacade::Undefined => self.create_undefined(),
1138 JsValueFacade::JsError { val } => {
1139 self.create_error(val.get_name(), val.get_message(), val.get_stack())
1140 }
1141 JsValueFacade::ProxyInstance {
1142 instance_id,
1143 namespace,
1144 class_name,
1145 } => self.instantiate_proxy_with_id(namespace, class_name, instance_id),
1146 JsValueFacade::TypedArray { buffer, array_type } => match array_type {
1147 TypedArrayType::Uint8 => self.create_typed_array_uint8(buffer),
1148 },
1149 JsValueFacade::JsonStr { json } => self.json_parse(json.as_str()),
1150 JsValueFacade::SerdeValue { value } => self.serde_value_to_value_adapter(value),
1151 }
1152 }
1153
1154 pub fn value_adapter_to_serde_value(
1155 &self,
1156 value_adapter: &QuickJsValueAdapter,
1157 ) -> Result<serde_json::Value, JsError> {
1158 match value_adapter.get_js_type() {
1159 JsValueType::I32 => Ok(Value::from(value_adapter.to_i32())),
1160 JsValueType::F64 => Ok(Value::from(value_adapter.to_f64())),
1161 JsValueType::String => Ok(Value::from(value_adapter.to_string()?)),
1162 JsValueType::Boolean => Ok(Value::from(value_adapter.to_bool())),
1163 JsValueType::Object => {
1164 let mut map: serde_json::Map<String, serde_json::Value> = serde_json::Map::new();
1165 self.traverse_object_mut(value_adapter, |k, v| {
1166 map.insert(k.to_string(), self.value_adapter_to_serde_value(v)?);
1167 Ok(())
1168 })?;
1169 let obj_val = serde_json::Value::Object(map);
1170 Ok(obj_val)
1171 }
1172 JsValueType::Array => {
1173 let mut arr: Vec<serde_json::Value> = vec![];
1174 self.traverse_array_mut(value_adapter, |_i, v| {
1175 arr.push(self.value_adapter_to_serde_value(v)?);
1176 Ok(())
1177 })?;
1178 let arr_val = serde_json::Value::Array(arr);
1179 Ok(arr_val)
1180 }
1181 JsValueType::Null => Ok(serde_json::Value::Null),
1182 JsValueType::Undefined => Ok(serde_json::Value::Null),
1183 JsValueType::Function => Ok(serde_json::Value::Null),
1184 JsValueType::BigInt => Ok(serde_json::Value::Null),
1185 JsValueType::Promise => Ok(serde_json::Value::Null),
1186 JsValueType::Date => Ok(serde_json::Value::Null),
1187 JsValueType::Error => Ok(serde_json::Value::Null),
1188 }
1189 }
1190
1191 pub fn serde_value_to_value_adapter(
1192 &self,
1193 value: Value,
1194 ) -> Result<QuickJsValueAdapter, JsError> {
1195 match value {
1196 Value::Null => self.create_null(),
1197 Value::Bool(b) => self.create_boolean(b),
1198 Value::Number(n) => {
1199 if n.is_i64() {
1200 let i = n.as_i64().unwrap();
1201 if i <= i32::MAX as i64 {
1202 self.create_i32(i as i32)
1203 } else {
1204 self.create_f64(i as f64)
1205 }
1206 } else if n.is_u64() {
1207 let i = n.as_u64().unwrap();
1208 if i <= i32::MAX as u64 {
1209 self.create_i32(i as i32)
1210 } else {
1211 self.create_f64(i as f64)
1212 }
1213 } else {
1214 let i = n.as_f64().unwrap();
1216 self.create_f64(i)
1217 }
1218 }
1219 Value::String(s) => self.create_string(s.as_str()),
1220 Value::Array(a) => {
1221 let arr = self.create_array()?;
1222 for (x, aval) in (0_u32..).zip(a) {
1223 let entry = self.serde_value_to_value_adapter(aval)?;
1224 self.set_array_element(&arr, x, &entry)?;
1225 }
1226 Ok(arr)
1227 }
1228 Value::Object(o) => {
1229 let obj = self.create_object()?;
1230 for oval in o {
1231 let entry = self.serde_value_to_value_adapter(oval.1)?;
1232 self.set_object_property(&obj, oval.0.as_str(), &entry)?;
1233 }
1234 Ok(obj)
1235 }
1236 }
1237 }
1238 pub fn create_resolving_promise_async<P, R: Send + 'static, M>(
1241 &self,
1242 producer: P,
1243 mapper: M,
1244 ) -> Result<QuickJsValueAdapter, JsError>
1245 where
1246 P: Future<Output = Result<R, JsError>> + Send + 'static,
1247 M: FnOnce(&QuickJsRealmAdapter, R) -> Result<QuickJsValueAdapter, JsError> + Send + 'static,
1248 Self: Sized + 'static,
1249 {
1250 new_resolving_promise_async(self, producer, mapper)
1251 }
1252 pub fn create_resolving_promise<P, R: Send + 'static, M>(
1256 &self,
1257 producer: P,
1258 mapper: M,
1259 ) -> Result<QuickJsValueAdapter, JsError>
1260 where
1261 P: FnOnce() -> Result<R, JsError> + Send + 'static,
1262 M: FnOnce(&QuickJsRealmAdapter, R) -> Result<QuickJsValueAdapter, JsError> + Send + 'static,
1263 Self: Sized + 'static,
1264 {
1265 new_resolving_promise(self, producer, mapper)
1266 }
1267}
1268
1269#[cfg(test)]
1270pub mod tests {
1271 use crate::builder::QuickJsRuntimeBuilder;
1272 use crate::facades::tests::init_test_rt;
1273 use crate::jsutils::Script;
1274 use crate::quickjs_utils;
1275 use crate::quickjs_utils::primitives::to_i32;
1276 use crate::quickjs_utils::{functions, get_global_q, objects};
1277
1278 #[test]
1279 fn test_eval() {
1280 let rt = init_test_rt();
1281 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1282 let q_ctx = q_js_rt.get_main_realm();
1283 let res = q_ctx.eval(Script::new("test_eval.es", "(1 + 1);"));
1284
1285 match res {
1286 Ok(res) => {
1287 log::info!("script ran ok: {:?}", res);
1288 assert!(res.is_i32());
1289 assert_eq!(to_i32(&res).ok().expect("conversion failed"), 2);
1290 }
1291 Err(e) => {
1292 log::error!("script failed: {}", e);
1293 panic!("script failed");
1294 }
1295 }
1296 });
1297 }
1298
1299 #[test]
1300 fn test_multi_ctx() {
1301 let rt = QuickJsRuntimeBuilder::new().build();
1302 rt.create_context("a").ok().expect("could not create ctx a");
1303 rt.create_context("b").ok().expect("could not create ctx b");
1304
1305 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1306 let ctx_a = q_js_rt.get_context("a");
1307 let ctx_b = q_js_rt.get_context("b");
1308 ctx_a
1309 .eval(Script::new("a.es", "this.a = 1"))
1310 .ok()
1311 .expect("script failed");
1312 ctx_b
1313 .eval(Script::new("a.es", "this.b = 1"))
1314 .ok()
1315 .expect("script failed");
1316 let v = ctx_a
1317 .eval(Script::new("a2.es", "this.a;"))
1318 .ok()
1319 .expect("script failed");
1320 assert!(v.is_i32());
1321 let v2 = ctx_b
1322 .eval(Script::new("b2.es", "this.a;"))
1323 .ok()
1324 .expect("script failed");
1325 assert!(v2.is_null_or_undefined());
1326 let v3 = ctx_a
1327 .eval(Script::new("a2.es", "this.b;"))
1328 .ok()
1329 .expect("script failed");
1330 assert!(v3.is_null_or_undefined());
1331 let v4 = ctx_b
1332 .eval(Script::new("b2.es", "this.b;"))
1333 .ok()
1334 .expect("script failed");
1335 assert!(v4.is_i32());
1336 });
1337 let _ = rt.drop_context("b");
1338
1339 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1340 q_js_rt.gc();
1341 let ctx_a = q_js_rt.get_context("a");
1342 let v = ctx_a
1343 .eval(Script::new("a2.es", "this.a;"))
1344 .ok()
1345 .expect("script failed");
1346 assert!(v.is_i32());
1347 q_js_rt.gc();
1348 });
1349
1350 rt.create_context("c")
1351 .ok()
1352 .expect("could not create context c");
1353
1354 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1355 let c_ctx = q_js_rt.get_context("c");
1356 let func = functions::new_function_q(
1357 c_ctx,
1358 "test",
1359 |_q_ctx, _this, _args| Ok(quickjs_utils::new_null_ref()),
1360 1,
1361 )
1362 .ok()
1363 .unwrap();
1364 let global = get_global_q(c_ctx);
1365 objects::set_property_q(c_ctx, &global, "test_func", &func)
1366 .ok()
1367 .expect("could not set prop");
1368 q_js_rt.gc();
1369 });
1370 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1371 q_js_rt.gc();
1372 let ctx_a = q_js_rt.get_context("a");
1373 let v = ctx_a
1374 .eval(Script::new("a2.es", "this.a;"))
1375 .ok()
1376 .expect("script failed");
1377 assert!(v.is_i32());
1378 q_js_rt.gc();
1379 });
1380 let _ = rt.drop_context("c");
1381 rt.exe_rt_task_in_event_loop(|q_js_rt| {
1382 q_js_rt.gc();
1383 let ctx_a = q_js_rt.get_context("a");
1384 let v = ctx_a
1385 .eval(Script::new("a2.es", "this.a;"))
1386 .ok()
1387 .expect("script failed");
1388 assert!(v.is_i32());
1389 q_js_rt.gc();
1390 });
1391 }
1392}