1use fusevm::{Chunk, NumOp, VMResult, Value, VM};
17use indexmap::IndexMap;
18use std::cell::RefCell;
19use std::collections::HashMap;
20use std::collections::HashSet;
21use std::rc::Rc;
22use std::sync::mpsc::{Receiver, Sender};
23use std::time::{Duration, Instant};
24
25pub type IoTask = Box<dyn FnOnce() -> Result<(), String> + Send>;
31
32pub mod ops {
36 pub const GETLOCAL: u16 = 1; pub const SETLOCAL: u16 = 2; pub const DECLARE: u16 = 3; pub const DELNAME: u16 = 4; pub const GETATTR: u16 = 5; pub const SETATTR: u16 = 6; pub const GETITEM: u16 = 7; pub const SETITEM: u16 = 8; pub const DELITEM: u16 = 9; pub const MKSTR: u16 = 10; pub const MKARR: u16 = 11; pub const MKOBJ: u16 = 12; pub const CALL: u16 = 13; pub const CALL_METHOD: u16 = 14; pub const CALL_VALUE: u16 = 15; pub const NEW: u16 = 16; pub const TRUTHY: u16 = 17; pub const TOSTR: u16 = 18; pub const MKFUNC: u16 = 19; pub const GETITER: u16 = 20; pub const FORITER: u16 = 21; pub const FORIN_KEYS: u16 = 22; pub const CONTAINS: u16 = 23; pub const SIG_RETURN: u16 = 24; pub const BINOP: u16 = 25; pub const UNARY: u16 = 26; pub const STRICT_EQ: u16 = 27; pub const LOOSE_EQ: u16 = 28; pub const TYPEOF: u16 = 29; pub const LOAD_NULL: u16 = 30; pub const THROW: u16 = 31; pub const TRY: u16 = 32; pub const NULLISH: u16 = 33; pub const UNPACK: u16 = 34; pub const BUILD_ARGS: u16 = 35; pub const THIS: u16 = 36; pub const INSTANCEOF: u16 = 37; pub const DELPROP_NAME: u16 = 38; pub const APPLY: u16 = 39; pub const APPLY_METHOD: u16 = 40; pub const OBJ_REST: u16 = 41; pub const DIV: u16 = 42; pub const MKCLASS: u16 = 43; pub const DEF_MEMBER: u16 = 44; pub const SUPER_CALL: u16 = 45; pub const SUPER_GET: u16 = 46; pub const YIELD: u16 = 47; pub const PROPKEY: u16 = 48; pub const NEW_TARGET: u16 = 49; pub const DEF_FIELD: u16 = 50; pub const AWAIT: u16 = 51; pub const DEF_ACCESSOR: u16 = 52; pub const DBG_LINE: u16 = 53; pub const MKBIGINT: u16 = 54; pub const MKREGEX: u16 = 55; pub const TAG_TMPL: u16 = 56; pub const GET_ASYNC_ITER: u16 = 57; pub const ASYNC_STEP: u16 = 58; pub const NUM_STEP: u16 = 59; pub const ITER_CLOSE: u16 = 60; pub const TYPEOF_NAME: u16 = 61; }
98
99pub mod member {
101 pub const METHOD: i64 = 0;
102 pub const GET: i64 = 1;
103 pub const SET: i64 = 2;
104}
105
106pub mod binop {
108 pub const BITAND: i64 = 0;
109 pub const BITOR: i64 = 1;
110 pub const BITXOR: i64 = 2;
111 pub const SHL: i64 = 3;
112 pub const SHR: i64 = 4;
113 pub const USHR: i64 = 5;
114}
115
116pub mod unop {
118 pub const POS: i64 = 0; pub const BITNOT: i64 = 1; }
121
122#[derive(Clone, serde::Serialize, serde::Deserialize)]
127pub struct FuncDef {
128 pub name: String,
129 pub params: Vec<ParamSlot>,
132 pub chunk: Chunk,
133 pub is_arrow: bool,
134 pub is_generator: bool,
137 pub is_async: bool,
140}
141
142#[derive(Clone, serde::Serialize, serde::Deserialize)]
145pub struct ParamSlot {
146 pub name: String,
147 pub rest: bool,
149 pub has_default: bool,
151}
152
153#[derive(Clone, serde::Serialize, serde::Deserialize)]
156pub struct TryDef {
157 pub block: Chunk,
158 pub handler: Option<(Option<String>, Chunk)>,
160 pub finalizer: Option<Chunk>,
161}
162
163#[derive(Clone)]
165pub struct FuncVal {
166 pub def_id: usize,
167 pub env: Option<Env>,
169 pub this: Option<Value>,
171 pub is_arrow: bool,
172 pub home_class: Option<String>,
175}
176
177#[derive(Clone)]
179pub enum JsObj {
180 Str(String),
181 Array(Vec<Value>),
182 Object(IndexMap<String, Value>),
183 Func(FuncVal),
184 Builtin(String),
187 BoundMethod {
190 recv: Value,
191 name: String,
192 },
193 Null,
195 Iter {
197 items: Vec<Value>,
198 idx: usize,
199 },
200 BoundFunc {
202 target: Value,
203 this: Value,
204 args: Vec<Value>,
205 },
206 Class(ClassVal),
208 Symbol {
211 desc: Option<String>,
212 id: u64,
213 },
214 Map {
216 entries: IndexMap<MapKey, (Value, Value)>,
217 weak: bool,
218 },
219 Set {
221 entries: IndexMap<MapKey, Value>,
222 weak: bool,
223 },
224 Generator {
227 id: u32,
228 },
229 Promise {
231 id: u32,
232 },
233 BigInt(num_bigint::BigInt),
235 RegExp(Box<RegExpObj>),
237}
238
239#[derive(Clone)]
244pub struct RegExpObj {
245 pub re: fancy_regex::Regex,
249 pub source: String,
250 pub flags: String,
251 pub global: bool,
252 pub ignore_case: bool,
253 pub multiline: bool,
254 pub dot_all: bool,
255 pub sticky: bool,
256 pub unicode: bool,
257 pub last_index: usize,
260}
261
262pub struct PromiseCell {
264 pub state: PromiseState,
265 pub value: Value,
266 pub reactions: Vec<PromiseReaction>,
269 pub handled: bool,
272}
273
274pub enum PromiseReaction {
277 Js {
278 on_ful: Value,
279 on_rej: Value,
280 result: Value,
281 },
282 Native(Box<dyn FnOnce(PromiseState, Value) -> Result<(), String>>),
283}
284
285#[derive(Default, Clone, Copy, PartialEq, Eq)]
286pub enum PromiseState {
287 #[default]
288 Pending,
289 Fulfilled,
290 Rejected,
291}
292
293#[derive(Clone)]
297pub struct ClassVal {
298 pub name: String,
299 pub ctor: Option<Value>,
302 pub parent: Option<Value>,
303 pub proto: Value,
305 pub statics: IndexMap<String, Value>,
307 pub fields: Vec<(String, Value)>,
310}
311
312pub enum SuperRef {
315 Getter(Value),
316 Data(Value),
317}
318
319#[derive(Clone, PartialEq, Eq, Hash)]
322pub enum MapKey {
323 Undef,
324 Null,
325 Bool(bool),
326 Num(u64),
328 Big(String),
330 Str(String),
331 Ref(u32),
333}
334
335pub struct EnvData {
340 pub vars: IndexMap<String, Value>,
341 pub parent: Option<Env>,
342}
343pub type Env = Rc<RefCell<EnvData>>;
344
345pub type Accessor = (Option<Value>, Option<Value>);
347
348fn new_env(parent: Option<Env>) -> Env {
349 Rc::new(RefCell::new(EnvData {
350 vars: IndexMap::new(),
351 parent,
352 }))
353}
354
355pub struct Frame {
357 pub env: Env,
358 pub this_obj: Option<Value>,
359 pub new_target: Option<Value>,
361 pub home_class: Option<Value>,
364 pub line: u32,
367 pub owner: Option<String>,
370}
371
372#[derive(Clone)]
374pub enum Signal {
375 Return(Value),
376 Break,
377 Continue,
378}
379
380pub struct JsHost {
382 heap: Vec<JsObj>,
383 pub funcs: Vec<FuncDef>,
385 pub tries: Vec<TryDef>,
387 globals: IndexMap<String, Value>,
389 frames: Vec<Frame>,
391 pub error: Option<String>,
392 pub exc: Option<Value>,
394 pub signal: Option<Signal>,
395 null_val: Value,
397 protos: HashMap<u32, Value>,
400 null_proto_objs: HashSet<u32>,
405 fn_props: HashMap<u32, IndexMap<String, Value>>,
408 accessors: HashMap<u32, IndexMap<String, Accessor>>,
411 builtin_statics: HashMap<String, IndexMap<String, Value>>,
417 object_proto: Value,
419 proto_class: HashMap<u32, Value>,
423 class_registry: HashMap<String, Value>,
426 error_protos: HashMap<String, Value>,
428 symbol_registry: HashMap<String, Value>,
430 next_symbol: u64,
432 generators: Vec<GenCell>,
434 promises: Vec<PromiseCell>,
436 pub nextticks: std::collections::VecDeque<Task>,
438 pub microtasks: std::collections::VecDeque<Task>,
440 pub macrotasks: Vec<Timer>,
442 next_timer: u64,
444 io_tx: Sender<IoTask>,
448 io_rx: Option<Receiver<IoTask>>,
451 open_handles: usize,
456}
457
458pub enum Task {
461 Js { cb: Value, args: Vec<Value> },
462 Native(Box<dyn FnOnce() -> Result<(), String>>),
463}
464
465impl Task {
466 fn run(self) -> Result<(), String> {
467 match self {
468 Task::Js { cb, args } => invoke(&cb, args, None).map(|_| ()),
469 Task::Native(f) => f(),
470 }
471 }
472}
473
474pub struct Timer {
477 pub id: u64,
478 pub delay: f64,
479 pub seq: u64,
480 pub callback: Value,
481 pub args: Vec<Value>,
482 pub cancelled: bool,
483 pub deadline: Instant,
487}
488
489struct GenCell {
493 coro: Option<corosensei::Coroutine<Value, Value, Result<Value, String>>>,
494 yielder: *const (),
497 ctx: GenContext,
498 done: bool,
499 started: bool,
502 inject: Option<GenInject>,
505}
506
507enum GenInject {
509 Return(Value),
510 Throw(Value),
511}
512
513#[derive(Default)]
518struct GenContext {
519 frames: Vec<Frame>,
520 error: Option<String>,
521 exc: Option<Value>,
522 signal: Option<Signal>,
523}
524
525thread_local! {
526 static CUR_GEN: std::cell::Cell<Option<u32>> = const { std::cell::Cell::new(None) };
529}
530
531thread_local! {
532 static HOST: RefCell<JsHost> = RefCell::new(JsHost::new());
533}
534
535pub fn with_host<R>(f: impl FnOnce(&mut JsHost) -> R) -> R {
537 HOST.with(|h| f(&mut h.borrow_mut()))
538}
539
540pub fn reset_host() {
542 with_host(|h| *h = JsHost::new());
543 crate::module::reset();
545}
546
547impl Default for JsHost {
548 fn default() -> Self {
549 Self::new()
550 }
551}
552
553impl JsHost {
554 pub fn new() -> JsHost {
555 let module_env = new_env(None);
556 let (io_tx, io_rx) = std::sync::mpsc::channel();
557 let mut h = JsHost {
558 heap: Vec::new(),
559 funcs: Vec::new(),
560 tries: Vec::new(),
561 globals: IndexMap::new(),
562 frames: vec![Frame {
563 env: module_env,
564 this_obj: None,
565 new_target: None,
566 home_class: None,
567 line: 0,
568 owner: None,
569 }],
570 error: None,
571 exc: None,
572 signal: None,
573 null_val: Value::Undef,
574 protos: HashMap::new(),
575 null_proto_objs: HashSet::new(),
576 fn_props: HashMap::new(),
577 accessors: HashMap::new(),
578 builtin_statics: HashMap::new(),
579 object_proto: Value::Undef,
580 proto_class: HashMap::new(),
581 class_registry: HashMap::new(),
582 error_protos: HashMap::new(),
583 symbol_registry: HashMap::new(),
584 next_symbol: 1,
585 generators: Vec::new(),
586 promises: Vec::new(),
587 microtasks: std::collections::VecDeque::new(),
588 nextticks: std::collections::VecDeque::new(),
589 macrotasks: Vec::new(),
590 next_timer: 1,
591 io_tx,
592 io_rx: Some(io_rx),
593 open_handles: 0,
594 };
595 h.null_val = h.alloc(JsObj::Null);
596 h.object_proto = h.new_object(IndexMap::new());
598 h
599 }
600
601 pub fn proto_of(&self, v: &Value) -> Option<Value> {
604 if let Value::Obj(i) = v {
605 self.protos.get(i).cloned()
606 } else {
607 None
608 }
609 }
610 pub fn set_proto(&mut self, v: &Value, proto: Value) {
614 if let Value::Obj(i) = v {
615 if self.is_null(&proto) {
616 self.protos.remove(i);
617 self.null_proto_objs.insert(*i);
618 } else if matches!(proto, Value::Undef) {
619 self.protos.remove(i);
620 } else {
621 self.protos.insert(*i, proto);
622 self.null_proto_objs.remove(i);
623 }
624 }
625 }
626 pub fn has_null_proto(&self, v: &Value) -> bool {
628 matches!(v, Value::Obj(i) if self.null_proto_objs.contains(i))
629 }
630 pub fn object_proto(&self) -> Value {
631 self.object_proto.clone()
632 }
633 pub fn tag_proto_class(&mut self, proto: &Value, class_val: Value) {
636 if let Value::Obj(i) = proto {
637 self.proto_class.insert(*i, class_val);
638 }
639 }
640 pub fn class_of(&self, obj: &Value) -> Option<Value> {
642 let mut cur = self.proto_of(obj);
643 while let Some(p) = cur {
644 if let Value::Obj(i) = &p {
645 if let Some(c) = self.proto_class.get(i) {
646 return Some(c.clone());
647 }
648 }
649 cur = self.proto_of(&p);
650 }
651 None
652 }
653 pub fn ctor_name(&self, obj: &Value) -> String {
656 match self.class_of(obj) {
657 Some(c) => match self.get(&c) {
658 Some(JsObj::Class(cv)) => cv.name.clone(),
659 _ => String::new(),
660 },
661 None => String::new(),
662 }
663 }
664
665 pub fn fn_prop(&self, v: &Value, name: &str) -> Option<Value> {
667 if let Value::Obj(i) = v {
668 self.fn_props.get(i).and_then(|m| m.get(name).cloned())
669 } else {
670 None
671 }
672 }
673
674 pub fn class_static(&self, class_val: &Value, name: &str) -> Option<Value> {
677 let mut cur = class_val.clone();
678 loop {
679 if let Some(v) = self.fn_prop(&cur, name) {
680 return Some(v);
681 }
682 match self.get(&cur) {
683 Some(JsObj::Class(c)) => cur = c.parent.clone()?,
684 _ => return None,
685 }
686 }
687 }
688 pub fn set_fn_prop(&mut self, v: &Value, name: &str, val: Value) {
689 if let Value::Obj(i) = v {
690 self.fn_props
691 .entry(*i)
692 .or_default()
693 .insert(name.to_string(), val);
694 }
695 }
696 pub fn builtin_static(&self, ns: &str, name: &str) -> Option<Value> {
698 self.builtin_statics
699 .get(ns)
700 .and_then(|m| m.get(name).cloned())
701 }
702 pub fn set_builtin_static(&mut self, ns: &str, name: &str, val: Value) {
705 self.builtin_statics
706 .entry(ns.to_string())
707 .or_default()
708 .insert(name.to_string(), val);
709 }
710 pub fn fn_prop_keys(&self, v: &Value) -> Vec<String> {
711 if let Value::Obj(i) = v {
712 self.fn_props
713 .get(i)
714 .map(|m| m.keys().cloned().collect())
715 .unwrap_or_default()
716 } else {
717 Vec::new()
718 }
719 }
720
721 pub fn set_accessor(
723 &mut self,
724 owner: &Value,
725 key: &str,
726 get: Option<Value>,
727 set: Option<Value>,
728 ) {
729 if let Value::Obj(i) = owner {
730 let slot = self
731 .accessors
732 .entry(*i)
733 .or_default()
734 .entry(key.to_string())
735 .or_insert((None, None));
736 if get.is_some() {
737 slot.0 = get;
738 }
739 if set.is_some() {
740 slot.1 = set;
741 }
742 }
743 }
744 pub fn own_accessor(&self, owner: &Value, key: &str) -> Option<(Option<Value>, Option<Value>)> {
746 if let Value::Obj(i) = owner {
747 self.accessors.get(i).and_then(|m| m.get(key).cloned())
748 } else {
749 None
750 }
751 }
752
753 pub fn new_symbol(&mut self, desc: Option<String>) -> Value {
755 let id = self.next_symbol;
756 self.next_symbol += 1;
757 self.alloc(JsObj::Symbol { desc, id })
758 }
759 pub fn symbol_for(&mut self, key: &str) -> Value {
761 if let Some(v) = self.symbol_registry.get(key) {
762 return v.clone();
763 }
764 let s = self.new_symbol(Some(key.to_string()));
765 self.symbol_registry.insert(key.to_string(), s.clone());
766 s
767 }
768 pub fn well_known_iterator(&mut self) -> Value {
771 self.symbol_for("@@Symbol.iterator")
772 }
773 pub fn well_known_async_iterator(&mut self) -> Value {
775 self.symbol_for("@@Symbol.asyncIterator")
776 }
777 pub fn property_key(&self, v: &Value) -> String {
781 if let Some(JsObj::Symbol { desc, id }) = self.get(v) {
782 if desc.as_deref() == Some("@@Symbol.iterator") {
783 return "@@iterator".to_string();
784 }
785 if desc.as_deref() == Some("@@Symbol.asyncIterator") {
786 return "@@asyncIterator".to_string();
787 }
788 return format!("@@sym:{id}");
789 }
790 self.str_of(v)
791 }
792
793 pub fn null(&self) -> Value {
794 self.null_val.clone()
795 }
796 pub fn is_null(&self, v: &Value) -> bool {
797 matches!(self.get(v), Some(JsObj::Null))
798 }
799
800 pub fn program_offsets(&self) -> (usize, usize) {
802 (self.funcs.len(), self.tries.len())
803 }
804 pub fn load_program(&mut self, funcs: Vec<FuncDef>, tries: Vec<TryDef>) {
805 self.funcs.extend(funcs);
806 self.tries.extend(tries);
807 }
808 pub fn try_def(&self, id: usize) -> Option<TryDef> {
809 self.tries.get(id).cloned()
810 }
811
812 pub fn alloc(&mut self, obj: JsObj) -> Value {
814 self.heap.push(obj);
815 Value::Obj((self.heap.len() - 1) as u32)
816 }
817 pub fn get(&self, v: &Value) -> Option<&JsObj> {
818 if let Value::Obj(i) = v {
819 self.heap.get(*i as usize)
820 } else {
821 None
822 }
823 }
824 pub fn get_mut(&mut self, v: &Value) -> Option<&mut JsObj> {
825 if let Value::Obj(i) = v {
826 self.heap.get_mut(*i as usize)
827 } else {
828 None
829 }
830 }
831 pub fn new_str(&mut self, s: impl Into<String>) -> Value {
832 self.alloc(JsObj::Str(s.into()))
833 }
834 pub fn new_array(&mut self, items: Vec<Value>) -> Value {
835 self.alloc(JsObj::Array(items))
836 }
837 pub fn new_object(&mut self, mut props: IndexMap<String, Value>) -> Value {
838 canonicalize_own_keys(&mut props);
841 self.alloc(JsObj::Object(props))
842 }
843 pub fn as_str(&self, v: &Value) -> Option<String> {
844 match v {
845 Value::Str(s) => Some((**s).clone()),
846 Value::Obj(_) => match self.get(v) {
847 Some(JsObj::Str(s)) => Some(s.clone()),
848 _ => None,
849 },
850 _ => None,
851 }
852 }
853
854 fn frame(&self) -> &Frame {
856 self.frames.last().unwrap()
857 }
858 fn cur_env(&self) -> Env {
859 self.frame().env.clone()
860 }
861
862 pub fn frame_depth(&self) -> usize {
865 self.frames.len()
866 }
867 pub fn set_cur_line(&mut self, line: u32) {
869 if let Some(f) = self.frames.last_mut() {
870 f.line = line;
871 }
872 }
873 pub fn dbg_stack(&self) -> Vec<(String, u32)> {
876 self.frames
877 .iter()
878 .rev()
879 .map(|f| {
880 let name = f.owner.clone().unwrap_or_else(|| "<module>".to_string());
881 (name, f.line)
882 })
883 .collect()
884 }
885 pub fn dbg_locals(&self) -> Vec<(String, String)> {
887 let env = self.cur_env();
888 let names: Vec<String> = env.borrow().vars.keys().cloned().collect();
889 names
890 .into_iter()
891 .map(|n| {
892 let v = self.read_name(&n).unwrap_or(Value::Undef);
893 (n, self.inspect(&v))
894 })
895 .collect()
896 }
897
898 pub fn read_name(&self, name: &str) -> Option<Value> {
900 let mut env = Some(self.cur_env());
901 while let Some(e) = env {
902 if let Some(v) = e.borrow().vars.get(name) {
903 return Some(v.clone());
904 }
905 env = e.borrow().parent.clone();
906 }
907 self.globals.get(name).cloned()
908 }
909 pub fn read_global(&self, name: &str) -> Option<Value> {
910 self.globals.get(name).cloned()
911 }
912
913 pub fn set_name(&mut self, name: &str, val: Value) {
916 let mut env = Some(self.cur_env());
917 while let Some(e) = env {
918 if e.borrow().vars.contains_key(name) {
919 e.borrow_mut().vars.insert(name.to_string(), val);
920 return;
921 }
922 env = e.borrow().parent.clone();
923 }
924 self.globals.insert(name.to_string(), val);
925 }
926
927 pub fn declare_name(&mut self, name: &str, val: Value) {
929 if self.frames.len() == 1 {
930 self.globals.insert(name.to_string(), val);
931 } else {
932 self.cur_env()
933 .borrow_mut()
934 .vars
935 .insert(name.to_string(), val);
936 }
937 }
938 pub fn set_global(&mut self, name: &str, val: Value) {
939 self.globals.insert(name.to_string(), val);
940 }
941 pub fn del_name(&mut self, name: &str) {
942 if self
943 .cur_env()
944 .borrow_mut()
945 .vars
946 .shift_remove(name)
947 .is_some()
948 {
949 return;
950 }
951 self.globals.shift_remove(name);
952 }
953
954 pub fn current_this(&self) -> Option<Value> {
955 self.frame().this_obj.clone()
956 }
957 pub fn current_env_capture(&self) -> Env {
958 self.frame().env.clone()
959 }
960 pub fn current_new_target(&self) -> Option<Value> {
961 self.frame().new_target.clone()
962 }
963 fn current_home_class(&self) -> Option<Value> {
964 self.frame().home_class.clone()
965 }
966
967 pub fn super_context(&self) -> (Option<Value>, Vec<(String, Value)>) {
970 match self.current_home_class() {
971 Some(cv) => match self.get(&cv) {
972 Some(JsObj::Class(c)) => (c.parent.clone(), c.fields.clone()),
973 _ => (None, Vec::new()),
974 },
975 None => (None, Vec::new()),
976 }
977 }
978
979 pub fn super_resolve(&self, name: &str) -> SuperRef {
982 let parent = match self
983 .current_home_class()
984 .and_then(|cv| match self.get(&cv) {
985 Some(JsObj::Class(c)) => c.parent.clone(),
986 _ => None,
987 }) {
988 Some(p) => p,
989 None => return SuperRef::Data(Value::Undef),
990 };
991 let parent_proto = match self.get(&parent) {
992 Some(JsObj::Class(pc)) => pc.proto.clone(),
993 _ => self.fn_prop(&parent, "prototype").unwrap_or(Value::Undef),
994 };
995 if let Some((Some(getter), _)) = lookup_accessor(self, &parent_proto, name) {
996 return SuperRef::Getter(getter);
997 }
998 SuperRef::Data(lookup_chain(self, &parent_proto, name).unwrap_or(Value::Undef))
999 }
1000
1001 pub fn take_error(&mut self) -> Option<String> {
1003 self.error.take()
1004 }
1005 pub fn raise_str(&mut self, class: &str, msg: &str) -> String {
1006 let s = if msg.is_empty() {
1007 class.to_string()
1008 } else {
1009 format!("{class}: {msg}")
1010 };
1011 self.error = Some(s.clone());
1012 s
1013 }
1014}
1015
1016pub fn type_error(msg: &str) -> String {
1019 format!("TypeError: {msg}")
1020}
1021pub fn ref_error(name: &str) -> String {
1022 format!("ReferenceError: {name} is not defined")
1023}
1024pub fn range_error(msg: &str) -> String {
1025 format!("RangeError: {msg}")
1026}
1027
1028thread_local! {
1031 static DEBUG_MODE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
1032}
1033
1034pub fn set_debug_mode(on: bool) {
1036 DEBUG_MODE.with(|d| d.set(on));
1037}
1038
1039pub fn run_chunk_on(chunk: Chunk) -> Result<Value, String> {
1041 let mut vm = VM::new(chunk);
1042 crate::builtins::install(&mut vm);
1043 vm.set_numeric_hook(std::sync::Arc::new(|op, a, b| {
1044 crate::builtins::numeric_hook(op, a, b)
1045 }));
1046 if DEBUG_MODE.with(|d| d.get()) {
1051 vm.set_extension_handler(Box::new(|vm, id, _| {
1052 crate::dap::on_ext(vm, id);
1053 }));
1054 } else {
1055 vm.enable_tracing_jit();
1056 }
1057 let outcome = vm.run();
1058 if let Some(e) = with_host(|h| h.take_error()) {
1059 return Err(e);
1060 }
1061 match outcome {
1062 VMResult::Ok(v) => Ok(v),
1063 VMResult::Halted => Ok(vm.stack.last().cloned().unwrap_or(Value::Undef)),
1064 VMResult::Error(e) => Err(e),
1065 }
1066}
1067
1068pub fn run_main(chunk: Chunk) -> Result<Value, String> {
1072 let r = run_chunk_on(chunk);
1073 with_host(|h| h.signal = None);
1074 if r.is_ok() {
1075 run_event_loop()?;
1076 }
1077 r
1078}
1079
1080pub fn fmt_number(f: f64) -> String {
1085 if f.is_nan() {
1086 return "NaN".into();
1087 }
1088 if f.is_infinite() {
1089 return if f > 0.0 { "Infinity" } else { "-Infinity" }.into();
1090 }
1091 if f == 0.0 {
1092 return "0".into();
1094 }
1095 if f < 0.0 {
1096 return format!("-{}", js_number_repr(-f));
1097 }
1098 js_number_repr(f)
1099}
1100
1101pub fn array_index(k: &str) -> Option<u32> {
1106 if k.is_empty() {
1107 return None;
1108 }
1109 if k == "0" {
1110 return Some(0);
1111 }
1112 if k.as_bytes()[0] == b'0' {
1114 return None;
1115 }
1116 if !k.bytes().all(|b| b.is_ascii_digit()) {
1117 return None;
1118 }
1119 match k.parse::<u64>() {
1120 Ok(n) if n < u32::MAX as u64 => Some(n as u32),
1122 _ => None,
1123 }
1124}
1125
1126pub fn key_order_cmp(a: &str, b: &str) -> std::cmp::Ordering {
1132 use std::cmp::Ordering;
1133 match (array_index(a), array_index(b)) {
1134 (Some(x), Some(y)) => x.cmp(&y),
1135 (Some(_), None) => Ordering::Less,
1136 (None, Some(_)) => Ordering::Greater,
1137 (None, None) => Ordering::Equal,
1138 }
1139}
1140
1141pub fn canonicalize_own_keys(props: &mut IndexMap<String, Value>) {
1147 if props.keys().any(|k| array_index(k).is_some()) {
1148 props.sort_by(|ak, _, bk, _| key_order_cmp(ak, bk));
1149 }
1150}
1151
1152fn js_number_repr(a: f64) -> String {
1162 let sci = format!("{a:e}");
1165 let (mant, exp_str) = sci.split_once('e').expect("LowerExp always has 'e'");
1166 let e: i32 = exp_str.parse().expect("LowerExp exponent is an integer");
1167 let s: String = mant.chars().filter(|c| *c != '.').collect();
1168 let k = s.len() as i32; let n = e + 1; if k <= n && n <= 21 {
1172 let mut out = s;
1174 out.push_str(&"0".repeat((n - k) as usize));
1175 out
1176 } else if 0 < n && n <= 21 {
1177 format!("{}.{}", &s[..n as usize], &s[n as usize..])
1179 } else if -6 < n && n <= 0 {
1180 format!("0.{}{}", "0".repeat((-n) as usize), s)
1182 } else {
1183 let exp = n - 1;
1185 let sign = if exp >= 0 { '+' } else { '-' };
1186 let mag = exp.abs();
1187 if k == 1 {
1188 format!("{s}e{sign}{mag}")
1189 } else {
1190 format!("{}.{}e{sign}{mag}", &s[..1], &s[1..])
1191 }
1192 }
1193}
1194
1195impl JsHost {
1196 pub fn type_of(&self, v: &Value) -> &'static str {
1198 match v {
1199 Value::Undef => "undefined",
1200 Value::Bool(_) => "boolean",
1201 Value::Int(_) | Value::Float(_) => "number",
1202 Value::Str(_) => "string",
1203 Value::Obj(_) => match self.get(v) {
1204 Some(JsObj::Str(_)) => "string",
1205 Some(JsObj::Func(_))
1206 | Some(JsObj::BoundMethod { .. })
1207 | Some(JsObj::BoundFunc { .. })
1208 | Some(JsObj::Class(_)) => "function",
1209 Some(JsObj::Builtin(n)) => {
1213 const NON_CALLABLE_NS: &[&str] = &[
1214 "Math",
1215 "JSON",
1216 "console",
1217 "Reflect",
1218 "process",
1219 "Atomics",
1220 "performance",
1221 "fs",
1222 "path",
1223 "os",
1224 "util",
1225 "crypto",
1226 "querystring",
1227 "events",
1228 "stream",
1229 "timers",
1230 "perf_hooks",
1231 "async_hooks",
1232 "diagnostics_channel",
1233 "v8",
1234 "dns",
1235 "punycode",
1236 "child_process",
1237 "tty",
1238 "url",
1239 "zlib",
1240 "string_decoder",
1241 "assert",
1242 "http",
1243 "net",
1244 "buffer",
1245 ];
1246 if NON_CALLABLE_NS.contains(&n.as_str()) {
1247 "object"
1248 } else {
1249 "function"
1250 }
1251 }
1252 Some(JsObj::Symbol { .. }) => "symbol",
1253 Some(JsObj::BigInt(_)) => "bigint",
1254 _ => "object", },
1256 _ => "object",
1257 }
1258 }
1259
1260 pub fn truthy(&self, v: &Value) -> bool {
1262 match v {
1263 Value::Undef => false,
1264 Value::Bool(b) => *b,
1265 Value::Int(n) => *n != 0,
1266 Value::Float(f) => *f != 0.0 && !f.is_nan(),
1267 Value::Str(s) => !s.is_empty(),
1268 Value::Obj(_) => match self.get(v) {
1269 Some(JsObj::Str(s)) => !s.is_empty(),
1270 Some(JsObj::Null) => false,
1271 Some(JsObj::BigInt(b)) => !num_traits::Zero::is_zero(b),
1272 _ => true, },
1274 _ => true,
1275 }
1276 }
1277
1278 pub fn to_number(&self, v: &Value) -> f64 {
1280 match v {
1281 Value::Undef => f64::NAN,
1282 Value::Bool(b) => {
1283 if *b {
1284 1.0
1285 } else {
1286 0.0
1287 }
1288 }
1289 Value::Int(n) => *n as f64,
1290 Value::Float(f) => *f,
1291 Value::Str(s) => str_to_number(s),
1292 Value::Obj(_) => match self.get(v) {
1293 Some(JsObj::Str(s)) => str_to_number(s),
1294 Some(JsObj::Null) => 0.0,
1295 Some(JsObj::BigInt(b)) => bigint_to_f64(b),
1296 Some(JsObj::Array(items)) => {
1297 if items.is_empty() {
1299 0.0
1300 } else if items.len() == 1 {
1301 self.to_number(&items[0])
1302 } else {
1303 f64::NAN
1304 }
1305 }
1306 _ => f64::NAN,
1307 },
1308 _ => f64::NAN,
1309 }
1310 }
1311
1312 pub fn str_of(&self, v: &Value) -> String {
1314 match v {
1315 Value::Undef => "undefined".into(),
1316 Value::Bool(b) => if *b { "true" } else { "false" }.into(),
1317 Value::Int(n) => n.to_string(),
1318 Value::Float(f) => fmt_number(*f),
1319 Value::Str(s) => (**s).clone(),
1320 Value::Obj(_) => match self.get(v) {
1321 Some(JsObj::Str(s)) => s.clone(),
1322 Some(JsObj::Null) => "null".into(),
1323 Some(JsObj::BigInt(b)) => b.to_string(),
1324 Some(JsObj::RegExp(r)) => format!("/{}/{}", r.source, r.flags),
1325 Some(JsObj::Array(items)) => {
1326 let parts: Vec<String> = items
1328 .iter()
1329 .map(|x| match x {
1330 Value::Undef => String::new(),
1331 _ if self.is_null(x) => String::new(),
1332 _ => self.str_of(x),
1333 })
1334 .collect();
1335 parts.join(",")
1336 }
1337 Some(JsObj::Object(props)) => {
1338 if props.get("@@native").map(|t| self.str_of(t)).as_deref() == Some("Buffer") {
1343 let bytes: Vec<u8> = match props.get("@@bytes").and_then(|b| self.get(b)) {
1344 Some(JsObj::Array(items)) => {
1345 items.iter().map(|x| self.to_number(x) as u8).collect()
1346 }
1347 _ => Vec::new(),
1348 };
1349 String::from_utf8_lossy(&bytes).into_owned()
1350 } else {
1351 "[object Object]".into()
1352 }
1353 }
1354 Some(JsObj::Func(f)) => {
1355 let name = self
1356 .funcs
1357 .get(f.def_id)
1358 .map(|d| d.name.clone())
1359 .unwrap_or_default();
1360 format!("function {name}() {{ [code] }}")
1361 }
1362 Some(JsObj::Builtin(n)) => format!("function {n}() {{ [native code] }}"),
1363 Some(JsObj::BoundMethod { .. }) | Some(JsObj::BoundFunc { .. }) => {
1364 "function () { [native code] }".into()
1365 }
1366 Some(JsObj::Class(c)) => format!("class {} {{ }}", c.name),
1367 Some(JsObj::Symbol { desc, .. }) => {
1368 match desc {
1371 Some(d) => format!("Symbol({d})"),
1372 None => "Symbol()".into(),
1373 }
1374 }
1375 _ => "[object Object]".into(),
1376 },
1377 _ => "[object Object]".into(),
1378 }
1379 }
1380
1381 pub fn console_format(&self, v: &Value) -> String {
1384 match v {
1385 Value::Str(_) => self.str_of(v),
1386 Value::Obj(_) if matches!(self.get(v), Some(JsObj::Str(_))) => self.str_of(v),
1387 _ => self.inspect(v),
1388 }
1389 }
1390
1391 pub fn inspect(&self, v: &Value) -> String {
1393 self.inspect_lvl(v, 0)
1394 }
1395
1396 fn inspect_lvl(&self, v: &Value, indent: usize) -> String {
1399 match v {
1400 Value::Undef => "undefined".into(),
1401 Value::Bool(b) => if *b { "true" } else { "false" }.into(),
1402 Value::Int(n) => n.to_string(),
1403 Value::Float(f) if *f == 0.0 && f.is_sign_negative() => "-0".into(),
1405 Value::Float(f) => fmt_number(*f),
1406 Value::Str(s) => quote_str(s),
1407 Value::Obj(_) => match self.get(v) {
1408 Some(JsObj::Str(s)) => quote_str(s),
1409 Some(JsObj::Null) => "null".into(),
1410 Some(JsObj::BigInt(b)) => format!("{b}n"),
1412 Some(JsObj::RegExp(r)) => format!("/{}/{}", r.source, r.flags),
1413 Some(JsObj::Array(items)) => {
1414 let prop_keys: Vec<String> = self
1418 .fn_prop_keys(v)
1419 .into_iter()
1420 .filter(|k| !k.starts_with("@@") && !k.starts_with('#'))
1421 .collect();
1422 if items.is_empty() && prop_keys.is_empty() {
1423 return "[]".into();
1424 }
1425 if indent > 2 * inspect_max_depth() {
1428 return "[Array]".into();
1429 }
1430 let mut inner: Vec<String> = items
1431 .iter()
1432 .map(|x| self.inspect_lvl(x, indent + 2))
1433 .collect();
1434 let has_props = !prop_keys.is_empty();
1435 for k in &prop_keys {
1436 let val = self.fn_prop(v, k).unwrap_or(Value::Undef);
1437 inner.push(format!(
1438 "{}: {}",
1439 fmt_key(k),
1440 self.inspect_lvl(&val, indent + 2)
1441 ));
1442 }
1443 self.render_array(&inner, items, indent, has_props)
1444 }
1445 Some(JsObj::Object(props)) => {
1446 let prefix = if self.has_null_proto(v) {
1451 "[Object: null prototype] ".to_string()
1452 } else {
1453 match self.ctor_name(v) {
1454 n if n.is_empty() || n == "Object" => String::new(),
1455 n => format!("{n} "),
1456 }
1457 };
1458 let shown: Vec<(&String, &Value)> = props
1460 .iter()
1461 .filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
1462 .collect();
1463 if shown.is_empty() {
1464 return format!("{prefix}{{}}");
1465 }
1466 if indent > 2 * inspect_max_depth() {
1469 return if prefix.is_empty() {
1470 "[Object]".into()
1471 } else if self.has_null_proto(v) {
1472 prefix.trim_end().to_string()
1474 } else {
1475 format!("[{}]", prefix.trim_end())
1476 };
1477 }
1478 let inner: Vec<String> = shown
1479 .iter()
1480 .map(|(k, val)| {
1481 format!("{}: {}", fmt_key(k), self.inspect_lvl(val, indent + 2))
1482 })
1483 .collect();
1484 self.render_object(&inner, &prefix, indent)
1485 }
1486 Some(JsObj::Symbol { desc, .. }) => match desc {
1487 Some(d) => format!("Symbol({d})"),
1488 None => "Symbol()".into(),
1489 },
1490 Some(JsObj::Class(c)) => {
1491 if c.parent.is_some() {
1492 let pname = c
1493 .parent
1494 .as_ref()
1495 .map(|p| self.callable_name(p))
1496 .unwrap_or_default();
1497 format!("[class {} extends {}]", c.name, pname)
1498 } else {
1499 format!("[class {}]", c.name)
1500 }
1501 }
1502 Some(JsObj::Map { entries, .. }) => {
1503 if entries.is_empty() {
1504 return "Map(0) {}".into();
1505 }
1506 let inner: Vec<String> = entries
1507 .values()
1508 .map(|(k, val)| format!("{} => {}", self.inspect(k), self.inspect(val)))
1509 .collect();
1510 format!("Map({}) {{ {} }}", entries.len(), inner.join(", "))
1511 }
1512 Some(JsObj::Set { entries, .. }) => {
1513 if entries.is_empty() {
1514 return "Set(0) {}".into();
1515 }
1516 let inner: Vec<String> = entries.values().map(|v| self.inspect(v)).collect();
1517 format!("Set({}) {{ {} }}", entries.len(), inner.join(", "))
1518 }
1519 Some(JsObj::Generator { .. }) => "Object [Generator] {}".into(),
1520 Some(JsObj::Promise { id }) => match self.promises.get(*id as usize) {
1521 Some(c) => match c.state {
1522 PromiseState::Pending => "Promise { <pending> }".into(),
1523 PromiseState::Fulfilled => {
1524 format!("Promise {{ {} }}", self.inspect(&c.value))
1525 }
1526 PromiseState::Rejected => {
1527 format!("Promise {{ <rejected> {} }}", self.inspect(&c.value))
1528 }
1529 },
1530 None => "Promise { <pending> }".into(),
1531 },
1532 Some(JsObj::Func(f)) => {
1533 let name = self
1534 .funcs
1535 .get(f.def_id)
1536 .map(|d| d.name.clone())
1537 .unwrap_or_default();
1538 if name.is_empty() {
1539 "[Function (anonymous)]".into()
1540 } else {
1541 format!("[Function: {name}]")
1542 }
1543 }
1544 Some(JsObj::Builtin(n)) => {
1545 let short = n.rsplit('.').next().unwrap_or(n);
1546 format!("[Function: {short}]")
1547 }
1548 Some(JsObj::BoundMethod { .. }) => "[Function (anonymous)]".into(),
1549 Some(JsObj::BoundFunc { target, .. }) => {
1550 let n = self.callable_name(target);
1551 if n.is_empty() {
1552 "[Function: bound ]".into()
1553 } else {
1554 format!("[Function: bound {n}]")
1555 }
1556 }
1557 _ => "undefined".into(),
1558 },
1559 _ => "undefined".into(),
1560 }
1561 }
1562
1563 fn render_array(
1569 &self,
1570 output: &[String],
1571 values: &[Value],
1572 indent: usize,
1573 has_props: bool,
1574 ) -> String {
1575 let entries = output.len();
1579 let (lines, grouped) = if entries > 6 && !has_props {
1580 group_array_elements(self, output, values, indent)
1581 } else {
1582 (output.to_vec(), false)
1583 };
1584 if !grouped {
1586 let start = output.len() + indent + 1 + 10;
1588 if is_below_break_length(output, start) {
1589 return format!("[ {} ]", output.join(", "));
1590 }
1591 }
1592 let pad = " ".repeat(indent);
1594 let sep = format!(",\n{pad} ");
1595 format!("[\n{pad} {}\n{pad}]", lines.join(&sep))
1596 }
1597
1598 fn render_object(&self, output: &[String], prefix: &str, indent: usize) -> String {
1605 let braces0 = prefix.chars().count() + 1;
1610 let start = output.len() + indent + braces0 + 10;
1611 if is_below_break_length(output, start) {
1612 return format!("{prefix}{{ {} }}", output.join(", "));
1613 }
1614 let pad = " ".repeat(indent);
1615 let sep = format!(",\n{pad} ");
1616 format!("{prefix}{{\n{pad} {}\n{pad}}}", output.join(&sep))
1617 }
1618
1619 pub fn callable_name(&self, v: &Value) -> String {
1621 if let Some(n) = self.fn_prop(v, "name") {
1623 return self.str_of(&n);
1624 }
1625 match self.get(v) {
1626 Some(JsObj::Func(f)) => self
1627 .funcs
1628 .get(f.def_id)
1629 .map(|d| d.name.clone())
1630 .unwrap_or_default(),
1631 Some(JsObj::Class(c)) => c.name.clone(),
1632 Some(JsObj::Builtin(n)) => n.rsplit('.').next().unwrap_or(n).to_string(),
1633 Some(JsObj::BoundFunc { target, .. }) => {
1634 format!("bound {}", self.callable_name(target))
1635 }
1636 _ => String::new(),
1637 }
1638 }
1639
1640 pub fn strict_eq(&self, a: &Value, b: &Value) -> bool {
1644 match (a, b) {
1645 (Value::Undef, Value::Undef) => true,
1646 (Value::Bool(x), Value::Bool(y)) => x == y,
1647 (Value::Str(x), Value::Str(y)) => x == y,
1648 _ => {
1649 let an = matches!(a, Value::Int(_) | Value::Float(_));
1651 let bn = matches!(b, Value::Int(_) | Value::Float(_));
1652 if an && bn {
1653 let x = self.to_number(a);
1654 let y = self.to_number(b);
1655 return x == y;
1656 }
1657 if let (Some(x), Some(y)) = (self.as_bigint(a), self.as_bigint(b)) {
1661 return x == y;
1662 }
1663 if let (Some(sa), Some(sb)) = (self.as_str(a), self.as_str(b)) {
1665 return sa == sb;
1666 }
1667 let na = self.is_null(a);
1668 let nb = self.is_null(b);
1669 if na || nb {
1670 return na && nb;
1671 }
1672 matches!((a, b), (Value::Obj(x), Value::Obj(y)) if x == y)
1674 }
1675 }
1676 }
1677
1678 pub fn is_nullish(&self, v: &Value) -> bool {
1680 matches!(v, Value::Undef) || self.is_null(v)
1681 }
1682
1683 fn js_type(&self, v: &Value) -> &'static str {
1687 match v {
1688 Value::Undef => "undefined",
1689 Value::Bool(_) => "boolean",
1690 Value::Int(_) | Value::Float(_) => "number",
1691 Value::Str(_) => "string",
1692 Value::Obj(_) => match self.get(v) {
1693 Some(JsObj::Str(_)) => "string",
1694 Some(JsObj::Null) => "null",
1695 Some(JsObj::BigInt(_)) => "bigint",
1696 _ => "object",
1697 },
1698 _ => "object",
1699 }
1700 }
1701
1702 pub fn loose_eq(&self, a: &Value, b: &Value) -> bool {
1707 if self.strict_eq(a, b) {
1709 return true;
1710 }
1711 let ta = self.js_type(a);
1712 let tb = self.js_type(b);
1713 if self.is_nullish(a) || self.is_nullish(b) {
1715 return self.is_nullish(a) && self.is_nullish(b);
1716 }
1717 if ta == "bigint" || tb == "bigint" {
1720 return self.bigint_loose_eq(a, b);
1721 }
1722 if ta == tb {
1723 return false;
1725 }
1726 if (ta == "number" && tb == "string") || (ta == "string" && tb == "number") {
1728 return self.to_number(a) == self.to_number(b);
1729 }
1730 if ta == "boolean" {
1732 return self.loose_eq(&Value::Float(self.to_number(a)), b);
1733 }
1734 if tb == "boolean" {
1735 return self.loose_eq(a, &Value::Float(self.to_number(b)));
1736 }
1737 if ta == "object" && (tb == "number" || tb == "string") {
1740 let pa = self.str_of(a);
1741 return if tb == "string" {
1742 pa == self.str_of(b)
1743 } else {
1744 str_to_number(&pa) == self.to_number(b)
1745 };
1746 }
1747 if tb == "object" && (ta == "number" || ta == "string") {
1748 let pb = self.str_of(b);
1749 return if ta == "string" {
1750 self.str_of(a) == pb
1751 } else {
1752 self.to_number(a) == str_to_number(&pb)
1753 };
1754 }
1755 false
1756 }
1757
1758 pub fn arith(&mut self, op: NumOp, a: &Value, b: &Value) -> Result<Value, String> {
1761 use NumOp::*;
1762 match op {
1763 Add => {
1764 let a_str = self.prefers_string(a);
1767 let b_str = self.prefers_string(b);
1768 if a_str || b_str {
1769 let s = format!("{}{}", self.str_of(a), self.str_of(b));
1772 Ok(self.new_str(s))
1773 } else if self.is_bigint_val(a) || self.is_bigint_val(b) {
1774 self.bigint_arith(op, a, b)
1775 } else {
1776 Ok(Value::Float(self.to_number(a) + self.to_number(b)))
1777 }
1778 }
1779 Sub | Mul | Div | Mod | Pow if self.is_bigint_val(a) || self.is_bigint_val(b) => {
1780 self.bigint_arith(op, a, b)
1781 }
1782 Sub => Ok(Value::Float(self.to_number(a) - self.to_number(b))),
1783 Mul => Ok(Value::Float(self.to_number(a) * self.to_number(b))),
1784 Div => Ok(Value::Float(self.to_number(a) / self.to_number(b))),
1785 Mod => Ok(Value::Float(js_mod(self.to_number(a), self.to_number(b)))),
1786 Pow => Ok(Value::Float(self.to_number(a).powf(self.to_number(b)))),
1787 Neg if self.is_bigint_val(a) => self.bigint_arith(op, a, b),
1788 Neg => Ok(Value::Float(-self.to_number(a))),
1789 Lt | Le | Gt | Ge => Ok(Value::Bool(self.relational(op, a, b))),
1790 Eq => Ok(Value::Bool(self.loose_eq(a, b))),
1791 Ne => Ok(Value::Bool(!self.loose_eq(a, b))),
1792 }
1793 }
1794
1795 fn prefers_string(&self, v: &Value) -> bool {
1801 match v {
1802 Value::Str(_) => true,
1803 Value::Obj(_) => !matches!(
1807 self.get(v),
1808 Some(JsObj::Null) | Some(JsObj::BigInt(_)) | None
1809 ),
1810 _ => false,
1811 }
1812 }
1813
1814 fn relational(&self, op: NumOp, a: &Value, b: &Value) -> bool {
1817 use std::cmp::Ordering;
1818 let ord = if let (Some(x), Some(y)) = (self.as_bigint(a), self.as_bigint(b)) {
1819 x.cmp(&y)
1821 } else if let (Some(x), Some(y)) = (self.as_str(a), self.as_str(b)) {
1822 x.cmp(&y)
1823 } else {
1824 let x = self.to_number(a);
1825 let y = self.to_number(b);
1826 match x.partial_cmp(&y) {
1827 Some(o) => o,
1828 None => return false, }
1830 };
1831 match op {
1832 NumOp::Lt => ord == Ordering::Less,
1833 NumOp::Le => ord != Ordering::Greater,
1834 NumOp::Gt => ord == Ordering::Greater,
1835 NumOp::Ge => ord != Ordering::Less,
1836 _ => false,
1837 }
1838 }
1839
1840 pub fn bitwise(&mut self, tag: i64, a: &Value, b: &Value) -> Result<Value, String> {
1844 if self.is_bigint_val(a) || self.is_bigint_val(b) {
1845 return self.bigint_bitwise(tag, a, b);
1846 }
1847 let x = to_int32(self.to_number(a));
1848 let y = to_int32(self.to_number(b));
1849 let r: i64 = match tag {
1850 binop::BITAND => (x & y) as i64,
1851 binop::BITOR => (x | y) as i64,
1852 binop::BITXOR => (x ^ y) as i64,
1853 binop::SHL => (x.wrapping_shl((y as u32) & 31)) as i64,
1854 binop::SHR => (x >> ((y as u32) & 31)) as i64,
1855 binop::USHR => (to_uint32(self.to_number(a)) >> ((y as u32) & 31)) as i64,
1856 _ => 0,
1857 };
1858 Ok(Value::Float(r as f64))
1859 }
1860
1861 pub fn is_bigint_val(&self, v: &Value) -> bool {
1864 matches!(self.get(v), Some(JsObj::BigInt(_)))
1865 }
1866 pub fn as_bigint(&self, v: &Value) -> Option<num_bigint::BigInt> {
1868 match self.get(v) {
1869 Some(JsObj::BigInt(b)) => Some(b.clone()),
1870 _ => None,
1871 }
1872 }
1873 pub fn new_bigint(&mut self, b: num_bigint::BigInt) -> Value {
1875 self.alloc(JsObj::BigInt(b))
1876 }
1877
1878 fn bigint_arith(&mut self, op: NumOp, a: &Value, b: &Value) -> Result<Value, String> {
1884 use num_traits::{Signed, Zero};
1885 use NumOp::*;
1886 if op == Neg {
1887 let x = self.as_bigint(a).expect("bigint_arith Neg on non-bigint");
1888 return Ok(self.new_bigint(-x));
1889 }
1890 let (x, y) = match (self.as_bigint(a), self.as_bigint(b)) {
1891 (Some(x), Some(y)) => (x, y),
1892 _ => {
1894 return Err(type_error(
1895 "Cannot mix BigInt and other types, use explicit conversions",
1896 ))
1897 }
1898 };
1899 let r = match op {
1900 Add => x + y,
1901 Sub => x - y,
1902 Mul => x * y,
1903 Div => {
1904 if y.is_zero() {
1905 return Err("RangeError: Division by zero".into());
1906 }
1907 x / y }
1909 Mod => {
1910 if y.is_zero() {
1911 return Err("RangeError: Division by zero".into());
1912 }
1913 x % y }
1915 Pow => {
1916 if y.is_negative() {
1917 return Err("RangeError: Exponent must be positive".into());
1918 }
1919 let exp = num_traits::ToPrimitive::to_u32(&y)
1920 .ok_or_else(|| "RangeError: Maximum BigInt size exceeded".to_string())?;
1921 num_traits::Pow::pow(x, exp)
1922 }
1923 _ => return Err(type_error("unsupported BigInt operation")),
1924 };
1925 Ok(self.new_bigint(r))
1926 }
1927
1928 fn bigint_bitwise(&mut self, tag: i64, a: &Value, b: &Value) -> Result<Value, String> {
1931 let (x, y) = match (self.as_bigint(a), self.as_bigint(b)) {
1932 (Some(x), Some(y)) => (x, y),
1933 _ => {
1934 return Err(type_error(
1935 "Cannot mix BigInt and other types, use explicit conversions",
1936 ))
1937 }
1938 };
1939 let r = match tag {
1940 binop::BITAND => x & y,
1941 binop::BITOR => x | y,
1942 binop::BITXOR => x ^ y,
1943 binop::SHL => {
1944 let n = num_traits::ToPrimitive::to_i64(&y).unwrap_or(0);
1945 if n >= 0 {
1946 x << (n as usize)
1947 } else {
1948 x >> ((-n) as usize)
1949 }
1950 }
1951 binop::SHR => {
1952 let n = num_traits::ToPrimitive::to_i64(&y).unwrap_or(0);
1953 if n >= 0 {
1954 x >> (n as usize)
1955 } else {
1956 x << ((-n) as usize)
1957 }
1958 }
1959 binop::USHR => {
1960 return Err(type_error(
1961 "BigInts have no unsigned right shift, use >> instead",
1962 ))
1963 }
1964 _ => return Err(type_error("unsupported BigInt operation")),
1965 };
1966 Ok(self.new_bigint(r))
1967 }
1968
1969 fn bigint_loose_eq(&self, a: &Value, b: &Value) -> bool {
1972 let (big, other) = match (self.as_bigint(a), self.as_bigint(b)) {
1974 (Some(x), _) => (x, b),
1975 (_, Some(y)) => (y, a),
1976 _ => return false,
1977 };
1978 match other {
1979 Value::Bool(bo) => big == num_bigint::BigInt::from(*bo as i64),
1980 Value::Int(n) => big == num_bigint::BigInt::from(*n),
1981 Value::Float(f) => {
1982 if !f.is_finite() || f.fract() != 0.0 {
1984 return false;
1985 }
1986 bigint_to_f64(&big) == *f
1987 }
1988 Value::Str(s) => match parse_bigint_str(s) {
1989 Some(bs) => big == bs,
1990 None => false,
1991 },
1992 Value::Obj(_) => match self.get(other) {
1993 Some(JsObj::Str(s)) => parse_bigint_str(s).map(|bs| big == bs).unwrap_or(false),
1995 _ => {
1996 let s = self.str_of(other);
1998 parse_bigint_str(&s).map(|bs| big == bs).unwrap_or(false)
1999 }
2000 },
2001 _ => false,
2002 }
2003 }
2004}
2005
2006pub fn parse_bigint_str(s: &str) -> Option<num_bigint::BigInt> {
2009 let t = s.trim();
2010 if t.is_empty() {
2011 return Some(num_bigint::BigInt::from(0));
2012 }
2013 let (radix, digits) = if let Some(h) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) {
2014 (16, h)
2015 } else if let Some(o) = t.strip_prefix("0o").or_else(|| t.strip_prefix("0O")) {
2016 (8, o)
2017 } else if let Some(bb) = t.strip_prefix("0b").or_else(|| t.strip_prefix("0B")) {
2018 (2, bb)
2019 } else {
2020 (10, t)
2021 };
2022 num_bigint::BigInt::parse_bytes(digits.as_bytes(), radix)
2023}
2024
2025fn bigint_to_f64(b: &num_bigint::BigInt) -> f64 {
2028 num_traits::ToPrimitive::to_f64(b).unwrap_or_else(|| {
2029 if num_traits::Signed::is_negative(b) {
2030 f64::NEG_INFINITY
2031 } else {
2032 f64::INFINITY
2033 }
2034 })
2035}
2036
2037fn js_mod(a: f64, b: f64) -> f64 {
2039 a % b
2040}
2041
2042thread_local! {
2043 static INSPECT_MAX_DEPTH: std::cell::Cell<usize> = const { std::cell::Cell::new(2) };
2047}
2048
2049pub fn set_inspect_max_depth(d: usize) {
2051 INSPECT_MAX_DEPTH.with(|c| c.set(d));
2052}
2053fn inspect_max_depth() -> usize {
2054 INSPECT_MAX_DEPTH.with(|c| c.get())
2055}
2056
2057fn to_int32(f: f64) -> i32 {
2058 if !f.is_finite() {
2059 return 0;
2060 }
2061 let n = f.trunc();
2062 (n as i64 as u32) as i32
2063}
2064fn to_uint32(f: f64) -> u32 {
2065 if !f.is_finite() {
2066 return 0;
2067 }
2068 f.trunc() as i64 as u32
2069}
2070
2071fn str_to_number(s: &str) -> f64 {
2073 let t = s.trim();
2074 if t.is_empty() {
2075 return 0.0;
2076 }
2077 if let Some(hex) = t.strip_prefix("0x").or_else(|| t.strip_prefix("0X")) {
2078 return i64::from_str_radix(hex, 16)
2079 .map(|n| n as f64)
2080 .unwrap_or(f64::NAN);
2081 }
2082 if let Some(oct) = t.strip_prefix("0o").or_else(|| t.strip_prefix("0O")) {
2083 return i64::from_str_radix(oct, 8)
2084 .map(|n| n as f64)
2085 .unwrap_or(f64::NAN);
2086 }
2087 if let Some(bin) = t.strip_prefix("0b").or_else(|| t.strip_prefix("0B")) {
2088 return i64::from_str_radix(bin, 2)
2089 .map(|n| n as f64)
2090 .unwrap_or(f64::NAN);
2091 }
2092 match t {
2093 "Infinity" | "+Infinity" => f64::INFINITY,
2094 "-Infinity" => f64::NEG_INFINITY,
2095 _ => t.parse::<f64>().unwrap_or(f64::NAN),
2096 }
2097}
2098
2099const BREAK_LENGTH: usize = 80;
2101const COMPACT: usize = 3;
2103
2104fn is_below_break_length(output: &[String], start: usize) -> bool {
2108 let mut total = output.len() + start;
2109 if total + output.len() > BREAK_LENGTH {
2110 return false;
2111 }
2112 for o in output {
2113 if o.contains('\n') {
2114 return false;
2115 }
2116 total += o.chars().count();
2117 if total > BREAK_LENGTH {
2118 return false;
2119 }
2120 }
2121 true
2122}
2123
2124fn group_array_elements(
2129 host: &JsHost,
2130 output: &[String],
2131 values: &[Value],
2132 indentation_lvl: usize,
2133) -> (Vec<String>, bool) {
2134 let separator_space = 2usize; let output_length = output.len();
2136 let data_len: Vec<usize> = output.iter().map(|o| o.chars().count()).collect();
2137 let mut total_length = 0usize;
2138 let mut max_length = 0usize;
2139 for &len in &data_len {
2140 total_length += len + separator_space;
2141 if len > max_length {
2142 max_length = len;
2143 }
2144 }
2145 let actual_max = max_length + separator_space;
2146 if !(actual_max * 3 + indentation_lvl < BREAK_LENGTH
2148 && (total_length as f64 / actual_max as f64 > 5.0 || max_length <= 6))
2149 {
2150 return (output.to_vec(), false);
2151 }
2152 let approx_char_heights = 2.5f64;
2153 let average_bias = (actual_max as f64 - total_length as f64 / output_length as f64).sqrt();
2154 let biased_max = (actual_max as f64 - 3.0 - average_bias).max(1.0);
2155 let columns = [
2157 ((approx_char_heights * biased_max * output_length as f64).sqrt() / biased_max).round()
2158 as i64,
2159 ((BREAK_LENGTH - indentation_lvl) as f64 / actual_max as f64).floor() as i64,
2160 (COMPACT * 4) as i64,
2161 15,
2162 ]
2163 .into_iter()
2164 .min()
2165 .unwrap();
2166 if columns <= 1 {
2167 return (output.to_vec(), false);
2168 }
2169 let columns = columns as usize;
2170 let mut max_line_length = vec![0usize; columns];
2172 for (i, slot) in max_line_length.iter_mut().enumerate() {
2173 let mut line_length = 0;
2174 let mut j = i;
2175 while j < output_length {
2176 if data_len[j] > line_length {
2177 line_length = data_len[j];
2178 }
2179 j += columns;
2180 }
2181 *slot = line_length + separator_space;
2182 }
2183 let pad_start = values.iter().all(|v| {
2185 matches!(v, Value::Int(_) | Value::Float(_))
2186 || matches!(host.get(v), Some(JsObj::BigInt(_)))
2187 });
2188 let mut tmp = Vec::new();
2189 let mut i = 0;
2190 while i < output_length {
2191 let max = (i + columns).min(output_length);
2192 let mut str_line = String::new();
2193 let mut j = i;
2194 while j < max.saturating_sub(1) {
2195 let col = j - i;
2197 let cell = format!("{}, ", output[j]);
2198 let target = max_line_length[col];
2199 str_line.push_str(&pad_to(&cell, target, pad_start));
2200 j += 1;
2201 }
2202 if pad_start {
2204 let col = j - i;
2205 let target = max_line_length[col] - separator_space;
2206 str_line.push_str(&pad_to(&output[j], target, true));
2207 } else {
2208 str_line.push_str(&output[j]);
2209 }
2210 tmp.push(str_line);
2211 i += columns;
2212 }
2213 (tmp, true)
2214}
2215
2216fn pad_to(s: &str, width: usize, pad_start: bool) -> String {
2219 let len = s.chars().count();
2220 if len >= width {
2221 return s.to_string();
2222 }
2223 let fill = " ".repeat(width - len);
2224 if pad_start {
2225 format!("{fill}{s}")
2226 } else {
2227 format!("{s}{fill}")
2228 }
2229}
2230
2231fn quote_str(s: &str) -> String {
2233 let mut out = String::from("'");
2234 for c in s.chars() {
2235 match c {
2236 '\'' => out.push_str("\\'"),
2237 '\\' => out.push_str("\\\\"),
2238 '\n' => out.push_str("\\n"),
2239 '\t' => out.push_str("\\t"),
2240 '\r' => out.push_str("\\r"),
2241 _ => out.push(c),
2242 }
2243 }
2244 out.push('\'');
2245 out
2246}
2247
2248fn fmt_key(k: &str) -> String {
2250 let ok = !k.is_empty()
2251 && k.chars()
2252 .next()
2253 .map(|c| c.is_ascii_alphabetic() || c == '_' || c == '$')
2254 .unwrap_or(false)
2255 && k.chars()
2256 .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$');
2257 if ok {
2258 k.to_string()
2259 } else {
2260 quote_str(k)
2261 }
2262}
2263
2264impl JsHost {
2267 pub fn iter_vec(&mut self, v: &Value) -> Result<Vec<Value>, String> {
2271 match self.get(v) {
2272 Some(JsObj::Array(items)) => Ok(items.clone()),
2273 Some(JsObj::Str(s)) => {
2274 let chars: Vec<String> = s.chars().map(|c| c.to_string()).collect();
2275 Ok(chars.into_iter().map(|c| self.new_str(c)).collect())
2276 }
2277 Some(JsObj::Iter { items, idx }) => Ok(items[*idx..].to_vec()),
2278 Some(JsObj::Set { entries, .. }) => Ok(entries.values().cloned().collect()),
2279 Some(JsObj::Map { entries, .. }) => {
2280 let pairs: Vec<(Value, Value)> = entries.values().cloned().collect();
2282 Ok(pairs
2283 .into_iter()
2284 .map(|(k, v)| self.new_array(vec![k, v]))
2285 .collect())
2286 }
2287 _ => Err(type_error(&format!("{} is not iterable", self.type_of(v)))),
2288 }
2289 }
2290
2291 pub fn enum_keys(&mut self, v: &Value) -> Vec<Value> {
2294 let keys: Vec<String> = match self.get(v) {
2295 Some(JsObj::Object(props)) => props
2296 .keys()
2297 .filter(|k| !k.starts_with("@@") && !k.starts_with('#'))
2298 .cloned()
2299 .collect(),
2300 Some(JsObj::Array(items)) => (0..items.len()).map(|i| i.to_string()).collect(),
2301 _ => Vec::new(),
2302 };
2303 keys.into_iter().map(|k| self.new_str(k)).collect()
2304 }
2305}
2306
2307fn marshal_ffi_arg(v: &Value) -> Value {
2316 match v {
2317 Value::Obj(_) => match with_host(|h| h.as_str(v)) {
2318 Some(s) => Value::str(s),
2319 None => v.clone(),
2320 },
2321 _ => v.clone(),
2322 }
2323}
2324
2325pub fn call_named(name: &str, args: Vec<Value>) -> Result<Value, String> {
2327 if name == "__rust_compile" {
2331 let b64 = args
2332 .first()
2333 .map(|v| with_host(|h| h.str_of(v)))
2334 .unwrap_or_default();
2335 return fusevm::ffi::compile_and_register(&b64).map(|_| Value::Undef);
2336 }
2337 if let Some(v) = with_host(|h| h.read_name(name)) {
2338 return invoke(&v, args, None);
2339 }
2340 if crate::builtins::is_known_builtin(name) {
2341 return crate::builtins::call_builtin_function(name, args);
2342 }
2343 if fusevm::ffi::is_registered(name) {
2347 let margs: Vec<Value> = args.iter().map(marshal_ffi_arg).collect();
2348 if let Some(r) = fusevm::ffi::try_call(name, &margs) {
2349 return r;
2350 }
2351 }
2352 Err(ref_error(name))
2353}
2354
2355pub fn call_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
2357 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(recv).cloned()) {
2360 let qualified = format!("{ns}.{name}");
2361 if crate::builtins::is_known_builtin(&qualified) {
2362 return crate::builtins::call_builtin_function(&qualified, args);
2363 }
2364 }
2365 if matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Object(_))) {
2371 if let Some(tag) = crate::stdlib::native_tag(recv) {
2382 if let Some(f) = with_host(|h| lookup_chain(h, recv, name)) {
2383 if with_host(|h| is_callable(h, &f)) {
2384 return invoke(&f, args, Some(recv.clone()));
2385 }
2386 }
2387 return crate::stdlib::instance_call(&tag, recv, name, args);
2388 }
2389 if let Some((Some(getter), _)) = with_host(|h| lookup_accessor(h, recv, name)) {
2390 let f = invoke(&getter, Vec::new(), Some(recv.clone()))?;
2391 if with_host(|h| is_callable(h, &f)) {
2392 return invoke(&f, args, Some(recv.clone()));
2393 }
2394 }
2395 if let Some(f) = with_host(|h| lookup_chain(h, recv, name)) {
2396 if with_host(|h| is_callable(h, &f)) {
2397 return invoke(&f, args, Some(recv.clone()));
2398 }
2399 return Err(type_error(&format!("{name} is not a function")));
2400 }
2401 if crate::builtins::is_object_builtin_method(name) {
2402 return crate::builtins::object_builtin_method(recv, name, args);
2403 }
2404 return Err(type_error(&format!("{name} is not a function")));
2405 }
2406 if matches!(
2409 with_host(|h| h.get(recv).cloned()),
2410 Some(JsObj::Func(_))
2411 | Some(JsObj::Class(_))
2412 | Some(JsObj::BoundFunc { .. })
2413 | Some(JsObj::BoundMethod { .. })
2414 | Some(JsObj::Builtin(_))
2415 ) {
2416 if let Some(r) = crate::builtins::function_builtin_method(recv, name, &args)? {
2417 return Ok(r);
2418 }
2419 let stat = if matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Class(_))) {
2421 with_host(|h| h.class_static(recv, name))
2422 } else {
2423 with_host(|h| h.fn_prop(recv, name))
2424 };
2425 if let Some(f) = stat {
2426 if with_host(|h| is_callable(h, &f)) {
2427 return invoke(&f, args, Some(recv.clone()));
2428 }
2429 }
2430 if let Some(f) = with_host(|h| lookup_chain(h, recv, name)) {
2434 if with_host(|h| is_callable(h, &f)) {
2435 return invoke(&f, args, Some(recv.clone()));
2436 }
2437 }
2438 if matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Builtin(_)))
2442 && crate::builtins::is_object_builtin_method(name)
2443 {
2444 return crate::builtins::object_builtin_method(recv, name, args);
2445 }
2446 }
2447 crate::builtins::call_type_method(recv, name, args)
2449}
2450
2451pub fn invoke(callable: &Value, args: Vec<Value>, this: Option<Value>) -> Result<Value, String> {
2453 let obj = with_host(|h| h.get(callable).cloned());
2454 match obj {
2455 Some(JsObj::Builtin(name)) if name.starts_with("@proto:") => {
2458 let recv = this.unwrap_or(Value::Undef);
2459 crate::builtins::proto_method(&recv, &name["@proto:".len()..], args)
2460 }
2461 Some(JsObj::Builtin(name)) => crate::builtins::call_builtin_function(&name, args),
2462 Some(JsObj::Func(fv)) => run_user_func(&fv, args, this),
2463 Some(JsObj::BoundMethod { recv, name }) => call_method(&recv, &name, args),
2464 Some(JsObj::BoundFunc {
2465 target,
2466 this: bthis,
2467 args: pre,
2468 }) => {
2469 let mut all = pre;
2470 all.extend(args);
2471 invoke(&target, all, Some(bthis))
2472 }
2473 Some(JsObj::Class(c)) => Err(type_error(&format!(
2474 "Class constructor {} cannot be invoked without 'new'",
2475 c.name
2476 ))),
2477 _ => Err(type_error(&format!(
2478 "{} is not a function",
2479 with_host(|h| h.str_of(callable))
2480 ))),
2481 }
2482}
2483
2484pub fn run_user_func(fv: &FuncVal, args: Vec<Value>, this: Option<Value>) -> Result<Value, String> {
2486 run_user_func_nt(fv, args, this, None)
2487}
2488
2489pub fn run_user_func_nt(
2491 fv: &FuncVal,
2492 args: Vec<Value>,
2493 this: Option<Value>,
2494 new_target: Option<Value>,
2495) -> Result<Value, String> {
2496 let def = with_host(|h| h.funcs[fv.def_id].clone());
2497 let env = new_env(fv.env.clone());
2498 bind_params(&env, &def, args);
2501 let this_val = if fv.is_arrow { fv.this.clone() } else { this };
2503 if def.is_generator {
2506 return Ok(make_generator(
2507 def.chunk.clone(),
2508 env,
2509 this_val,
2510 fv.home_class.clone(),
2511 ));
2512 }
2513 if def.is_async {
2516 let gen = make_generator(def.chunk.clone(), env, this_val, fv.home_class.clone());
2517 return Ok(run_async(gen));
2518 }
2519 let home = fv
2520 .home_class
2521 .as_ref()
2522 .and_then(|n| with_host(|h| h.class_registry.get(n).cloned()));
2523 with_host(|h| {
2524 h.frames.push(Frame {
2525 env,
2526 this_obj: this_val,
2527 new_target,
2528 home_class: home,
2529 line: 0,
2530 owner: Some(def.name.clone()),
2531 })
2532 });
2533 let r = run_chunk_on(def.chunk.clone());
2534 let sig = with_host(|h| {
2535 h.frames.pop();
2536 h.signal.take()
2537 });
2538 match r {
2539 Err(e) => Err(e),
2540 Ok(_) => Ok(match sig {
2541 Some(Signal::Return(v)) => v,
2542 _ => Value::Undef,
2543 }),
2544 }
2545}
2546
2547fn bind_params(env: &Env, def: &FuncDef, args: Vec<Value>) {
2550 let mut vars: IndexMap<String, Value> = IndexMap::new();
2551 let mut i = 0;
2552 for slot in &def.params {
2553 if slot.rest {
2554 let rest: Vec<Value> = args.get(i..).map(|s| s.to_vec()).unwrap_or_default();
2555 let arr = with_host(|h| h.new_array(rest));
2556 vars.insert(slot.name.clone(), arr);
2557 } else {
2558 let v = args.get(i).cloned().unwrap_or(Value::Undef);
2559 vars.insert(slot.name.clone(), v);
2560 i += 1;
2561 }
2562 }
2563 let args_arr = with_host(|h| h.new_array(args));
2565 vars.entry("arguments".to_string()).or_insert(args_arr);
2566 env.borrow_mut().vars = vars;
2567}
2568
2569pub fn construct(ctor: &Value, args: Vec<Value>) -> Result<Value, String> {
2573 construct_nt(ctor, args, ctor.clone())
2574}
2575
2576pub fn construct_nt(ctor: &Value, args: Vec<Value>, new_target: Value) -> Result<Value, String> {
2579 let obj = with_host(|h| h.get(ctor).cloned());
2580 match obj {
2581 Some(JsObj::Class(_)) => construct_class(ctor, args, new_target),
2582 Some(JsObj::Func(fv)) => {
2583 let inst = with_host(|h| {
2586 let o = h.new_object(IndexMap::new());
2587 let proto = h.fn_prop(ctor, "prototype").unwrap_or_else(|| {
2588 let p = h.new_object(IndexMap::new());
2589 if let Some(JsObj::Object(pp)) = h.get_mut(&p) {
2590 pp.insert("constructor".to_string(), ctor.clone());
2591 }
2592 h.set_fn_prop(ctor, "prototype", p.clone());
2593 p
2594 });
2595 h.set_proto(&o, proto);
2596 o
2597 });
2598 let r = run_user_func_nt(&fv, args, Some(inst.clone()), Some(new_target))?;
2599 if returns_object(&r) {
2600 Ok(r)
2601 } else {
2602 Ok(inst)
2603 }
2604 }
2605 Some(JsObj::Builtin(name)) => crate::builtins::construct_builtin(&name, args),
2606 Some(JsObj::BoundFunc {
2607 target, args: pre, ..
2608 }) => {
2609 let mut all = pre;
2610 all.extend(args);
2611 construct_nt(&target, all, new_target)
2612 }
2613 _ => Err(type_error(&format!(
2614 "{} is not a constructor",
2615 with_host(|h| h.str_of(ctor))
2616 ))),
2617 }
2618}
2619
2620fn returns_object(r: &Value) -> bool {
2625 matches!(
2626 with_host(|h| h.get(r).cloned()),
2627 Some(JsObj::Object(_))
2628 | Some(JsObj::Array(_))
2629 | Some(JsObj::Map { .. })
2630 | Some(JsObj::Set { .. })
2631 | Some(JsObj::Func(_))
2632 | Some(JsObj::Class(_))
2633 | Some(JsObj::BoundFunc { .. })
2634 | Some(JsObj::BoundMethod { .. })
2635 | Some(JsObj::RegExp(_))
2636 )
2637}
2638
2639fn construct_class(
2642 class_val: &Value,
2643 args: Vec<Value>,
2644 new_target: Value,
2645) -> Result<Value, String> {
2646 let cv = match with_host(|h| h.get(class_val).cloned()) {
2647 Some(JsObj::Class(c)) => c,
2648 _ => return Err(type_error("not a class")),
2649 };
2650 let leaf_proto = match with_host(|h| h.get(&new_target).cloned()) {
2654 Some(JsObj::Class(c)) => c.proto.clone(),
2655 _ => cv.proto.clone(),
2656 };
2657 let inst = with_host(|h| {
2658 let o = h.new_object(IndexMap::new());
2659 h.set_proto(&o, leaf_proto.clone());
2660 o
2661 });
2662 match run_class_ctor(&cv, &inst, args, &new_target)? {
2664 Some(obj) if returns_object(&obj) => Ok(obj),
2665 _ => Ok(inst),
2666 }
2667}
2668
2669fn run_class_ctor(
2674 cv: &ClassVal,
2675 inst: &Value,
2676 args: Vec<Value>,
2677 new_target: &Value,
2678) -> Result<Option<Value>, String> {
2679 if cv.parent.is_none() {
2682 init_fields(cv, inst)?;
2683 }
2684 match &cv.ctor {
2685 Some(ctor_fn) => {
2686 let fv = match with_host(|h| h.get(ctor_fn).cloned()) {
2687 Some(JsObj::Func(f)) => f,
2688 _ => return Err(type_error("class constructor is not a function")),
2689 };
2690 let r = run_user_func_nt(&fv, args, Some(inst.clone()), Some(new_target.clone()))?;
2691 return Ok(Some(r));
2692 }
2693 None => {
2694 if let Some(parent) = &cv.parent {
2697 super_construct(parent, args, inst, new_target)?;
2698 init_fields(cv, inst)?;
2699 }
2700 }
2701 }
2702 Ok(None)
2703}
2704
2705fn init_fields(cv: &ClassVal, inst: &Value) -> Result<(), String> {
2707 for (name, thunk) in &cv.fields {
2708 let val = invoke(thunk, Vec::new(), Some(inst.clone()))?;
2711 with_host(|h| {
2712 if let Some(JsObj::Object(props)) = h.get_mut(inst) {
2713 let is_new = !props.contains_key(name);
2714 props.insert(name.clone(), val);
2715 if is_new && array_index(name).is_some() {
2716 canonicalize_own_keys(props);
2717 }
2718 }
2719 });
2720 }
2721 Ok(())
2722}
2723
2724pub fn super_construct(
2727 parent: &Value,
2728 args: Vec<Value>,
2729 inst: &Value,
2730 new_target: &Value,
2731) -> Result<(), String> {
2732 match with_host(|h| h.get(parent).cloned()) {
2733 Some(JsObj::Class(pcv)) => run_class_ctor(&pcv, inst, args, new_target).map(|_| ()),
2734 Some(JsObj::Func(fv)) => {
2735 run_user_func_nt(&fv, args, Some(inst.clone()), Some(new_target.clone()))?;
2736 Ok(())
2737 }
2738 Some(JsObj::Builtin(name)) => {
2739 let built = crate::builtins::construct_builtin(&name, args)?;
2743 let entries: Vec<(String, Value)> = with_host(|h| match h.get(&built) {
2744 Some(JsObj::Object(p)) => p.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
2745 _ => Vec::new(),
2746 });
2747 with_host(|h| {
2748 if let Some(JsObj::Object(props)) = h.get_mut(inst) {
2749 for (k, v) in entries {
2750 props.insert(k, v);
2751 }
2752 canonicalize_own_keys(props);
2753 }
2754 });
2755 Ok(())
2756 }
2757 _ => Err(type_error("super is not a constructor")),
2758 }
2759}
2760
2761pub fn build_class(name: &str, parent: Value, ctor: Value) -> Value {
2768 with_host(|h| {
2769 let parent_opt = if matches!(parent, Value::Undef) {
2770 None
2771 } else {
2772 Some(parent.clone())
2773 };
2774 let parent_proto = match &parent_opt {
2778 Some(p) => match h.get(p).cloned() {
2779 Some(JsObj::Class(pc)) => pc.proto.clone(),
2780 Some(JsObj::Builtin(bn)) => {
2781 h.ensure_error_protos();
2782 error_proto_of(h, &bn)
2783 .or_else(|| h.fn_prop(p, "prototype"))
2784 .unwrap_or_else(|| h.object_proto())
2785 }
2786 _ => h
2787 .fn_prop(p, "prototype")
2788 .unwrap_or_else(|| h.object_proto()),
2789 },
2790 None => h.object_proto(),
2791 };
2792 let proto = h.new_object(IndexMap::new());
2793 h.set_proto(&proto, parent_proto);
2794 let ctor_opt = if matches!(ctor, Value::Undef) {
2795 None
2796 } else {
2797 Some(ctor.clone())
2798 };
2799 if let Some(cf) = &ctor_opt {
2802 if let Some(JsObj::Func(f)) = h.get_mut(cf) {
2803 f.home_class = Some(name.to_string());
2804 }
2805 }
2806 let cval = ClassVal {
2807 name: name.to_string(),
2808 ctor: ctor_opt,
2809 parent: parent_opt,
2810 proto: proto.clone(),
2811 statics: IndexMap::new(),
2812 fields: Vec::new(),
2813 };
2814 let class_val = h.alloc(JsObj::Class(cval));
2815 h.class_registry.insert(name.to_string(), class_val.clone());
2816 h.tag_proto_class(&proto, class_val.clone());
2819 h.set_fn_prop(&class_val, "prototype", proto.clone());
2820 if let Some(JsObj::Object(p)) = h.get_mut(&proto) {
2822 p.insert("constructor".to_string(), class_val.clone());
2823 }
2824 class_val
2825 })
2826}
2827
2828pub fn define_member(class_val: &Value, name: &str, kind: i64, is_static: bool, func: Value) {
2831 with_host(|h| {
2832 let cname = match h.get(class_val) {
2833 Some(JsObj::Class(c)) => c.name.clone(),
2834 _ => String::new(),
2835 };
2836 if let Some(JsObj::Func(f)) = h.get_mut(&func) {
2838 f.home_class = Some(cname);
2839 }
2840 let target = if is_static {
2843 class_val.clone()
2844 } else {
2845 match h.get(class_val) {
2846 Some(JsObj::Class(c)) => c.proto.clone(),
2847 _ => return,
2848 }
2849 };
2850 match kind {
2851 member::GET => h.set_accessor(&target, name, Some(func), None),
2852 member::SET => h.set_accessor(&target, name, None, Some(func)),
2853 _ => {
2854 if is_static {
2855 if let Some(JsObj::Class(c)) = h.get_mut(class_val) {
2856 c.statics.insert(name.to_string(), func.clone());
2857 }
2858 h.set_fn_prop(class_val, name, func);
2859 } else if let Some(JsObj::Object(p)) = h.get_mut(&target) {
2860 p.insert(name.to_string(), func);
2861 }
2862 }
2863 }
2864 });
2865}
2866
2867pub fn define_field(class_val: &Value, name: &str, thunk: Value) {
2869 with_host(|h| {
2870 if let Some(JsObj::Class(c)) = h.get_mut(class_val) {
2871 c.fields.push((name.to_string(), thunk));
2872 }
2873 });
2874}
2875
2876fn ctor_prototype(h: &JsHost, ctor: &Value) -> Option<Value> {
2879 match h.get(ctor) {
2880 Some(JsObj::Class(c)) => Some(c.proto.clone()),
2881 Some(JsObj::Func(_)) => h.fn_prop(ctor, "prototype"),
2882 Some(JsObj::Builtin(name)) => h.error_protos.get(name).cloned(),
2883 Some(JsObj::BoundFunc { target, .. }) => ctor_prototype(h, &target.clone()),
2884 _ => None,
2885 }
2886}
2887
2888pub fn instance_of(obj: &Value, ctor: &Value) -> Result<bool, String> {
2891 if !matches!(obj, Value::Obj(_)) {
2893 return Ok(false);
2894 }
2895 let ctor_callable = with_host(|h| {
2896 matches!(
2897 h.get(ctor),
2898 Some(JsObj::Func(_))
2899 | Some(JsObj::Class(_))
2900 | Some(JsObj::Builtin(_))
2901 | Some(JsObj::BoundFunc { .. })
2902 )
2903 });
2904 if !ctor_callable {
2905 return Err(type_error(
2906 "Right-hand side of 'instanceof' is not callable",
2907 ));
2908 }
2909 if let Some(JsObj::Builtin(name)) = with_host(|h| h.get(ctor).cloned()) {
2912 let kind = with_host(|h| h.get(obj).cloned());
2913 match name.as_str() {
2914 "Array" => return Ok(matches!(kind, Some(JsObj::Array(_)))),
2915 "Function" => return Ok(with_host(|h| is_callable(h, obj))),
2916 "Map" => return Ok(matches!(kind, Some(JsObj::Map { weak: false, .. }))),
2920 "WeakMap" => return Ok(matches!(kind, Some(JsObj::Map { weak: true, .. }))),
2921 "Set" => return Ok(matches!(kind, Some(JsObj::Set { weak: false, .. }))),
2922 "WeakSet" => return Ok(matches!(kind, Some(JsObj::Set { weak: true, .. }))),
2923 "Promise" => return Ok(matches!(kind, Some(JsObj::Promise { .. }))),
2924 "Object" => {
2925 let is_obj = matches!(
2928 kind,
2929 Some(JsObj::Object(_))
2930 | Some(JsObj::Array(_))
2931 | Some(JsObj::Func(_))
2932 | Some(JsObj::Class(_))
2933 | Some(JsObj::Map { .. })
2934 | Some(JsObj::Set { .. })
2935 | Some(JsObj::Promise { .. })
2936 | Some(JsObj::Generator { .. })
2937 );
2938 if is_obj {
2939 if with_host(|h| h.has_null_proto(obj)) {
2942 return Ok(false);
2943 }
2944 return Ok(true);
2945 }
2946 return Ok(false);
2947 }
2948 other => {
2952 if crate::stdlib::native_tag(obj).as_deref() == Some(other) {
2953 return Ok(true);
2954 }
2955 }
2956 }
2957 }
2958 with_host(|h| h.ensure_error_protos());
2959 let target = match with_host(|h| ctor_prototype(h, ctor)) {
2960 Some(p) => p,
2961 None => return Ok(false),
2962 };
2963 let mut cur = with_host(|h| h.proto_of(obj));
2964 while let Some(p) = cur {
2965 if with_host(|h| h.strict_eq(&p, &target)) {
2966 return Ok(true);
2967 }
2968 cur = with_host(|h| h.proto_of(&p));
2969 }
2970 Ok(false)
2971}
2972
2973impl JsHost {
2976 fn install_gen_ctx(&mut self, mut c: GenContext) -> GenContext {
2979 std::mem::swap(&mut self.frames, &mut c.frames);
2980 std::mem::swap(&mut self.error, &mut c.error);
2981 std::mem::swap(&mut self.exc, &mut c.exc);
2982 std::mem::swap(&mut self.signal, &mut c.signal);
2983 c
2984 }
2985 pub fn is_generator_val(&self, v: &Value) -> bool {
2986 matches!(self.get(v), Some(JsObj::Generator { .. }))
2987 }
2988 pub fn gen_done(&self, id: u32) -> bool {
2989 self.generators
2990 .get(id as usize)
2991 .map(|g| g.done)
2992 .unwrap_or(true)
2993 }
2994 fn gen_started(&self, id: u32) -> bool {
2995 self.generators
2996 .get(id as usize)
2997 .map(|g| g.started)
2998 .unwrap_or(false)
2999 }
3000}
3001
3002fn make_generator(
3005 chunk: Chunk,
3006 env: Env,
3007 this_val: Option<Value>,
3008 home_class: Option<String>,
3009) -> Value {
3010 let home = home_class
3011 .as_ref()
3012 .and_then(|n| with_host(|h| h.class_registry.get(n).cloned()));
3013 let frame = Frame {
3014 env,
3015 this_obj: this_val,
3016 new_target: None,
3017 home_class: home,
3018 line: 0,
3019 owner: None,
3020 };
3021 let id = with_host(|h| {
3022 let id = h.generators.len() as u32;
3023 h.generators.push(GenCell {
3024 coro: None,
3025 yielder: std::ptr::null(),
3026 ctx: GenContext {
3027 frames: vec![frame],
3028 ..GenContext::default()
3029 },
3030 done: false,
3031 started: false,
3032 inject: None,
3033 });
3034 id
3035 });
3036 let coro = corosensei::Coroutine::new(
3037 move |yielder: &corosensei::Yielder<Value, Value>, _first: Value| {
3038 with_host(|h| h.generators[id as usize].yielder = yielder as *const _ as *const ());
3041 let r = run_chunk_on(chunk);
3042 let ret = with_host(|h| match h.signal.take() {
3045 Some(Signal::Return(v)) => v,
3046 _ => Value::Undef,
3047 });
3048 r.map(|_| ret)
3049 },
3050 );
3051 with_host(|h| h.generators[id as usize].coro = Some(coro));
3052 with_host(|h| h.alloc(JsObj::Generator { id }))
3053}
3054
3055pub fn gen_yield(v: Value) -> Result<Value, String> {
3058 let id = match CUR_GEN.with(|c| c.get()) {
3059 Some(id) => id,
3060 None => return Err(type_error("yield outside a generator")),
3061 };
3062 let yp = with_host(|h| h.generators[id as usize].yielder);
3063 let yielder = unsafe { &*(yp as *const corosensei::Yielder<Value, Value>) };
3066 let sent = yielder.suspend(v);
3067 if let Some(inj) = with_host(|h| h.generators[id as usize].inject.take()) {
3071 match inj {
3072 GenInject::Return(rv) => {
3073 with_host(|h| h.signal = Some(Signal::Return(rv)));
3074 return Ok(Value::Undef);
3075 }
3076 GenInject::Throw(ev) => {
3077 let msg = with_host(|h| crate::builtins::error_string(h, &ev));
3078 with_host(|h| h.exc = Some(ev));
3079 return Err(msg);
3080 }
3081 }
3082 }
3083 Ok(sent)
3084}
3085
3086pub fn gen_return(gen: &Value, v: Value) -> Result<GenStep, String> {
3090 let id = match with_host(|h| h.get(gen).cloned()) {
3091 Some(JsObj::Generator { id }) => id,
3092 _ => return Err(type_error("not a generator")),
3093 };
3094 let started = with_host(|h| h.gen_started(id));
3097 if with_host(|h| h.generators[id as usize].done) || !started {
3098 with_host(|h| h.generators[id as usize].done = true);
3099 return Ok(GenStep::Done(v));
3100 }
3101 with_host(|h| h.generators[id as usize].inject = Some(GenInject::Return(v)));
3102 gen_resume(gen, Value::Undef)
3103}
3104
3105pub fn gen_throw(gen: &Value, e: Value) -> Result<GenStep, String> {
3108 let id = match with_host(|h| h.get(gen).cloned()) {
3109 Some(JsObj::Generator { id }) => id,
3110 _ => return Err(type_error("not a generator")),
3111 };
3112 let started = with_host(|h| h.gen_started(id));
3113 if with_host(|h| h.generators[id as usize].done) || !started {
3114 with_host(|h| h.generators[id as usize].done = true);
3116 let msg = with_host(|h| crate::builtins::error_string(h, &e));
3117 with_host(|h| h.exc = Some(e));
3118 return Err(msg);
3119 }
3120 with_host(|h| h.generators[id as usize].inject = Some(GenInject::Throw(e)));
3121 gen_resume(gen, Value::Undef)
3122}
3123
3124pub enum GenStep {
3127 Yield(Value),
3128 Done(Value),
3129}
3130
3131pub fn gen_resume(gen: &Value, send: Value) -> Result<GenStep, String> {
3136 let id = match with_host(|h| h.get(gen).cloned()) {
3137 Some(JsObj::Generator { id }) => id,
3138 _ => return Err(type_error("not a generator")),
3139 };
3140 if with_host(|h| h.generators[id as usize].done) {
3141 return Ok(GenStep::Done(Value::Undef));
3142 }
3143 let mut coro = match with_host(|h| h.generators[id as usize].coro.take()) {
3144 Some(c) => c,
3145 None => return Err("TypeError: generator already executing".into()),
3146 };
3147 with_host(|h| h.generators[id as usize].started = true);
3148 let gen_ctx = with_host(|h| std::mem::take(&mut h.generators[id as usize].ctx));
3149 let caller_ctx = with_host(|h| h.install_gen_ctx(gen_ctx));
3150 let prev = CUR_GEN.with(|c| c.replace(Some(id)));
3151
3152 let out = coro.resume(send); CUR_GEN.with(|c| c.set(prev));
3155 let gen_ctx = with_host(|h| h.install_gen_ctx(caller_ctx));
3156 with_host(|h| {
3157 h.generators[id as usize].ctx = gen_ctx;
3158 h.generators[id as usize].coro = Some(coro);
3159 });
3160
3161 match out {
3162 corosensei::CoroutineResult::Yield(y) => Ok(GenStep::Yield(y)),
3163 corosensei::CoroutineResult::Return(r) => {
3164 with_host(|h| h.generators[id as usize].done = true);
3165 match r {
3166 Ok(v) => Ok(GenStep::Done(v)),
3167 Err(e) => Err(e),
3168 }
3169 }
3170 }
3171}
3172
3173pub fn gen_close(gen: &Value) {
3176 if let Some(JsObj::Generator { id }) = with_host(|h| h.get(gen).cloned()) {
3177 with_host(|h| h.generators[id as usize].done = true);
3178 }
3179}
3180
3181pub fn map_key(h: &JsHost, v: &Value) -> MapKey {
3185 match v {
3186 Value::Undef => MapKey::Undef,
3187 Value::Bool(b) => MapKey::Bool(*b),
3188 Value::Int(n) => MapKey::Num(norm_num_bits(*n as f64)),
3189 Value::Float(f) => MapKey::Num(norm_num_bits(*f)),
3190 Value::Str(s) => MapKey::Str((**s).clone()),
3191 Value::Obj(i) => match h.get(v) {
3192 Some(JsObj::Str(s)) => MapKey::Str(s.clone()),
3193 Some(JsObj::Null) => MapKey::Null,
3194 Some(JsObj::BigInt(b)) => MapKey::Big(b.to_string()),
3195 _ => MapKey::Ref(*i),
3196 },
3197 _ => MapKey::Undef,
3198 }
3199}
3200
3201fn norm_num_bits(f: f64) -> u64 {
3203 if f.is_nan() {
3204 return f64::NAN.to_bits();
3205 }
3206 if f == 0.0 {
3207 return 0.0f64.to_bits(); }
3209 f.to_bits()
3210}
3211
3212pub fn iter_all(v: &Value) -> Result<Vec<Value>, String> {
3214 if with_host(|h| h.is_generator_val(v)) {
3216 let mut out = Vec::new();
3217 while let GenStep::Yield(x) = gen_resume(v, Value::Undef)? {
3218 out.push(x);
3219 }
3220 return Ok(out);
3221 }
3222 if let Some(iter_fn) = user_iterator_fn(v) {
3224 let iterator = invoke(&iter_fn, Vec::new(), Some(v.clone()))?;
3225 return drain_iterator(&iterator);
3226 }
3227 with_host(|h| h.iter_vec(v))
3228}
3229
3230pub fn get_async_iterator(src: &Value) -> Result<Value, String> {
3237 if let Some(f) = user_async_iterator_fn(src) {
3238 return invoke(&f, Vec::new(), Some(src.clone()));
3239 }
3240 let items = iter_all(src)?;
3241 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
3242}
3243
3244fn user_async_iterator_fn(v: &Value) -> Option<Value> {
3246 let is_plain = with_host(|h| matches!(h.get(v), Some(JsObj::Object(_))));
3247 if !is_plain {
3248 return None;
3249 }
3250 let f = with_host(|h| lookup_chain(h, v, "@@asyncIterator"));
3251 match f {
3252 Some(f) if with_host(|h| is_callable(h, &f)) => Some(f),
3253 _ => None,
3254 }
3255}
3256
3257pub fn async_step(iterator: &Value) -> Result<Value, String> {
3263 if let Some(JsObj::Iter { items, idx }) = with_host(|h| h.get(iterator).cloned()) {
3265 if idx >= items.len() {
3266 let rec = with_host(|h| {
3267 let mut m = IndexMap::new();
3268 m.insert("value".to_string(), Value::Undef);
3269 m.insert("done".to_string(), Value::Bool(true));
3270 h.new_object(m)
3271 });
3272 return Ok(promise_of(&rec));
3273 }
3274 let raw = items[idx].clone();
3275 with_host(|h| {
3276 if let Some(JsObj::Iter { idx, .. }) = h.get_mut(iterator) {
3277 *idx += 1;
3278 }
3279 });
3280 let step = with_host(|h| h.new_promise());
3282 let sid = with_host(|h| h.promise_id(&step).unwrap());
3283 let raw_p = promise_of(&raw);
3284 let raw_id = with_host(|h| h.promise_id(&raw_p).unwrap());
3285 subscribe_native(
3286 raw_id,
3287 Box::new(move |state, val| {
3288 if state == PromiseState::Rejected {
3289 reject_promise_val(sid, val);
3290 } else {
3291 let rec = with_host(|h| {
3292 let mut m = IndexMap::new();
3293 m.insert("value".to_string(), val.clone());
3294 m.insert("done".to_string(), Value::Bool(false));
3295 h.new_object(m)
3296 });
3297 resolve_promise_val(sid, rec);
3298 }
3299 Ok(())
3300 }),
3301 );
3302 return Ok(step);
3303 }
3304 let r = call_method(iterator, "next", Vec::new())?;
3306 Ok(promise_of(&r))
3307}
3308
3309fn user_iterator_fn(v: &Value) -> Option<Value> {
3312 let is_plain = with_host(|h| matches!(h.get(v), Some(JsObj::Object(_))));
3313 if !is_plain {
3314 return None;
3315 }
3316 let f = with_host(|h| lookup_chain(h, v, "@@iterator"));
3317 match f {
3318 Some(f) if with_host(|h| is_callable(h, &f)) => Some(f),
3319 _ => None,
3320 }
3321}
3322
3323fn drain_iterator(iterator: &Value) -> Result<Vec<Value>, String> {
3326 let mut out = Vec::new();
3327 loop {
3328 let step = call_method(iterator, "next", Vec::new())?;
3329 let done = get_prop_chain(&step, "done")?;
3330 if with_host(|h| h.truthy(&done)) {
3331 break;
3332 }
3333 out.push(get_prop_chain(&step, "value")?);
3334 }
3335 Ok(out)
3336}
3337
3338pub fn get_prop_chain(recv: &Value, name: &str) -> Result<Value, String> {
3340 crate::builtins::get_property(recv, name)
3341}
3342
3343pub fn to_string_value(v: &Value) -> Result<Value, String> {
3347 if with_host(|h| matches!(h.get(v), Some(JsObj::Object(_)))) {
3348 for m in ["toString", "valueOf"] {
3350 if let Some(f) = with_host(|h| lookup_chain(h, v, m)) {
3351 if with_host(|h| is_callable(h, &f)) {
3352 let r = invoke(&f, Vec::new(), Some(v.clone()))?;
3353 if !matches!(with_host(|h| h.get(&r).cloned()), Some(JsObj::Object(_))) {
3357 return Ok(with_host(|h| {
3358 let s = h.str_of(&r);
3359 h.new_str(s)
3360 }));
3361 }
3362 }
3363 }
3364 }
3365 }
3366 Ok(with_host(|h| {
3367 let s = h.str_of(v);
3368 h.new_str(s)
3369 }))
3370}
3371
3372pub fn is_callable(h: &JsHost, v: &Value) -> bool {
3374 matches!(
3375 h.get(v),
3376 Some(JsObj::Func(_))
3377 | Some(JsObj::Builtin(_))
3378 | Some(JsObj::BoundMethod { .. })
3379 | Some(JsObj::BoundFunc { .. })
3380 | Some(JsObj::Class(_))
3381 )
3382}
3383
3384pub fn lookup_chain(h: &JsHost, recv: &Value, key: &str) -> Option<Value> {
3387 if let Some(JsObj::Object(p)) = h.get(recv) {
3388 if let Some(v) = p.get(key) {
3389 return Some(v.clone());
3390 }
3391 }
3392 let mut cur = h.proto_of(recv);
3393 while let Some(p) = cur {
3394 match h.get(&p) {
3398 Some(JsObj::Object(props)) => {
3399 if let Some(v) = props.get(key) {
3400 return Some(v.clone());
3401 }
3402 }
3403 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => {
3404 if let Some(v) = h.fn_prop(&p, key) {
3405 return Some(v);
3406 }
3407 }
3408 _ => {}
3409 }
3410 cur = h.proto_of(&p);
3411 }
3412 None
3413}
3414
3415pub fn lookup_accessor(
3417 h: &JsHost,
3418 recv: &Value,
3419 key: &str,
3420) -> Option<(Option<Value>, Option<Value>)> {
3421 if let Some(a) = h.own_accessor(recv, key) {
3422 return Some(a);
3423 }
3424 let mut cur = h.proto_of(recv);
3425 while let Some(p) = cur {
3426 if let Some(a) = h.own_accessor(&p, key) {
3427 return Some(a);
3428 }
3429 cur = h.proto_of(&p);
3430 }
3431 None
3432}
3433
3434pub fn set_error_proto(name: &str, proto: Value) {
3436 with_host(|h| {
3437 h.error_protos.insert(name.to_string(), proto);
3438 });
3439}
3440pub fn error_proto(name: &str) -> Option<Value> {
3441 with_host(|h| h.error_protos.get(name).cloned())
3442}
3443pub fn error_proto_of(h: &JsHost, name: &str) -> Option<Value> {
3445 h.error_protos.get(name).cloned()
3446}
3447
3448pub const ERROR_NAMES: &[&str] = &[
3450 "Error",
3451 "TypeError",
3452 "RangeError",
3453 "SyntaxError",
3454 "ReferenceError",
3455 "EvalError",
3456 "URIError",
3457];
3458
3459impl JsHost {
3460 pub fn ensure_error_protos(&mut self) {
3465 if !self.error_protos.is_empty() {
3466 return;
3467 }
3468 let obj_proto = self.object_proto();
3469 let err_proto = self.new_object(IndexMap::new());
3471 self.set_proto(&err_proto, obj_proto);
3472 let nm = self.new_str("Error");
3473 let empty = self.new_str("");
3474 let ctor = self.alloc(JsObj::Builtin("Error".into()));
3475 if let Some(JsObj::Object(p)) = self.get_mut(&err_proto) {
3476 p.insert("name".into(), nm);
3477 p.insert("message".into(), empty);
3478 p.insert("constructor".into(), ctor);
3479 }
3480 self.error_protos.insert("Error".into(), err_proto.clone());
3481 for name in &ERROR_NAMES[1..] {
3482 let p = self.new_object(IndexMap::new());
3483 self.set_proto(&p, err_proto.clone());
3484 let nm = self.new_str(*name);
3485 let ctor = self.alloc(JsObj::Builtin((*name).to_string()));
3486 if let Some(JsObj::Object(o)) = self.get_mut(&p) {
3487 o.insert("name".into(), nm);
3488 o.insert("constructor".into(), ctor);
3489 }
3490 self.error_protos.insert((*name).to_string(), p);
3491 }
3492 }
3493}
3494
3495impl JsHost {
3498 pub fn func_arity(&self, v: &Value) -> usize {
3501 let def_id = match self.get(v) {
3502 Some(JsObj::Func(f)) => Some(f.def_id),
3503 Some(JsObj::Class(c)) => match c.ctor.as_ref().and_then(|cf| self.get(cf)) {
3504 Some(JsObj::Func(f)) => Some(f.def_id),
3505 _ => None,
3506 },
3507 _ => None,
3508 };
3509 match def_id.and_then(|id| self.funcs.get(id)) {
3510 Some(def) => def
3511 .params
3512 .iter()
3513 .take_while(|p| !p.rest && !p.has_default)
3514 .count(),
3515 None => 0,
3516 }
3517 }
3518
3519 pub fn is_map(&self, v: &Value) -> bool {
3520 matches!(self.get(v), Some(JsObj::Map { .. }))
3521 }
3522 pub fn is_set(&self, v: &Value) -> bool {
3523 matches!(self.get(v), Some(JsObj::Set { .. }))
3524 }
3525}
3526
3527impl JsHost {
3530 pub fn new_promise(&mut self) -> Value {
3532 let id = self.promises.len() as u32;
3533 self.promises.push(PromiseCell {
3534 state: PromiseState::Pending,
3535 value: Value::Undef,
3536 reactions: Vec::new(),
3537 handled: false,
3538 });
3539 self.alloc(JsObj::Promise { id })
3540 }
3541 pub fn promise_id(&self, v: &Value) -> Option<u32> {
3542 match self.get(v) {
3543 Some(JsObj::Promise { id }) => Some(*id),
3544 _ => None,
3545 }
3546 }
3547 pub fn promise_state(&self, id: u32) -> PromiseState {
3548 self.promises[id as usize].state
3549 }
3550 pub fn promise_value(&self, id: u32) -> Value {
3551 self.promises[id as usize].value.clone()
3552 }
3553 pub fn promise_mark_handled(&mut self, id: u32) {
3554 self.promises[id as usize].handled = true;
3555 }
3556 pub fn take_reactions(&mut self, id: u32) -> Vec<PromiseReaction> {
3558 std::mem::take(&mut self.promises[id as usize].reactions)
3559 }
3560 pub fn add_reaction(&mut self, id: u32, r: PromiseReaction) {
3561 self.promises[id as usize].reactions.push(r);
3562 }
3563 pub fn settle_promise(&mut self, id: u32, state: PromiseState, value: Value) {
3564 let c = &mut self.promises[id as usize];
3565 if c.state != PromiseState::Pending {
3566 return; }
3568 c.state = state;
3569 c.value = value;
3570 }
3571 pub fn queue_micro(&mut self, cb: Value, args: Vec<Value>) {
3572 self.microtasks.push_back(Task::Js { cb, args });
3573 }
3574 pub fn queue_nexttick(&mut self, cb: Value, args: Vec<Value>) {
3575 self.nextticks.push_back(Task::Js { cb, args });
3576 }
3577 pub fn queue_micro_native(&mut self, f: Box<dyn FnOnce() -> Result<(), String>>) {
3580 self.microtasks.push_back(Task::Native(f));
3581 }
3582 pub fn add_timer(&mut self, delay: f64, callback: Value, args: Vec<Value>) -> u64 {
3583 let id = self.next_timer;
3584 self.next_timer += 1;
3585 let deadline = Instant::now() + Duration::from_millis(delay.max(0.0) as u64);
3588 self.macrotasks.push(Timer {
3589 id,
3590 delay,
3591 seq: id,
3592 callback,
3593 args,
3594 cancelled: false,
3595 deadline,
3596 });
3597 id
3598 }
3599 pub fn io_sender(&self) -> Sender<IoTask> {
3601 self.io_tx.clone()
3602 }
3603 pub fn incr_handle(&mut self) {
3606 self.open_handles += 1;
3607 }
3608 pub fn decr_handle(&mut self) {
3610 self.open_handles = self.open_handles.saturating_sub(1);
3611 }
3612 pub fn open_handles(&self) -> usize {
3613 self.open_handles
3614 }
3615 fn pop_due_timer(&mut self, now: Instant) -> Option<Timer> {
3618 let idx = self
3619 .macrotasks
3620 .iter()
3621 .enumerate()
3622 .filter(|(_, t)| !t.cancelled && t.deadline <= now)
3623 .min_by(|(_, a), (_, b)| a.deadline.cmp(&b.deadline).then(a.seq.cmp(&b.seq)))
3624 .map(|(i, _)| i);
3625 idx.map(|i| self.macrotasks.remove(i))
3626 }
3627 fn next_timer_timeout(&self, now: Instant) -> Option<Duration> {
3630 self.macrotasks
3631 .iter()
3632 .filter(|t| !t.cancelled)
3633 .map(|t| t.deadline)
3634 .min()
3635 .map(|d| d.saturating_duration_since(now))
3636 }
3637 pub fn cancel_timer(&mut self, id: u64) {
3638 for t in &mut self.macrotasks {
3639 if t.id == id {
3640 t.cancelled = true;
3641 }
3642 }
3643 }
3644 fn pop_next_timer(&mut self) -> Option<Timer> {
3645 let idx = self
3647 .macrotasks
3648 .iter()
3649 .enumerate()
3650 .filter(|(_, t)| !t.cancelled)
3651 .min_by(|(_, a), (_, b)| {
3652 a.delay
3653 .partial_cmp(&b.delay)
3654 .unwrap_or(std::cmp::Ordering::Equal)
3655 .then(a.seq.cmp(&b.seq))
3656 })
3657 .map(|(i, _)| i);
3658 idx.map(|i| self.macrotasks.remove(i))
3659 }
3660 fn next_microtask(&mut self) -> Option<Task> {
3661 self.nextticks
3663 .pop_front()
3664 .or_else(|| self.microtasks.pop_front())
3665 }
3666 fn has_microtasks(&self) -> bool {
3667 !self.nextticks.is_empty() || !self.microtasks.is_empty()
3668 }
3669 fn has_macrotasks(&self) -> bool {
3670 self.macrotasks.iter().any(|t| !t.cancelled)
3671 }
3672}
3673
3674pub fn run_event_loop() -> Result<(), String> {
3691 let rx = with_host(|h| h.io_rx.take());
3694 let result = drive_event_loop(rx.as_ref());
3695 with_host(|h| h.io_rx = rx);
3696 result
3697}
3698
3699fn drive_event_loop(rx: Option<&Receiver<IoTask>>) -> Result<(), String> {
3700 loop {
3701 while let Some(task) = with_host(|h| h.next_microtask()) {
3703 task.run()?;
3704 }
3705
3706 if with_host(|h| h.open_handles()) == 0 {
3707 match with_host(|h| h.pop_next_timer()) {
3709 Some(t) => {
3710 invoke(&t.callback, t.args, None)?;
3711 }
3712 None => {
3713 if !with_host(|h| h.has_microtasks()) {
3714 break;
3715 }
3716 }
3717 }
3718 if !with_host(|h| h.has_microtasks() || h.has_macrotasks()) {
3719 break;
3720 }
3721 continue;
3722 }
3723
3724 let now = Instant::now();
3726 if let Some(t) = with_host(|h| h.pop_due_timer(now)) {
3727 invoke(&t.callback, t.args, None)?;
3728 continue; }
3730 let rx = rx.expect("blocking-I/O regime requires the I/O receiver");
3733 let timeout = with_host(|h| h.next_timer_timeout(now));
3734 let recv = match timeout {
3735 Some(d) => rx.recv_timeout(d),
3736 None => rx
3737 .recv()
3738 .map_err(|_| std::sync::mpsc::RecvTimeoutError::Disconnected),
3739 };
3740 match recv {
3741 Ok(task) => task()?,
3742 Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {} Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => break, }
3745 }
3746 Ok(())
3747}
3748
3749fn run_async(gen: Value) -> Value {
3753 let result = with_host(|h| h.new_promise());
3754 let rid = with_host(|h| h.promise_id(&result).unwrap());
3755 drive_async(gen, rid, Value::Undef);
3756 result
3757}
3758
3759fn drive_async(gen: Value, rid: u32, send: Value) {
3762 match gen_resume(&gen, send) {
3763 Ok(GenStep::Yield(awaited)) => {
3764 let ap = promise_of(&awaited);
3765 let aid = with_host(|h| h.promise_id(&ap).unwrap());
3766 let gen2 = gen.clone();
3767 subscribe_native(
3768 aid,
3769 Box::new(move |state, val| {
3770 let tag = if state == PromiseState::Rejected {
3773 1.0
3774 } else {
3775 0.0
3776 };
3777 let packet = with_host(|h| h.new_array(vec![Value::Float(tag), val]));
3778 drive_async(gen2, rid, packet);
3779 Ok(())
3780 }),
3781 );
3782 }
3783 Ok(GenStep::Done(v)) => resolve_promise_val(rid, v),
3784 Err(e) => {
3785 let ev = take_exc_or_error(&e);
3786 reject_promise_val(rid, ev);
3787 }
3788 }
3789}
3790
3791pub fn await_value(awaited: Value) -> Result<Value, String> {
3794 let packet = gen_yield(awaited)?;
3795 let items = with_host(|h| h.iter_vec(&packet)).unwrap_or_default();
3796 let tag = items
3797 .first()
3798 .map(|v| with_host(|h| h.to_number(v)))
3799 .unwrap_or(0.0);
3800 let val = items.get(1).cloned().unwrap_or(Value::Undef);
3801 if tag == 1.0 {
3802 with_host(|h| h.exc = Some(val.clone()));
3803 Err(with_host(|h| crate::builtins::error_string(h, &val)))
3804 } else {
3805 Ok(val)
3806 }
3807}
3808
3809pub fn promise_of(v: &Value) -> Value {
3812 if with_host(|h| h.promise_id(v)).is_some() {
3813 return v.clone();
3814 }
3815 let p = with_host(|h| h.new_promise());
3816 let id = with_host(|h| h.promise_id(&p).unwrap());
3817 resolve_promise_val(id, v.clone());
3818 p
3819}
3820
3821pub fn subscribe_native(id: u32, f: Box<dyn FnOnce(PromiseState, Value) -> Result<(), String>>) {
3824 let state = with_host(|h| h.promise_state(id));
3825 if state == PromiseState::Pending {
3826 with_host(|h| h.add_reaction(id, PromiseReaction::Native(f)));
3827 } else {
3828 let val = with_host(|h| h.promise_value(id));
3829 with_host(|h| h.queue_micro_native(Box::new(move || f(state, val))));
3830 }
3831}
3832
3833pub fn resolve_promise_val(id: u32, value: Value) {
3836 if with_host(|h| h.promise_state(id)) != PromiseState::Pending {
3837 return;
3838 }
3839 if let Some(vid) = with_host(|h| h.promise_id(&value)) {
3840 if vid == id {
3841 let e = with_host(|h| {
3843 crate::builtins::synth_error(h, "TypeError: Chaining cycle detected")
3844 });
3845 reject_promise_val(id, e);
3846 return;
3847 }
3848 subscribe_native(
3849 vid,
3850 Box::new(move |state, val| {
3851 with_host(|h| h.settle_promise(id, state, val.clone()));
3852 schedule_reactions(id);
3853 Ok(())
3854 }),
3855 );
3856 return;
3857 }
3858 with_host(|h| h.settle_promise(id, PromiseState::Fulfilled, value));
3859 schedule_reactions(id);
3860}
3861
3862pub fn reject_promise_val(id: u32, value: Value) {
3863 if with_host(|h| h.promise_state(id)) != PromiseState::Pending {
3864 return;
3865 }
3866 with_host(|h| h.settle_promise(id, PromiseState::Rejected, value));
3867 schedule_reactions(id);
3868}
3869
3870fn schedule_reactions(id: u32) {
3872 let reactions = with_host(|h| h.take_reactions(id));
3873 let state = with_host(|h| h.promise_state(id));
3874 let value = with_host(|h| h.promise_value(id));
3875 for r in reactions {
3876 let value = value.clone();
3877 match r {
3878 PromiseReaction::Native(f) => {
3879 with_host(|h| h.queue_micro_native(Box::new(move || f(state, value))));
3880 }
3881 PromiseReaction::Js {
3882 on_ful,
3883 on_rej,
3884 result,
3885 } => {
3886 with_host(|h| {
3887 h.queue_micro_native(Box::new(move || {
3888 run_js_reaction(state, value, on_ful, on_rej, result)
3889 }))
3890 });
3891 }
3892 }
3893 }
3894}
3895
3896fn run_js_reaction(
3899 state: PromiseState,
3900 value: Value,
3901 on_ful: Value,
3902 on_rej: Value,
3903 result: Value,
3904) -> Result<(), String> {
3905 let rid = match with_host(|h| h.promise_id(&result)) {
3906 Some(i) => i,
3907 None => return Ok(()),
3908 };
3909 let handler = if state == PromiseState::Rejected {
3910 on_rej
3911 } else {
3912 on_ful
3913 };
3914 if with_host(|h| is_callable(h, &handler)) {
3915 match invoke(&handler, vec![value], None) {
3916 Ok(r) => resolve_promise_val(rid, r),
3917 Err(e) => reject_promise_val(rid, take_exc_or_error(&e)),
3918 }
3919 } else if state == PromiseState::Rejected {
3920 reject_promise_val(rid, value);
3921 } else {
3922 resolve_promise_val(rid, value);
3923 }
3924 Ok(())
3925}
3926
3927pub fn take_exc_or_error(e: &str) -> Value {
3930 with_host(|h| {
3931 h.error.take();
3932 h.exc
3933 .take()
3934 .unwrap_or_else(|| crate::builtins::synth_error(h, e))
3935 })
3936}
3937
3938pub fn promise_then(p: &Value, on_ful: Value, on_rej: Value) -> Value {
3940 let id = match with_host(|h| h.promise_id(p)) {
3941 Some(i) => i,
3942 None => return Value::Undef,
3943 };
3944 with_host(|h| h.promise_mark_handled(id));
3945 let result = with_host(|h| h.new_promise());
3946 let reaction = PromiseReaction::Js {
3947 on_ful,
3948 on_rej,
3949 result: result.clone(),
3950 };
3951 let state = with_host(|h| h.promise_state(id));
3952 if state == PromiseState::Pending {
3953 with_host(|h| h.add_reaction(id, reaction));
3954 } else {
3955 let value = with_host(|h| h.promise_value(id));
3956 if let PromiseReaction::Js {
3957 on_ful,
3958 on_rej,
3959 result,
3960 } = reaction
3961 {
3962 with_host(|h| {
3963 h.queue_micro_native(Box::new(move || {
3964 run_js_reaction(state, value, on_ful, on_rej, result)
3965 }))
3966 });
3967 }
3968 }
3969 result
3970}