1use crate::host::{self, ops, with_host, FuncVal, JsObj, ObjKind};
7use fusevm::{NumOp, Value, VM};
8use indexmap::IndexMap;
9
10pub fn install(vm: &mut VM) {
12 vm.register_builtin(ops::GETLOCAL, b_getlocal);
13 vm.register_builtin(ops::SETLOCAL, b_setlocal);
14 vm.register_builtin(ops::SETLOCAL_STRICT, b_setlocal_strict);
15 vm.register_builtin(ops::DECLARE, b_declare);
16 vm.register_builtin(ops::DECLARE_CONST, b_declare_const);
17 vm.register_builtin(ops::MARK_HOLE, b_mark_hole);
18 vm.register_builtin(ops::DELNAME, b_delname);
19 vm.register_builtin(ops::GETATTR, b_getattr);
20 vm.register_builtin(ops::SETATTR, b_setattr);
21 vm.register_builtin(ops::GETITEM, b_getitem);
22 vm.register_builtin(ops::SETITEM, b_setitem);
23 vm.register_builtin(ops::DELITEM, b_delitem);
24 vm.register_builtin(ops::MKSTR, b_mkstr);
25 vm.register_builtin(ops::MKARR, b_mkarr);
26 vm.register_builtin(ops::MKOBJ, b_mkobj);
27 vm.register_builtin(ops::CALL, b_call);
28 vm.register_builtin(ops::CALL_METHOD, b_call_method);
29 vm.register_builtin(ops::CALL_VALUE, b_call_value);
30 vm.register_builtin(ops::NEW, b_new);
31 vm.register_builtin(ops::TRUTHY, b_truthy);
32 vm.register_builtin(ops::TOSTR, b_tostr);
33 vm.register_builtin(ops::MKFUNC, b_mkfunc);
34 vm.register_builtin(ops::GETITER, b_getiter);
35 vm.register_builtin(ops::FORITER, b_foriter);
36 vm.register_builtin(ops::FORIN_KEYS, b_forin_keys);
37 vm.register_builtin(ops::CONTAINS, b_contains);
38 vm.register_builtin(ops::SIG_RETURN, b_sig_return);
39 vm.register_builtin(ops::BINOP, b_binop);
40 vm.register_builtin(ops::UNARY, b_unary);
41 vm.register_builtin(ops::STRICT_EQ, b_strict_eq);
42 vm.register_builtin(ops::LOOSE_EQ, b_loose_eq);
43 vm.register_builtin(ops::TYPEOF, b_typeof);
44 vm.register_builtin(ops::LOAD_NULL, b_load_null);
45 vm.register_builtin(ops::THROW, b_throw);
46 vm.register_builtin(ops::TRY, b_try);
47 vm.register_builtin(ops::NULLISH, b_nullish);
48 vm.register_builtin(ops::UNPACK, b_unpack);
49 vm.register_builtin(ops::BUILD_ARGS, b_build_args);
50 vm.register_builtin(ops::THIS, b_this);
51 vm.register_builtin(ops::INSTANCEOF, b_instanceof);
52 vm.register_builtin(ops::DELPROP_NAME, b_delprop_name);
53 vm.register_builtin(ops::APPLY, b_apply);
54 vm.register_builtin(ops::APPLY_METHOD, b_apply_method);
55 vm.register_builtin(ops::OBJ_REST, b_obj_rest);
56 vm.register_builtin(ops::DIV, b_div);
57 vm.register_builtin(ops::POW, b_pow);
58 vm.register_builtin(ops::MKCLASS, b_mkclass);
59 vm.register_builtin(ops::DEF_MEMBER, b_def_member);
60 vm.register_builtin(ops::DEF_FIELD, b_def_field);
61 vm.register_builtin(ops::SUPER_CALL, b_super_call);
62 vm.register_builtin(ops::SUPER_GET, b_super_get);
63 vm.register_builtin(ops::YIELD, b_yield);
64 vm.register_builtin(ops::PROPKEY, b_propkey);
65 vm.register_builtin(ops::NEW_TARGET, b_new_target);
66 vm.register_builtin(ops::AWAIT, b_await);
67 vm.register_builtin(ops::DEF_ACCESSOR, b_def_accessor);
68 vm.register_builtin(ops::DBG_LINE, b_dbg_line);
69 vm.register_builtin(ops::MKBIGINT, b_mkbigint);
70 vm.register_builtin(ops::MKREGEX, b_mkregex);
71 vm.register_builtin(ops::TAG_TMPL, b_tag_tmpl);
72 vm.register_builtin(ops::GET_ASYNC_ITER, b_get_async_iter);
73 vm.register_builtin(ops::ASYNC_STEP, b_async_step);
74 vm.register_builtin(ops::NUM_STEP, b_num_step);
75 vm.register_builtin(ops::ITER_CLOSE, b_iter_close);
76 vm.register_builtin(ops::TYPEOF_NAME, b_typeof_name);
77 vm.register_builtin(ops::SIG_BREAK, b_sig_break);
78 vm.register_builtin(ops::SIG_CONTINUE, b_sig_continue);
79 vm.register_builtin(ops::SIG_UNWIND, b_sig_unwind);
80 vm.register_builtin(ops::PUSH_SCOPE, b_push_scope);
81 vm.register_builtin(ops::POP_SCOPE, b_pop_scope);
82 vm.register_builtin(ops::COPY_SCOPE, b_copy_scope);
83 vm.register_builtin(ops::DECLARE_VAR, b_declare_var);
84 vm.register_builtin(ops::HOIST_VAR, b_hoist_var);
85 vm.register_builtin(ops::NAMED_EVAL, b_named_eval);
86}
87
88pub(crate) fn close_iterator(it: &Value) -> Result<(), String> {
95 if with_host(|h| h.is_generator_val(it)) {
96 host::gen_return(it, Value::Undef)?;
97 return Ok(());
98 }
99 if matches!(with_host(|h| h.get(it).cloned()), Some(JsObj::Object(_))) {
100 if let Some(f) = with_host(|h| host::lookup_chain(h, it, "return")) {
101 if with_host(|h| host::is_callable(h, &f)) {
102 host::invoke(&f, Vec::new(), Some(it.clone()))?;
103 }
104 }
105 }
106 Ok(())
107}
108
109fn b_iter_close(vm: &mut VM, _: u8) -> Value {
110 let it = vm.pop();
111 match close_iterator(&it) {
114 Ok(()) => Value::Undef,
115 Err(e) => abort(vm, e),
116 }
117}
118
119fn b_num_step(vm: &mut VM, _: u8) -> Value {
124 let old = vm.pop();
125 let tag = match vm.pop() {
126 Value::Int(n) => n,
127 Value::Float(f) => f as i64,
128 _ => 1,
129 };
130 if with_host(|h| h.is_bigint_val(&old)) {
131 let b = with_host(|h| h.as_bigint(&old)).unwrap();
132 let old_n = with_host(|h| h.new_bigint(b.clone()));
133 let new = with_host(|h| h.new_bigint(b + num_bigint::BigInt::from(tag)));
134 vm.push(old_n);
135 new
136 } else {
137 let n = with_host(|h| h.to_number(&old));
138 vm.push(Value::Float(n));
139 Value::Float(n + tag as f64)
140 }
141}
142
143fn b_async_step(vm: &mut VM, _: u8) -> Value {
146 let iter = vm.pop();
147 let r = host::async_step(&iter);
148 finish(vm, r)
149}
150
151fn b_mkbigint(vm: &mut VM, _: u8) -> Value {
154 let digits = sval(&vm.pop());
155 match digits.parse::<num_bigint::BigInt>() {
156 Ok(b) => with_host(|h| h.new_bigint(b)),
157 Err(_) => abort(vm, host::type_error("invalid BigInt literal")),
158 }
159}
160
161fn b_tag_tmpl(vm: &mut VM, argc: u8) -> Value {
166 let mut all = pop_n(vm, argc as usize);
167 let int_of = |v: &Value| match v {
168 Value::Int(n) => *n as usize,
169 Value::Float(f) => *f as usize,
170 _ => 0,
171 };
172 let tag = all.remove(0);
173 let n = int_of(&all.remove(0));
174 let mcount = int_of(&all.remove(0));
175 let cooked: Vec<Value> = all.drain(0..n.min(all.len())).collect();
176 let raw: Vec<Value> = all.drain(0..n.min(all.len())).collect();
177 let values: Vec<Value> = all.drain(0..mcount.min(all.len())).collect();
178 let strings = with_host(|h| h.new_array(cooked));
181 let raw_arr = with_host(|h| h.new_array(raw));
182 with_host(|h| {
187 h.set_fn_prop(&strings, "raw", raw_arr);
188 h.set_prop_attrs(
189 &strings,
190 "raw",
191 host::PropAttrs {
192 writable: false,
193 enumerable: false,
194 configurable: false,
195 },
196 );
197 });
198 let mut call_args = vec![strings];
199 call_args.extend(values);
200 let r = host::invoke(&tag, call_args, None);
201 finish(vm, r)
202}
203
204fn b_get_async_iter(vm: &mut VM, _: u8) -> Value {
208 let src = vm.pop();
209 let r = host::get_async_iterator(&src);
210 finish(vm, r)
211}
212
213fn b_mkregex(vm: &mut VM, _: u8) -> Value {
217 let flags = sval(&vm.pop());
218 let pattern = sval(&vm.pop());
219 match crate::regexp::build_regexp(&pattern, &flags) {
220 Ok(v) => v,
221 Err(e) => abort(vm, e),
222 }
223}
224
225fn b_dbg_line(vm: &mut VM, _: u8) -> Value {
231 let line = match vm.pop() {
232 Value::Int(n) => n as u32,
233 _ => 0,
234 };
235 crate::dap::on_debug_line(line);
236 Value::Undef
237}
238
239fn b_def_accessor(vm: &mut VM, _: u8) -> Value {
242 let func = vm.pop();
243 let kind = match vm.pop() {
244 Value::Int(n) => n,
245 _ => 0,
246 };
247 let name = sval(&vm.pop());
248 let obj = vm.pop();
249 with_host(|h| {
250 if kind == host::member::SET {
251 h.set_accessor(&obj, &name, None, Some(func));
252 } else {
253 h.set_accessor(&obj, &name, Some(func), None);
254 }
255 });
256 obj
257}
258
259fn b_await(vm: &mut VM, _: u8) -> Value {
260 let v = vm.pop();
261 match host::await_value(v) {
262 Ok(r) => r,
263 Err(e) => abort(vm, e),
264 }
265}
266
267fn b_mkclass(vm: &mut VM, _: u8) -> Value {
270 let ctor = vm.pop();
271 let parent = vm.pop();
272 let name = sval(&vm.pop());
273 host::build_class(&name, parent, ctor)
274}
275
276fn b_def_member(vm: &mut VM, _: u8) -> Value {
277 let func = vm.pop();
278 let is_static = matches!(vm.pop(), Value::Bool(true));
279 let kind = match vm.pop() {
280 Value::Int(n) => n,
281 _ => 0,
282 };
283 let name = sval(&vm.pop());
284 let class_val = vm.pop();
285 host::define_member(&class_val, &name, kind, is_static, func);
286 class_val
287}
288
289fn b_def_field(vm: &mut VM, _: u8) -> Value {
290 let name_anon = matches!(vm.pop(), Value::Bool(true));
294 let thunk = vm.pop();
295 let name = sval(&vm.pop());
296 let class_val = vm.pop();
297 host::define_field(&class_val, &name, thunk, name_anon);
298 class_val
299}
300
301fn b_super_call(vm: &mut VM, argc: u8) -> Value {
304 let args = pop_n(vm, argc as usize);
305 let this = with_host(|h| h.current_this());
306 let this = match this {
307 Some(t) => t,
308 None => return abort(vm, host::type_error("'super' keyword unexpected here")),
309 };
310 let (parent, fields) = with_host(|h| h.super_context());
312 let (parent, fields) = match parent {
313 Some(p) => (p, fields),
314 None => return abort(vm, host::type_error("'super' keyword unexpected here")),
315 };
316 let nt = with_host(|h| h.current_new_target()).unwrap_or_else(|| this.clone());
317 let r = host::super_construct(&parent, args, &this, &nt);
318 if let Err(e) = r {
319 return abort(vm, e);
320 }
321 for (name, thunk, name_anon) in fields {
323 if let Err(e) = host::init_one_field(&this, &name, &thunk, name_anon) {
324 return abort(vm, e);
325 }
326 }
327 Value::Undef
328}
329
330fn b_super_get(vm: &mut VM, _: u8) -> Value {
332 let name = sval(&vm.pop());
333 match with_host(|h| h.super_resolve(&name)) {
334 host::SuperRef::Data(v) => v,
335 host::SuperRef::Getter(getter) => {
336 let this = with_host(|h| h.current_this());
337 match host::invoke(&getter, Vec::new(), this) {
338 Ok(v) => v,
339 Err(e) => abort(vm, e),
340 }
341 }
342 }
343}
344
345fn close_parked_iters(vm: &mut VM) {
353 let n = host::parked_iters(vm);
354 if n == 0 {
355 return;
356 }
357 let saved = with_host(|h| (h.signal.take(), h.error.take()));
362 for _ in 0..n {
363 let it = vm.pop();
364 let _ = close_iterator(&it);
365 }
366 with_host(|h| {
367 h.signal = saved.0;
368 h.error = saved.1;
369 });
370}
371
372fn b_yield(vm: &mut VM, _: u8) -> Value {
373 let v = vm.pop();
374 match host::gen_yield(v) {
375 Ok(sent) => {
376 if with_host(|h| h.error.is_some() || h.signal.is_some()) {
380 close_parked_iters(vm);
386 vm.ip = vm.chunk.ops.len();
387 }
388 sent
389 }
390 Err(e) => {
395 close_parked_iters(vm);
396 abort(vm, e)
397 }
398 }
399}
400
401fn b_propkey(vm: &mut VM, _: u8) -> Value {
410 let v = vm.pop();
411 match host::to_property_key(&v) {
412 Ok(k) => with_host(|h| h.new_str(k)),
413 Err(e) => abort(vm, e),
414 }
415}
416
417fn b_new_target(_vm: &mut VM, _: u8) -> Value {
418 with_host(|h| h.current_new_target().unwrap_or(Value::Undef))
419}
420
421fn b_div(vm: &mut VM, _: u8) -> Value {
432 let b = vm.pop();
433 let a = vm.pop();
434 let r = numeric_hook(NumOp::Div, &a, &b);
435 finish(vm, r)
436}
437
438fn b_pow(vm: &mut VM, _: u8) -> Value {
443 let b = vm.pop();
444 let a = vm.pop();
445 let r = numeric_hook(NumOp::Pow, &a, &b);
446 finish(vm, r)
447}
448
449fn b_obj_rest(vm: &mut VM, _: u8) -> Value {
451 let excluded = vm.pop();
452 let obj = vm.pop();
453 let excl: Vec<String> = with_host(|h| h.iter_vec(&excluded))
454 .unwrap_or_default()
455 .iter()
456 .map(|v| with_host(|h| h.str_of(v)))
457 .collect();
458 with_host(|h| {
459 let props: IndexMap<String, Value> = match h.get(&obj) {
460 Some(JsObj::Object(m)) => m
461 .iter()
462 .filter(|(k, _)| !excl.contains(k))
463 .map(|(k, v)| (k.clone(), v.clone()))
464 .collect(),
465 _ => IndexMap::new(),
466 };
467 h.new_object(props)
468 })
469}
470
471fn pop_n(vm: &mut VM, n: usize) -> Vec<Value> {
474 let mut v = Vec::with_capacity(n);
475 for _ in 0..n {
476 v.push(vm.pop());
477 }
478 v.reverse();
479 v
480}
481
482fn sval(v: &Value) -> String {
484 if let Value::Str(s) = v {
485 return (**s).clone();
486 }
487 with_host(|h| h.as_str(v)).unwrap_or_default()
488}
489
490fn sname(v: &Value) -> std::sync::Arc<String> {
496 match v {
497 Value::Str(s) => s.clone(),
498 _ => std::sync::Arc::new(sval(v)),
499 }
500}
501
502fn abort(vm: &mut VM, e: String) -> Value {
503 with_host(|h| h.error = Some(e));
504 vm.ip = vm.chunk.ops.len();
505 Value::Undef
506}
507
508fn finish(vm: &mut VM, r: Result<Value, String>) -> Value {
510 match r {
511 Ok(v) => {
512 if with_host(|h| h.error.is_some() || h.signal.is_some()) {
513 vm.ip = vm.chunk.ops.len();
514 }
515 v
516 }
517 Err(e) => abort(vm, e),
518 }
519}
520
521pub(crate) fn global_binding(name: &str) -> Option<Value> {
529 if let Some(v) = with_host(|h| h.read_name(name)) {
530 return Some(v);
531 }
532 match name {
534 "undefined" => return Some(Value::Undef),
535 "NaN" => return Some(Value::Float(f64::NAN)),
536 "Infinity" => return Some(Value::Float(f64::INFINITY)),
537 "globalThis" | "global" => return Some(with_host(|h| h.global_object())),
542 _ => {}
543 }
544 if is_namespace(name) || is_known_builtin(name) {
545 return Some(with_host(|h| h.alloc(JsObj::Builtin(name.to_string()))));
546 }
547 None
548}
549
550fn b_getlocal(vm: &mut VM, _: u8) -> Value {
551 let name = sname(&vm.pop());
552 match global_binding(&name) {
553 Some(v) => v,
554 None => abort(vm, host::ref_error(&name)),
555 }
556}
557
558const READONLY_GLOBALS: [&str; 3] = ["undefined", "NaN", "Infinity"];
562
563fn readonly_global_error(name: &str) -> String {
564 host::type_error(&format!(
565 "Cannot assign to read only property '{name}' of object '#<Object>'"
566 ))
567}
568
569fn b_setlocal(vm: &mut VM, _: u8) -> Value {
570 let val = vm.pop();
571 let name = sname(&vm.pop());
572 if READONLY_GLOBALS.contains(&name.as_str()) && !with_host(|h| h.has_name(&name)) {
576 return val;
577 }
578 if !with_host(|h| h.set_name(&name, val.clone())) {
581 return abort(vm, host::type_error("Assignment to constant variable."));
582 }
583 val
584}
585
586fn b_setlocal_strict(vm: &mut VM, _: u8) -> Value {
595 let val = vm.pop();
596 let name = sname(&vm.pop());
597 if !binding_exists(&name) {
598 return abort(vm, host::ref_error(&name));
599 }
600 if READONLY_GLOBALS.contains(&name.as_str()) && !with_host(|h| h.has_name(&name)) {
601 return abort(vm, readonly_global_error(&name));
602 }
603 if !with_host(|h| h.set_name(&name, val.clone())) {
604 return abort(vm, host::type_error("Assignment to constant variable."));
605 }
606 val
607}
608
609fn binding_exists(name: &str) -> bool {
614 if with_host(|h| h.has_name(name)) {
615 return true;
616 }
617 matches!(
618 name,
619 "undefined" | "NaN" | "Infinity" | "globalThis" | "global"
620 ) || is_namespace(name)
621 || is_known_builtin(name)
622}
623
624fn b_declare(vm: &mut VM, _: u8) -> Value {
625 let val = vm.pop();
626 let name = sname(&vm.pop());
627 with_host(|h| h.declare_name(&name, val.clone()));
628 val
629}
630
631fn b_declare_const(vm: &mut VM, _: u8) -> Value {
634 let val = vm.pop();
635 let name = sname(&vm.pop());
636 with_host(|h| h.declare_const_name(&name, val.clone()));
637 val
638}
639
640fn b_hoist_var(vm: &mut VM, _: u8) -> Value {
644 let name = sname(&vm.pop());
645 with_host(|h| h.hoist_var_name(&name));
646 Value::Undef
647}
648
649fn b_declare_var(vm: &mut VM, _: u8) -> Value {
650 let val = vm.pop();
651 let name = sname(&vm.pop());
652 with_host(|h| h.declare_var_name(&name, val.clone()));
653 val
654}
655
656fn b_push_scope(_: &mut VM, _: u8) -> Value {
657 with_host(|h| h.push_scope());
658 Value::Undef
659}
660
661fn b_pop_scope(_: &mut VM, _: u8) -> Value {
662 with_host(|h| h.pop_scope());
663 Value::Undef
664}
665
666fn b_copy_scope(_: &mut VM, _: u8) -> Value {
667 with_host(|h| h.copy_scope());
668 Value::Undef
669}
670
671fn b_delname(vm: &mut VM, _: u8) -> Value {
672 let name = sval(&vm.pop());
673 with_host(|h| h.del_name(&name));
674 Value::Bool(true)
675}
676
677fn b_this(_vm: &mut VM, _: u8) -> Value {
678 with_host(|h| h.current_this().unwrap_or(Value::Undef))
679}
680
681fn b_load_null(_vm: &mut VM, _: u8) -> Value {
682 with_host(|h| h.null())
683}
684
685fn b_getattr(vm: &mut VM, _: u8) -> Value {
688 let name = sval(&vm.pop());
689 let recv = vm.pop();
690 match get_property(&recv, &name) {
691 Ok(v) => v,
692 Err(e) => abort(vm, e),
693 }
694}
695
696fn peek<R>(recv: &Value, f: impl FnOnce(&JsObj) -> Option<R>) -> Option<R> {
706 with_host(|h| h.get(recv).and_then(f))
707}
708
709pub(crate) fn proxy_proto_link(recv: &Value, name: &str) -> Option<Value> {
716 with_host(|h| {
717 let mut cur = h.proto_of(recv);
718 for _ in 0..100 {
719 let p = cur?;
720 match h.get(&p) {
721 Some(JsObj::Proxy { .. }) => return Some(p),
722 Some(JsObj::Object(props)) if props.contains_key(name) => return None,
723 _ => {}
724 }
725 if h.own_accessor(&p, name).is_some() {
726 return None;
727 }
728 cur = h.proto_of(&p);
729 }
730 None
731 })
732}
733
734pub fn get_property(recv: &Value, name: &str) -> Result<Value, String> {
735 if name.starts_with('#') && !with_host(|h| h.has_private(recv, name)) {
739 return Err(private_brand_message(name, false));
740 }
741 get_property_recv(recv, name, recv)
742}
743
744pub fn private_brand_message(name: &str, writing: bool) -> String {
748 if with_host(|h| h.is_private_method(name)) {
749 if let Some(class) = with_host(|h| h.current_home_class_name()) {
750 return host::type_error(&format!("Receiver must be an instance of class {class}"));
751 }
752 }
753 let verb = if writing { "write" } else { "read" };
754 let prep = if writing { "to" } else { "from" };
755 host::type_error(&format!(
756 "Cannot {verb} private member {name} {prep} an object whose class did not declare it"
757 ))
758}
759
760pub fn get_property_recv(recv: &Value, name: &str, receiver: &Value) -> Result<Value, String> {
766 if let Some(v) = crate::proxy::get(recv, name, receiver)? {
770 return Ok(v);
771 }
772 if with_host(|h| h.is_nullish(recv)) {
773 return Err(host::type_error(&format!(
774 "Cannot read properties of {} (reading '{name}')",
775 with_host(|h| h.str_of(recv))
776 )));
777 }
778 if with_host(|h| h.is_global_object(recv)) {
784 let own = with_host(|h| match h.get(recv) {
785 Some(JsObj::Object(p)) => p.contains_key(name),
786 _ => false,
787 });
788 const CJS_WRAPPER_LOCALS: &[&str] = &[
792 "require",
793 "module",
794 "exports",
795 "__filename",
796 "__dirname",
797 "__cjs_require",
798 "__cjs_resolve",
799 ];
800 if !own && !CJS_WRAPPER_LOCALS.contains(&name) {
801 if let Some(v) = global_binding(name) {
802 return Ok(v);
803 }
804 }
805 }
806 if let Some((getter, _)) = with_host(|h| host::lookup_accessor(h, recv, name)) {
809 return match getter {
810 Some(g) => host::invoke(&g, Vec::new(), Some(receiver.clone())),
811 None => Ok(Value::Undef), };
813 }
814 if name == "@@toStringTag" && with_host(|h| host::lookup_chain(h, recv, name)).is_none() {
821 if let Some(tag) = with_host(|h| well_known_tag(h, recv)) {
822 return Ok(with_host(|h| h.new_str(tag)));
823 }
824 }
825 if name == "constructor" {
830 if let Some(v) = with_host(|h| {
831 match h.get(recv) {
832 Some(JsObj::Object(p)) => p.get("constructor").cloned(),
833 _ => None,
834 }
835 .or_else(|| host::lookup_chain(h, recv, "constructor"))
836 }) {
837 return Ok(v);
838 }
839 if let Some(cn) = with_host(|h| default_ctor_name(h, recv)) {
840 return Ok(with_host(|h| h.alloc(JsObj::Builtin(cn.to_string()))));
841 }
842 }
843 if name == "__proto__"
850 && !with_host(|h| h.has_null_proto(recv))
851 && peek(recv, |o| match o {
852 JsObj::Object(p) => Some(p.contains_key("__proto__")),
853 _ => Some(false),
854 }) != Some(true)
855 {
856 return Ok(prototype_of(recv));
857 }
858 let kind = with_host(|h| h.kind_of(recv));
859 Ok(match kind {
860 Some(ObjKind::Object) => {
861 let numeric = !name.is_empty() && name.bytes().all(|b| b.is_ascii_digit());
862 if numeric && crate::stdlib::native_tag(recv).as_deref() == Some("TypedArray") {
865 if let Some(v) = crate::stdlib::typedarray::elem_get(recv, name) {
866 return Ok(v);
867 }
868 }
869 if numeric
872 && peek(recv, |o| match o {
873 JsObj::Object(p) => Some(p.contains_key("@@bytes")),
874 _ => None,
875 })
876 .unwrap_or(false)
877 {
878 return Ok(crate::stdlib::buffer::byte_get(recv, name));
879 }
880 if let Some(v) = peek(recv, |o| match o {
881 JsObj::Object(p) => p.get(name).cloned(),
882 _ => None,
883 }) {
884 v
885 } else if let Some(link) = proxy_proto_link(recv, name) {
886 return Ok(crate::proxy::get(&link, name, recv)?.expect("link is a proxy"));
893 } else if let Some(v) = with_host(|h| host::lookup_chain(h, recv, name)) {
894 v
896 } else if crate::stdlib::native_tag(recv)
897 .map(|tag| crate::stdlib::instance_has_method(&tag, name))
898 .unwrap_or(false)
899 {
900 bound_method(recv, name)
903 } else if is_object_method(name) && !with_host(|h| h.has_null_proto(recv)) {
904 bound_method(recv, name)
909 } else {
910 Value::Undef
911 }
912 }
913 Some(ObjKind::Class) | Some(ObjKind::Func) | Some(ObjKind::BoundFunc) => {
914 function_property(recv, name)
915 }
916 Some(ObjKind::Symbol) => match name {
917 "description" => {
918 match peek(recv, |o| match o {
919 JsObj::Symbol { desc, .. } => desc.clone(),
920 _ => None,
921 }) {
922 Some(d) => with_host(|h| h.new_str(d)),
923 None => Value::Undef,
924 }
925 }
926 "toString" => bound_method(recv, name),
927 _ => Value::Undef,
928 },
929 Some(ObjKind::BigInt) => {
930 if matches!(
931 name,
932 "toString" | "valueOf" | "toLocaleString" | "constructor"
933 ) {
934 bound_method(recv, name)
935 } else {
936 Value::Undef
937 }
938 }
939 Some(ObjKind::RegExp) => {
940 let r = peek(recv, |o| match o {
944 JsObj::RegExp(r) => Some(r.clone()),
945 _ => None,
946 });
947 match r {
948 Some(r) => crate::regexp::regexp_property(&r, name).unwrap_or_else(|| {
949 if crate::regexp::is_regexp_method(name) {
950 bound_method(recv, name)
951 } else {
952 Value::Undef
953 }
954 }),
955 None => Value::Undef,
956 }
957 }
958 Some(ObjKind::Map) => {
961 let (len, weak) = peek(recv, |o| match o {
962 JsObj::Map { entries, weak } => Some((entries.len(), *weak)),
963 _ => None,
964 })
965 .unwrap_or((0, false));
966 match name {
967 "size" if !weak => Value::Float(len as f64),
968 "@@iterator" => bound_method(recv, name),
969 _ if is_map_method(name) => bound_method(recv, name),
970 _ => Value::Undef,
971 }
972 }
973 Some(ObjKind::Set) => {
974 let (len, weak) = peek(recv, |o| match o {
975 JsObj::Set { entries, weak } => Some((entries.len(), *weak)),
976 _ => None,
977 })
978 .unwrap_or((0, false));
979 match name {
980 "size" if !weak => Value::Float(len as f64),
981 "@@iterator" => bound_method(recv, name),
982 _ if is_set_method(name) => bound_method(recv, name),
983 _ => Value::Undef,
984 }
985 }
986 Some(ObjKind::Generator) => {
987 if is_generator_method(name) {
988 bound_method(recv, name)
989 } else {
990 Value::Undef
991 }
992 }
993 Some(ObjKind::Promise) => {
994 if matches!(name, "then" | "catch" | "finally") {
995 bound_method(recv, name)
996 } else {
997 Value::Undef
998 }
999 }
1000 Some(ObjKind::Iter) => {
1001 if matches!(name, "next" | "return" | "@@iterator") {
1002 bound_method(recv, name)
1003 } else {
1004 Value::Undef
1005 }
1006 }
1007 Some(ObjKind::Array) => {
1008 if name == "length" {
1009 let n = peek(recv, |o| match o {
1010 JsObj::Array(items) => Some(items.len()),
1011 _ => None,
1012 })
1013 .unwrap_or(0);
1014 Value::Float(n as f64)
1015 } else if let Ok(i) = name.parse::<usize>() {
1016 peek(recv, |o| match o {
1017 JsObj::Array(items) => items.get(i).cloned(),
1018 _ => None,
1019 })
1020 .unwrap_or(Value::Undef)
1021 } else if name == "@@iterator" || is_array_method(name) || is_object_method(name) {
1022 bound_method(recv, name)
1023 } else if let Some(v) = with_host(|h| h.fn_prop(recv, name)) {
1024 v
1027 } else {
1028 Value::Undef
1029 }
1030 }
1031 Some(ObjKind::Str) => {
1032 if name == "length" {
1034 let n = peek(recv, |o| match o {
1035 JsObj::Str(s) => Some(crate::utf16::len(s)),
1036 _ => None,
1037 })
1038 .unwrap_or(0);
1039 Value::Float(n as f64)
1040 } else if let Ok(i) = name.parse::<usize>() {
1041 match peek(recv, |o| match o {
1042 JsObj::Str(s) => crate::utf16::Units::of(s).unit_str(i),
1043 _ => None,
1044 }) {
1045 Some(c) => with_host(|h| h.new_str(c)),
1046 None => Value::Undef,
1047 }
1048 } else if name == "@@iterator" || is_string_method(name) {
1049 bound_method(recv, name)
1050 } else {
1051 Value::Undef
1052 }
1053 }
1054 Some(ObjKind::Builtin) => {
1055 let ns = peek(recv, |o| match o {
1056 JsObj::Builtin(ns) => Some(ns.clone()),
1057 _ => None,
1058 })
1059 .unwrap_or_default();
1060 namespace_property(&ns, name)
1061 }
1062 _ => {
1063 if matches!(recv, Value::Float(_) | Value::Int(_)) && is_number_method(name) {
1065 bound_method(recv, name)
1066 } else {
1067 Value::Undef
1068 }
1069 }
1070 })
1071}
1072
1073pub const REQUIRE_CACHE: &str = "__cjs_cache";
1078
1079fn default_ctor_name(h: &host::JsHost, recv: &Value) -> Option<&'static str> {
1085 match h.get(recv) {
1086 Some(JsObj::Array(_)) => Some("Array"),
1087 Some(JsObj::Object(props)) => {
1088 match props.get("@@native").map(|t| h.str_of(t)).as_deref() {
1094 Some("Buffer") => Some("Buffer"),
1095 Some("URL") => Some("URL"),
1096 Some("Date") => Some("Date"),
1097 Some("WeakRef") => Some("WeakRef"),
1098 Some("FinalizationRegistry") => Some("FinalizationRegistry"),
1099 Some("TextEncoder") => Some("TextEncoder"),
1100 Some("TextDecoder") => Some("TextDecoder"),
1101 Some("EventEmitter") => Some("EventEmitter"),
1102 Some("Timeout") => Some("Timeout"),
1103 Some("Immediate") => Some("Immediate"),
1104 _ => Some("Object"),
1105 }
1106 }
1107 Some(JsObj::Map { weak, .. }) => Some(if *weak { "WeakMap" } else { "Map" }),
1108 Some(JsObj::Set { weak, .. }) => Some(if *weak { "WeakSet" } else { "Set" }),
1109 Some(JsObj::Promise { .. }) => Some("Promise"),
1110 Some(JsObj::Str(_)) => Some("String"),
1111 Some(JsObj::Symbol { .. }) => Some("Symbol"),
1112 Some(JsObj::BigInt(_)) => Some("BigInt"),
1113 Some(JsObj::RegExp(_)) => Some("RegExp"),
1114 Some(JsObj::Iter { .. }) => Some("Iterator"),
1115 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) | Some(JsObj::BoundFunc { .. }) => {
1116 Some("Function")
1117 }
1118 _ => match recv {
1119 Value::Float(_) | Value::Int(_) => Some("Number"),
1120 Value::Bool(_) => Some("Boolean"),
1121 _ => None,
1122 },
1123 }
1124}
1125
1126fn is_builtin_ctor(name: &str) -> bool {
1134 matches!(
1135 name,
1136 "Array"
1137 | "Object"
1138 | "Number"
1139 | "String"
1140 | "Boolean"
1141 | "Symbol"
1142 | "Function"
1143 | "Map"
1144 | "Set"
1145 | "WeakMap"
1146 | "WeakSet"
1147 | "Promise"
1148 | "BigInt"
1149 | "Iterator"
1150 | "RegExp"
1151 | "Date"
1152 | "ArrayBuffer"
1153 | "Uint8Array"
1154 | "Int8Array"
1155 | "Uint8ClampedArray"
1156 | "Int16Array"
1157 | "Uint16Array"
1158 | "Int32Array"
1159 | "Uint32Array"
1160 | "Float32Array"
1161 | "Float64Array"
1162 | "BigInt64Array"
1163 | "BigUint64Array"
1164 | "WeakRef"
1165 | "FinalizationRegistry"
1166 | "TextEncoder"
1167 | "TextDecoder"
1168 | "IncomingMessage"
1169 | "ServerResponse"
1170 | "EventEmitter"
1171 | "Buffer"
1172 | "URL"
1173 | "URLSearchParams"
1174 | "Timeout"
1175 | "Immediate"
1176 ) || host::ERROR_NAMES.contains(&name)
1177}
1178
1179fn bound_method(recv: &Value, name: &str) -> Value {
1180 with_host(|h| {
1181 h.alloc(JsObj::BoundMethod {
1182 recv: recv.clone(),
1183 name: name.to_string(),
1184 })
1185 })
1186}
1187
1188fn is_object_method(name: &str) -> bool {
1190 matches!(
1191 name,
1192 "hasOwnProperty"
1193 | "isPrototypeOf"
1194 | "propertyIsEnumerable"
1195 | "toString"
1196 | "toLocaleString"
1197 | "valueOf"
1198 | "constructor"
1199 )
1200}
1201
1202pub const OBJECT_PROTO_METHODS: &[&str] = &[
1206 "hasOwnProperty",
1207 "isPrototypeOf",
1208 "propertyIsEnumerable",
1209 "toString",
1210 "toLocaleString",
1211 "valueOf",
1212];
1213
1214pub fn is_object_builtin_method(name: &str) -> bool {
1215 matches!(
1216 name,
1217 "hasOwnProperty"
1218 | "isPrototypeOf"
1219 | "propertyIsEnumerable"
1220 | "toString"
1221 | "toLocaleString"
1222 | "valueOf"
1223 )
1224}
1225
1226pub fn object_builtin_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
1228 match name {
1229 "hasOwnProperty" => {
1230 let k = with_host(|h| h.property_key(&arg0(&args)));
1231 if with_host(|h| h.kind_of(recv)) == Some(ObjKind::Builtin) {
1234 return Ok(Value::Bool(has_property(recv, &k)?));
1235 }
1236 if with_host(|h| h.kind_of(recv)) == Some(ObjKind::Proxy) {
1240 let d = crate::proxy::get_own_descriptor(recv, &k)?.unwrap_or(Value::Undef);
1241 return Ok(Value::Bool(!matches!(d, Value::Undef)));
1242 }
1243 if let Some(hit) = crate::stdlib::typedarray::has_index(recv, &k) {
1248 return Ok(Value::Bool(hit));
1249 }
1250 let has = with_host(|h| match h.get(recv) {
1251 Some(JsObj::Object(p)) => p.contains_key(&k) || h.own_accessor(recv, &k).is_some(),
1252 Some(JsObj::Array(items)) => {
1253 k == "length"
1254 || k.parse::<usize>()
1255 .map(|i| i < items.len() && !h.is_hole(recv, i))
1256 .unwrap_or(false)
1257 }
1258 _ => false,
1259 });
1260 Ok(Value::Bool(has))
1261 }
1262 "isPrototypeOf" => {
1263 let target = arg0(&args);
1264 let mut cur = match crate::proxy::get_prototype_of(&target)? {
1270 Some(p) => Some(p).filter(|p| !with_host(|h| h.is_null(p))),
1271 None => with_host(|h| h.proto_of(&target)),
1272 };
1273 while let Some(p) = cur {
1274 if with_host(|h| h.strict_eq(&p, recv)) {
1275 return Ok(Value::Bool(true));
1276 }
1277 cur = with_host(|h| h.proto_of(&p));
1278 }
1279 Ok(Value::Bool(false))
1280 }
1281 "propertyIsEnumerable" => {
1282 let k = with_host(|h| h.str_of(&arg0(&args)));
1283 if with_host(|h| h.kind_of(recv)) == Some(ObjKind::Proxy) {
1287 let has = crate::proxy::own_enum_string_keys(recv)?.contains(&k);
1288 return Ok(Value::Bool(has));
1289 }
1290 let has = with_host(|h| h.own_enum_key_names(recv).contains(&k));
1291 Ok(Value::Bool(has))
1292 }
1293 "toString" => Ok(with_host(|h| {
1294 let s = h.str_of(recv);
1297 h.new_str(s)
1298 })),
1299 "toLocaleString" => {
1304 let v = host::call_method(recv, "toString", Vec::new())?;
1305 Ok(v)
1306 }
1307 "valueOf" => Ok(recv.clone()),
1308 _ => Err(host::type_error(&format!("{name} is not a function"))),
1309 }
1310}
1311
1312pub fn function_builtin_method(
1316 recv: &Value,
1317 name: &str,
1318 args: &[Value],
1319) -> Result<Option<Value>, String> {
1320 match name {
1321 "call" => {
1322 let this = args.first().cloned();
1323 let rest = args.get(1..).map(|s| s.to_vec()).unwrap_or_default();
1324 Ok(Some(host::invoke(recv, rest, this)?))
1325 }
1326 "apply" => {
1327 let this = args.first().cloned();
1328 let arr = args.get(1).cloned().unwrap_or(Value::Undef);
1329 let call_args = if matches!(arr, Value::Undef) || with_host(|h| h.is_null(&arr)) {
1330 Vec::new()
1331 } else {
1332 with_host(|h| h.iter_vec(&arr)).unwrap_or_default()
1333 };
1334 Ok(Some(host::invoke(recv, call_args, this)?))
1335 }
1336 "bind" => {
1337 let this = args.first().cloned().unwrap_or(Value::Undef);
1338 let pre = args.get(1..).map(|s| s.to_vec()).unwrap_or_default();
1339 Ok(Some(with_host(|h| {
1340 h.alloc(JsObj::BoundFunc {
1341 target: recv.clone(),
1342 this,
1343 args: pre,
1344 })
1345 })))
1346 }
1347 "toString" => Ok(Some(with_host(|h| {
1348 let s = h.str_of(recv);
1349 h.new_str(s)
1350 }))),
1351 _ => Ok(None),
1352 }
1353}
1354
1355fn is_function_method(name: &str) -> bool {
1356 matches!(name, "call" | "apply" | "bind" | "toString")
1357}
1358fn is_map_method(name: &str) -> bool {
1359 matches!(
1360 name,
1361 "get" | "set" | "has" | "delete" | "clear" | "forEach" | "keys" | "values" | "entries"
1362 )
1363}
1364fn is_set_method(name: &str) -> bool {
1365 matches!(
1366 name,
1367 "add" | "has" | "delete" | "clear" | "forEach" | "keys" | "values" | "entries"
1368 )
1369}
1370fn is_generator_method(name: &str) -> bool {
1371 matches!(name, "next" | "return" | "throw")
1372}
1373
1374fn function_property(recv: &Value, name: &str) -> Value {
1377 if with_host(|h| h.kind_of(recv)) == Some(ObjKind::Class) {
1379 if let Some(v) = with_host(|h| h.class_static(recv, name)) {
1380 return v;
1381 }
1382 if let Some(anc) = with_host(|h| h.class_builtin_ancestor(recv)) {
1387 if let Ok(v) = get_property(&anc, name) {
1388 if !matches!(v, Value::Undef) {
1389 return v;
1390 }
1391 }
1392 }
1393 } else if let Some(v) = with_host(|h| h.fn_prop(recv, name)) {
1394 return v;
1395 }
1396 if let Some(v) = with_host(|h| host::lookup_chain(h, recv, name)) {
1400 return v;
1401 }
1402 match name {
1403 "name" => with_host(|h| {
1404 let n = h.callable_name(recv);
1405 h.new_str(n)
1406 }),
1407 "length" => Value::Float(with_host(|h| h.func_arity(recv)) as f64),
1408 "prototype" => ensure_fn_prototype(recv),
1409 _ if is_function_method(name) => bound_method(recv, name),
1410 _ => Value::Undef,
1411 }
1412}
1413
1414fn ensure_fn_prototype(recv: &Value) -> Value {
1418 if let Some(p) = with_host(|h| h.fn_prop(recv, "prototype")) {
1419 return p;
1420 }
1421 if with_host(|h| h.kind_of(recv)) != Some(ObjKind::Func) {
1424 return Value::Undef;
1425 }
1426 if !with_host(|h| h.owns_prototype(recv)) {
1427 return Value::Undef;
1428 }
1429 with_host(|h| {
1430 let proto = h.new_object(IndexMap::new());
1431 if let Some(JsObj::Object(p)) = h.get_mut(&proto) {
1432 p.insert("constructor".to_string(), recv.clone());
1433 }
1434 h.hide_prop(&proto, "constructor");
1435 h.set_fn_prop(recv, "prototype", proto.clone());
1436 proto
1437 })
1438}
1439
1440pub fn namespace_property(ns: &str, name: &str) -> Value {
1443 if ns == REQUIRE_CACHE {
1447 return crate::module::cache_get(name).unwrap_or(Value::Undef);
1448 }
1449 if ns == "require" && name == "cache" {
1452 return with_host(|h| h.alloc(JsObj::Builtin(REQUIRE_CACHE.to_string())));
1453 }
1454 let konst = match (ns, name) {
1456 ("Math", "PI") => Some(std::f64::consts::PI),
1457 ("Math", "E") => Some(std::f64::consts::E),
1458 ("Math", "LN2") => Some(std::f64::consts::LN_2),
1459 ("Math", "LN10") => Some(std::f64::consts::LN_10),
1460 ("Math", "LOG2E") => Some(std::f64::consts::LOG2_E),
1461 ("Math", "LOG10E") => Some(std::f64::consts::LOG10_E),
1462 ("Math", "SQRT2") => Some(std::f64::consts::SQRT_2),
1463 ("Math", "SQRT1_2") => Some(std::f64::consts::FRAC_1_SQRT_2),
1464 ("Number", "MAX_SAFE_INTEGER") => Some(9007199254740991.0),
1465 ("Number", "MIN_SAFE_INTEGER") => Some(-9007199254740991.0),
1466 ("Number", "MAX_VALUE") => Some(f64::MAX),
1467 ("Number", "MIN_VALUE") => Some(f64::from_bits(1)),
1472 ("Number", "EPSILON") => Some(f64::EPSILON),
1473 ("Number", "POSITIVE_INFINITY") => Some(f64::INFINITY),
1474 ("Number", "NEGATIVE_INFINITY") => Some(f64::NEG_INFINITY),
1475 ("Number", "NaN") => Some(f64::NAN),
1476 _ => None,
1477 };
1478 if let Some(k) = konst {
1479 return Value::Float(k);
1480 }
1481 if name == "name" && is_builtin_ctor(ns) {
1485 return with_host(|h| h.new_str(ns.to_string()));
1486 }
1487 if ns == "Symbol" && host::WELL_KNOWN_SYMBOLS.contains(&name) {
1490 return with_host(|h| h.well_known_symbol(name));
1491 }
1492 if let Some(v) = crate::stdlib::constant(ns, name) {
1495 return v;
1496 }
1497 if name == "prototype" && is_builtin_ctor(ns) {
1502 if host::ERROR_NAMES.contains(&ns) {
1510 if let Some(p) = with_host(|h| {
1511 h.ensure_error_protos();
1512 host::error_proto_of(h, ns)
1513 }) {
1514 return p;
1515 }
1516 }
1517 if let Some(p) = with_host(|h| {
1522 h.ensure_native_protos();
1523 h.native_proto(ns)
1524 }) {
1525 return p;
1526 }
1527 let _ = ns;
1528 return with_host(|h| h.alloc(JsObj::Builtin(format!("{ns}.prototype"))));
1529 }
1530 if name == "prototype" {
1538 if let Some(p) = with_host(|h| h.ensure_ctor_proto(ns)) {
1539 return p;
1540 }
1541 }
1542 if let Some(ctor) = ns.strip_suffix(".prototype") {
1546 return with_host(|h| h.alloc(JsObj::Builtin(format!("@proto:{ctor}:{name}"))));
1547 }
1548 let qualified = format!("{ns}.{name}");
1549 if is_known_builtin(&qualified) {
1550 return with_host(|h| h.alloc(JsObj::Builtin(qualified)));
1551 }
1552 if let Some(v) = with_host(|h| h.builtin_static(ns, name)) {
1554 return v;
1555 }
1556 Value::Undef
1557}
1558
1559pub fn proto_method(recv: &Value, ctor_method: &str, args: Vec<Value>) -> Result<Value, String> {
1565 let (ctor, method) = ctor_method.split_once(':').unwrap_or(("", ctor_method));
1566 if ctor == "Error" && method == "toString" {
1569 let s = with_host(|h| h.error_to_string(recv)).unwrap_or_else(|| {
1570 with_host(|h| {
1571 let name = host::lookup_chain(h, recv, "name")
1572 .map(|n| h.str_of(&n))
1573 .unwrap_or_else(|| "Error".into());
1574 let msg = host::lookup_chain(h, recv, "message")
1575 .map(|m| h.str_of(&m))
1576 .unwrap_or_default();
1577 if msg.is_empty() {
1578 name
1579 } else {
1580 format!("{name}: {msg}")
1581 }
1582 })
1583 });
1584 return Ok(with_host(|h| h.new_str(s)));
1585 }
1586 if ctor == "Object" && method == "toString" {
1587 let tagged = with_host(|h| h.kind_of(recv)) == Some(ObjKind::Proxy)
1597 || with_host(|h| {
1598 host::lookup_chain(h, recv, "@@toStringTag").is_some()
1599 || host::lookup_accessor(h, recv, "@@toStringTag").is_some()
1600 });
1601 if tagged {
1602 let t = get_property(recv, "@@toStringTag")?;
1603 if let Some(s) = with_host(|h| h.as_str(&t)) {
1604 return Ok(with_host(|h| h.new_str(format!("[object {s}]"))));
1605 }
1606 }
1607 return Ok(with_host(|h| h.new_str(object_tag(h, recv))));
1608 }
1609 if ctor == "Object" && is_object_builtin_method(method) {
1613 return object_builtin_method(recv, method, args);
1614 }
1615 if ctor == "EventEmitter" {
1619 return crate::stdlib::events::instance_call(recv, method, args);
1620 }
1621 if ctor == "Buffer" && crate::stdlib::native_tag(recv).as_deref() == Some("Buffer") {
1626 return crate::stdlib::buffer::instance_call(recv, method, &args);
1627 }
1628 if ctor == "Uint8Array" || ctor == "TypedArray" {
1634 match crate::stdlib::native_tag(recv).as_deref() {
1635 Some("Buffer") => return crate::stdlib::buffer::instance_call(recv, method, &args),
1636 Some("TypedArray") => {
1637 return crate::stdlib::typedarray::instance_call(recv, method, &args)
1638 }
1639 _ => {}
1640 }
1641 }
1642 if ctor == "Array" && with_host(|h| h.kind_of(recv)) != Some(ObjKind::Array) {
1648 return array_generic(recv, method, args);
1649 }
1650 if crate::stdlib::native_tag(recv).as_deref() == Some(ctor) {
1656 return crate::stdlib::instance_call(ctor, recv, method, args);
1657 }
1658 host::call_method(recv, method, args)
1659}
1660
1661fn well_known_tag(h: &host::JsHost, v: &Value) -> Option<String> {
1674 let tag = object_brand(h, v);
1677 const NO_TAG: &[&str] = &[
1678 "Undefined",
1679 "Null",
1680 "Boolean",
1681 "Number",
1682 "String",
1683 "Array",
1684 "Function",
1685 "Object",
1686 "Date",
1687 "RegExp",
1688 "Error",
1689 ];
1690 if NO_TAG.contains(&tag.as_str()) {
1691 return None;
1692 }
1693 Some(tag)
1694}
1695
1696fn object_tag(h: &host::JsHost, v: &Value) -> String {
1702 format!("[object {}]", object_brand(h, v))
1703}
1704
1705fn object_brand(h: &host::JsHost, v: &Value) -> String {
1709 let tag: String = match v {
1710 Value::Undef => "Undefined".into(),
1711 Value::Bool(_) => "Boolean".into(),
1712 Value::Int(_) | Value::Float(_) => "Number".into(),
1713 Value::Str(_) => "String".into(),
1714 Value::Obj(_) => match h.get(v) {
1715 Some(JsObj::Null) => "Null".into(),
1716 Some(JsObj::Str(_)) => "String".into(),
1717 Some(JsObj::Array(_)) => "Array".into(),
1718 Some(JsObj::Proxy { target, .. }) => {
1724 let mut cur = target;
1725 for _ in 0..100 {
1726 match h.get(cur) {
1727 Some(JsObj::Proxy { target: t, .. }) => cur = t,
1728 _ => break,
1729 }
1730 }
1731 match h.get(cur) {
1732 Some(JsObj::Array(_)) => "Array".into(),
1733 _ => "Object".into(),
1734 }
1735 }
1736 Some(JsObj::Func(f)) => match h.funcs.get(f.def_id) {
1739 Some(d) if d.is_generator && d.is_async => "AsyncGeneratorFunction".into(),
1740 Some(d) if d.is_generator => "GeneratorFunction".into(),
1741 Some(d) if d.is_async => "AsyncFunction".into(),
1742 _ => "Function".into(),
1743 },
1744 Some(JsObj::Builtin(n)) if matches!(n.as_str(), "Math" | "JSON" | "Reflect") => {
1747 n.clone()
1748 }
1749 Some(JsObj::Class(_))
1750 | Some(JsObj::Builtin(_))
1751 | Some(JsObj::BoundFunc { .. })
1752 | Some(JsObj::BoundMethod { .. }) => "Function".into(),
1753 Some(JsObj::Generator { .. }) if h.is_async_gen_val(v) => "AsyncGenerator".into(),
1756 Some(JsObj::Generator { .. }) => "Generator".into(),
1757 Some(JsObj::RegExp(_)) => "RegExp".into(),
1758 Some(JsObj::Map { weak, .. }) => if *weak { "WeakMap" } else { "Map" }.into(),
1759 Some(JsObj::Set { weak, .. }) => if *weak { "WeakSet" } else { "Set" }.into(),
1760 Some(JsObj::Promise { .. }) => "Promise".into(),
1761 Some(JsObj::Symbol { .. }) => "Symbol".into(),
1762 Some(JsObj::BigInt(_)) => "BigInt".into(),
1763 Some(JsObj::Object(p)) => match p.get("@@native").map(|t| h.str_of(t)).as_deref() {
1766 Some("TypedArray") => p
1767 .get("@@kind")
1768 .map(|k| h.str_of(k))
1769 .unwrap_or_else(|| "Uint8Array".into()),
1770 Some("Buffer") => "Uint8Array".into(),
1771 Some(
1779 t @ ("ArrayBuffer"
1780 | "DataView"
1781 | "Date"
1782 | "WeakRef"
1783 | "FinalizationRegistry"
1784 | "TextEncoder"
1785 | "TextDecoder"
1786 | "URL"
1787 | "URLSearchParams"),
1788 ) => t.into(),
1789 _ if h.error_to_string(v).is_some() => "Error".into(),
1790 _ => "Object".into(),
1791 },
1792 _ => "Object".into(),
1793 },
1794 _ => "Object".into(),
1797 };
1798 tag
1799}
1800
1801fn b_setattr(vm: &mut VM, _: u8) -> Value {
1802 let val = vm.pop();
1803 let name = sval(&vm.pop());
1804 let recv = vm.pop();
1805 if let Err(e) = set_property(&recv, &name, val.clone()) {
1806 return abort(vm, e);
1807 }
1808 val
1809}
1810
1811fn b_named_eval(vm: &mut VM, _: u8) -> Value {
1823 let func = vm.pop();
1824 let kind = vm.pop().to_int();
1825 let key = vm.pop();
1826 let key = sval(&key);
1827 let base = match with_host(|h| h.symbol_of_key(&key)) {
1830 Some(sym) => match with_host(|h| h.get(&sym).cloned()) {
1831 Some(JsObj::Symbol {
1832 desc: Some(desc), ..
1833 }) => format!("[{desc}]"),
1834 _ => String::new(),
1835 },
1836 None => key,
1837 };
1838 let name = match kind {
1839 host::member::GET => format!("get {base}"),
1840 host::member::SET => format!("set {base}"),
1841 _ => base,
1842 };
1843 with_host(|h| {
1844 let s = h.new_str(name);
1845 h.set_fn_prop(&func, "name", s);
1846 });
1847 func
1848}
1849
1850pub fn set_property_pub(recv: &Value, name: &str, val: Value) -> Result<(), String> {
1853 set_property(recv, name, val)
1854}
1855
1856fn set_property(recv: &Value, name: &str, val: Value) -> Result<(), String> {
1857 if name.starts_with('#') && !with_host(|h| h.has_private(recv, name)) {
1861 return Err(private_brand_message(name, true));
1862 }
1863 if crate::proxy::set(recv, name, &val, recv)? {
1865 return Ok(());
1866 }
1867 if with_host(|h| h.is_global_object(recv)) && !name.starts_with("@@") {
1871 with_host(|h| h.set_name(name, val.clone()));
1872 }
1873 if name == "__proto__" && with_host(|h| h.kind_of(recv)) == Some(ObjKind::Object) {
1881 if with_host(|h| h.has_null_proto(recv)) {
1882 } else {
1884 let assignable =
1885 with_host(|h| h.is_null(&val) || matches!(h.kind_of(&val), Some(ObjKind::Object)));
1886 if assignable {
1887 with_host(|h| h.set_proto(recv, val));
1888 }
1889 return Ok(());
1890 }
1891 }
1892 if !with_host(|h| h.can_write_prop(recv, name)) {
1895 return Ok(());
1896 }
1897 if let Some((_, Some(setter))) = with_host(|h| host::lookup_accessor(h, recv, name)) {
1899 let _ = host::invoke(&setter, vec![val], Some(recv.clone()));
1900 return Ok(());
1901 }
1902 if let Some((Some(_), None)) = with_host(|h| host::lookup_accessor(h, recv, name)) {
1904 return Ok(());
1905 }
1906 if matches!(
1908 with_host(|h| h.kind_of(recv)),
1909 Some(ObjKind::Func) | Some(ObjKind::Class)
1910 ) {
1911 with_host(|h| h.set_fn_prop(recv, name, val));
1912 return Ok(());
1913 }
1914 if let Some(ns) = peek(recv, |o| match o {
1918 JsObj::Builtin(ns) => Some(ns.clone()),
1919 _ => None,
1920 }) {
1921 if ns == "process" && name == "exitCode" {
1927 return crate::stdlib::process::set_exit_code(&val);
1928 }
1929 with_host(|h| h.set_builtin_static(&ns, name, val));
1930 return Ok(());
1931 }
1932 if name == "lastIndex" {
1934 if let Some(n) = with_host(|h| match h.get(recv) {
1935 Some(JsObj::RegExp(_)) => Some(h.to_number(&val)),
1936 _ => None,
1937 }) {
1938 with_host(|h| {
1939 if let Some(JsObj::RegExp(r)) = h.get_mut(recv) {
1940 r.last_index = if n.is_finite() && n >= 0.0 {
1941 crate::utf16::U16Index::new(n as usize)
1942 } else {
1943 crate::utf16::U16Index::ZERO
1944 };
1945 }
1946 });
1947 return Ok(());
1948 }
1949 }
1950 if !name.is_empty() && name.bytes().all(|b| b.is_ascii_digit()) {
1952 let is_ta = crate::stdlib::native_tag(recv).as_deref() == Some("TypedArray");
1953 if is_ta && crate::stdlib::typedarray::elem_set(recv, name, &val)? {
1954 return Ok(());
1955 }
1956 if crate::stdlib::buffer::byte_set(recv, name, &val) {
1958 return Ok(());
1959 }
1960 }
1961 if with_host(|h| h.kind_of(recv)) == Some(ObjKind::Array)
1963 && name != "length"
1964 && name.parse::<usize>().is_err()
1965 {
1966 with_host(|h| h.set_fn_prop(recv, name, val));
1967 return Ok(());
1968 }
1969 let new_len = if name == "length" && with_host(|h| h.kind_of(recv)) == Some(ObjKind::Array) {
1973 Some(host::to_array_length(&val)?)
1974 } else {
1975 None
1976 };
1977 with_host(|h| match h.get_mut(recv) {
1978 Some(JsObj::Object(props)) => {
1979 let is_new = !props.contains_key(name);
1982 props.insert(name.to_string(), val);
1983 if is_new && host::array_index(name).is_some() {
1984 host::canonicalize_own_keys(props);
1985 }
1986 }
1987 Some(JsObj::Array(items)) => {
1988 if let Some(n) = new_len {
1989 let old = items.len();
1992 items.resize(n, Value::Undef);
1993 if n > old {
1994 h.mark_hole_range(recv, old..n);
1995 } else {
1996 h.truncate_holes(recv, n);
1997 }
1998 } else if let Ok(i) = name.parse::<usize>() {
1999 let old = items.len();
2001 if i >= old {
2002 items.resize(i + 1, Value::Undef);
2003 }
2004 items[i] = val;
2005 if i > old {
2006 h.mark_hole_range(recv, old..i);
2007 }
2008 h.clear_hole(recv, i);
2013 }
2014 }
2015 _ => {}
2016 });
2017 Ok(())
2018}
2019
2020fn b_getitem(vm: &mut VM, _: u8) -> Value {
2021 let idx = vm.pop();
2022 let recv = vm.pop();
2023 let key = match host::to_property_key(&idx) {
2024 Ok(k) => k,
2025 Err(e) => return abort(vm, e),
2026 };
2027 match get_property(&recv, &key) {
2028 Ok(v) => v,
2029 Err(e) => abort(vm, e),
2030 }
2031}
2032
2033fn b_setitem(vm: &mut VM, _: u8) -> Value {
2034 let val = vm.pop();
2035 let idx = vm.pop();
2036 let recv = vm.pop();
2037 let key = match host::to_property_key(&idx) {
2038 Ok(k) => k,
2039 Err(e) => return abort(vm, e),
2040 };
2041 if let Err(e) = set_property(&recv, &key, val.clone()) {
2042 return abort(vm, e);
2043 }
2044 val
2045}
2046
2047pub fn delete_property(recv: &Value, key: &str) -> Result<bool, String> {
2053 if let Some(b) = crate::proxy::delete(recv, key)? {
2056 return Ok(b);
2057 }
2058 if peek(recv, |o| match o {
2061 JsObj::Builtin(ns) => Some(ns == REQUIRE_CACHE),
2062 _ => None,
2063 }) == Some(true)
2064 {
2065 return Ok(crate::module::cache_delete(key));
2066 }
2067 if !with_host(|h| h.prop_attrs(recv, key).configurable) {
2068 return Ok(false);
2069 }
2070 with_host(|h| {
2071 let index = key.parse::<usize>();
2072 match h.get_mut(recv) {
2073 Some(JsObj::Object(props)) => {
2074 props.shift_remove(key);
2075 return;
2076 }
2077 Some(JsObj::Array(items)) => {
2078 if let Ok(i) = index {
2079 if i < items.len() {
2080 items[i] = Value::Undef;
2083 h.mark_hole(recv, i);
2084 }
2085 return;
2086 }
2087 }
2088 _ => {}
2089 }
2090 h.remove_fn_prop(recv, key);
2093 });
2094 Ok(true)
2095}
2096
2097fn b_delitem(vm: &mut VM, _: u8) -> Value {
2098 let idx = vm.pop();
2099 let recv = vm.pop();
2100 let key = match host::to_property_key(&idx) {
2104 Ok(k) => k,
2105 Err(e) => return abort(vm, e),
2106 };
2107 match delete_property(&recv, &key) {
2108 Ok(b) => Value::Bool(b),
2109 Err(e) => abort(vm, e),
2110 }
2111}
2112
2113fn b_delprop_name(vm: &mut VM, _: u8) -> Value {
2114 let name = sval(&vm.pop());
2115 let recv = vm.pop();
2116 match delete_property(&recv, &name) {
2117 Ok(b) => Value::Bool(b),
2118 Err(e) => abort(vm, e),
2119 }
2120}
2121
2122fn b_mkstr(vm: &mut VM, argc: u8) -> Value {
2125 let parts = pop_n(vm, argc as usize);
2126 let s: String = with_host(|h| parts.iter().map(|p| h.str_of(p)).collect());
2127 with_host(|h| h.new_str(s))
2128}
2129
2130fn b_mkarr(vm: &mut VM, argc: u8) -> Value {
2131 let items = pop_n(vm, argc as usize);
2132 with_host(|h| h.new_array(items))
2133}
2134
2135fn b_mark_hole(vm: &mut VM, _: u8) -> Value {
2140 let idx = vm.pop();
2141 let arr = vm.pop();
2142 let i = match idx {
2143 Value::Int(i) if i >= 0 => i as usize,
2144 _ => return Value::Undef,
2145 };
2146 with_host(|h| h.mark_hole(&arr, i));
2147 Value::Undef
2148}
2149
2150fn b_mkobj(vm: &mut VM, argc: u8) -> Value {
2151 let flat = pop_n(vm, argc as usize);
2152 let mut props: IndexMap<String, Value> = IndexMap::new();
2153 let mut proto_override: Option<Value> = None;
2155 let mut i = 0;
2156 while i + 2 < flat.len() || (i + 2 == flat.len() && flat.len() % 3 == 0 && i < flat.len()) {
2157 if i + 2 >= flat.len() {
2158 break;
2159 }
2160 if matches!(flat[i], Value::Int(2)) {
2166 let key = with_host(|h| h.str_of(&flat[i + 1]));
2167 props
2168 .entry(format!("{}{key}", host::ORD_MARKER))
2169 .or_insert(Value::Undef);
2170 i += 3;
2171 continue;
2172 }
2173 let spread = matches!(flat[i], Value::Int(1));
2174 if spread {
2175 let src = flat[i + 1].clone();
2176 if let Some(s) = with_host(|h| h.as_str(&src)) {
2185 for idx in 0..crate::utf16::len(&s) {
2186 if let Ok(ch) = get_property(&src, &idx.to_string()) {
2187 props.insert(idx.to_string(), ch);
2188 }
2189 }
2190 i += 3;
2191 continue;
2192 }
2193 let entries = host::own_enum_entries_deep(&src);
2198 for (k, v) in entries {
2199 props.insert(k, v);
2200 }
2201 for (k, v) in with_host(|h| h.own_symbol_entries(&src)) {
2204 props.insert(k, v);
2205 }
2206 } else {
2207 let key = with_host(|h| h.str_of(&flat[i + 1]));
2208 if key == "__proto__" {
2209 proto_override = Some(flat[i + 2].clone());
2210 } else {
2211 props.insert(key, flat[i + 2].clone());
2212 }
2213 }
2214 i += 3;
2215 }
2216 with_host(|h| {
2217 let o = h.new_object(props);
2218 if let Some(p) = proto_override {
2219 if matches!(p, Value::Obj(_)) {
2220 h.set_proto(&o, p);
2221 }
2222 }
2223 o
2224 })
2225}
2226
2227fn b_mkfunc(vm: &mut VM, _: u8) -> Value {
2228 let def_id = match vm.pop() {
2229 Value::Int(n) => n as usize,
2230 Value::Float(f) => f as usize,
2231 _ => return abort(vm, "internal: MKFUNC id".into()),
2232 };
2233 let (is_arrow, self_name) = with_host(|h| match h.funcs.get(def_id) {
2234 Some(d) => (
2235 d.is_arrow,
2236 (d.self_name && !d.name.is_empty()).then(|| d.name.clone()),
2237 ),
2238 None => (false, None),
2239 });
2240 with_host(|h| {
2241 let mut env = h.current_env_capture();
2242 let this = h.current_this();
2243 if self_name.is_some() {
2247 env = host::child_env(env);
2248 }
2249 let f = h.alloc(JsObj::Func(FuncVal {
2250 def_id,
2251 env: Some(env.clone()),
2252 this,
2253 is_arrow,
2254 home_class: None,
2255 }));
2256 if let Some(n) = self_name {
2257 env.borrow_mut().vars.insert(n, f.clone());
2258 }
2259 f
2260 })
2261}
2262
2263fn b_truthy(vm: &mut VM, _: u8) -> Value {
2266 let v = vm.pop();
2267 Value::Bool(with_host(|h| h.truthy(&v)))
2268}
2269
2270fn b_nullish(vm: &mut VM, _: u8) -> Value {
2271 let v = vm.pop();
2272 Value::Bool(with_host(|h| h.is_nullish(&v)))
2273}
2274
2275fn b_tostr(vm: &mut VM, _: u8) -> Value {
2276 let v = vm.pop();
2277 match host::to_string_value(&v) {
2280 Ok(s) => s,
2281 Err(e) => abort(vm, e),
2282 }
2283}
2284
2285fn b_typeof(vm: &mut VM, _: u8) -> Value {
2286 let v = vm.pop();
2287 with_host(|h| {
2288 let t = h.type_of(&v);
2289 h.new_str(t)
2290 })
2291}
2292
2293fn b_typeof_name(vm: &mut VM, _: u8) -> Value {
2296 let name = sval(&vm.pop());
2297 if let Some(v) = with_host(|h| h.read_name(&name)) {
2299 return with_host(|h| {
2300 let t = h.type_of(&v);
2301 h.new_str(t)
2302 });
2303 }
2304 let t = match name.as_str() {
2308 "undefined" => "undefined".to_string(),
2309 "NaN" | "Infinity" => "number".to_string(),
2310 "globalThis" | "global" => "object".to_string(),
2311 n if is_namespace(n) || is_known_builtin(n) => {
2312 let v = with_host(|h| h.alloc(JsObj::Builtin(name.clone())));
2313 with_host(|h| h.type_of(&v)).to_string()
2314 }
2315 _ => "undefined".to_string(), };
2317 with_host(|h| h.new_str(t))
2318}
2319
2320fn b_strict_eq(vm: &mut VM, _: u8) -> Value {
2321 let b = vm.pop();
2322 let a = vm.pop();
2323 Value::Bool(with_host(|h| h.strict_eq(&a, &b)))
2324}
2325
2326fn b_loose_eq(vm: &mut VM, _: u8) -> Value {
2327 let b = vm.pop();
2328 let a = vm.pop();
2329 let (a, b) = match with_host(|h| (host::is_primitive(h, &a), host::is_primitive(h, &b))) {
2333 (false, true) if coerces_against_object(&b) => match host::to_primitive(&a, "default") {
2334 Ok(p) => (p, b),
2335 Err(e) => return abort(vm, e),
2336 },
2337 (true, false) if coerces_against_object(&a) => match host::to_primitive(&b, "default") {
2338 Ok(p) => (a, p),
2339 Err(e) => return abort(vm, e),
2340 },
2341 _ => (a, b),
2342 };
2343 Value::Bool(with_host(|h| h.loose_eq(&a, &b)))
2344}
2345
2346fn b_instanceof(vm: &mut VM, _: u8) -> Value {
2347 let ctor = vm.pop();
2348 let obj = vm.pop();
2349 match host::instance_of(&obj, &ctor) {
2350 Ok(b) => Value::Bool(b),
2351 Err(e) => abort(vm, e),
2352 }
2353}
2354
2355fn b_binop(vm: &mut VM, _: u8) -> Value {
2358 let b = vm.pop();
2359 let a = vm.pop();
2360 let tag = match vm.pop() {
2361 Value::Int(n) => n,
2362 _ => 0,
2363 };
2364 let r = host::to_primitive(&a, "number")
2367 .and_then(|a| host::to_primitive(&b, "number").map(|b| (a, b)))
2368 .and_then(|(a, b)| with_host(|h| h.bitwise(tag, &a, &b)));
2369 finish(vm, r)
2370}
2371
2372fn b_unary(vm: &mut VM, _: u8) -> Value {
2373 let v = vm.pop();
2374 let tag = match vm.pop() {
2375 Value::Int(n) => n,
2376 _ => 0,
2377 };
2378 if with_host(|h| h.is_bigint_val(&v)) {
2381 return match tag {
2382 host::unop::POS => abort(
2383 vm,
2384 host::type_error("Cannot convert a BigInt value to a number"),
2385 ),
2386 host::unop::BITNOT => {
2387 let b = with_host(|h| h.as_bigint(&v)).unwrap();
2388 let r = -(b + num_bigint::BigInt::from(1));
2389 with_host(|h| h.new_bigint(r))
2390 }
2391 _ => Value::Undef,
2392 };
2393 }
2394 let n = match host::to_number_value(&v) {
2397 Ok(n) => n,
2398 Err(e) => return abort(vm, e),
2399 };
2400 match tag {
2401 host::unop::POS => Value::Float(n),
2402 host::unop::BITNOT => {
2403 let i = if n.is_finite() {
2404 n.trunc() as i64 as i32
2405 } else {
2406 0
2407 };
2408 Value::Float(!i as f64)
2409 }
2410 _ => Value::Undef,
2411 }
2412}
2413
2414fn b_contains(vm: &mut VM, _: u8) -> Value {
2417 let container = vm.pop();
2418 let key = vm.pop();
2419 if !matches!(container, Value::Obj(_)) {
2422 let (k, c) = with_host(|h| (h.property_key(&key), h.str_of(&container)));
2423 return abort(
2424 vm,
2425 host::type_error(&format!(
2426 "Cannot use 'in' operator to search for '{k}' in {c}"
2427 )),
2428 );
2429 }
2430 let k = with_host(|h| h.property_key(&key));
2431 match has_property(&container, &k) {
2432 Ok(b) => Value::Bool(b),
2433 Err(e) => abort(vm, e),
2434 }
2435}
2436
2437fn b_sig_return(vm: &mut VM, _: u8) -> Value {
2440 let v = vm.pop();
2441 with_host(|h| h.signal = Some(host::Signal::Return(v.clone())));
2442 vm.ip = vm.chunk.ops.len();
2443 v
2444}
2445
2446fn b_sig_break(vm: &mut VM, _: u8) -> Value {
2450 let label = sval(&vm.pop());
2451 let label = (!label.is_empty()).then_some(label);
2452 with_host(|h| h.signal = Some(host::Signal::Break(label)));
2453 vm.ip = vm.chunk.ops.len();
2454 Value::Undef
2455}
2456
2457fn b_sig_continue(vm: &mut VM, _: u8) -> Value {
2459 let label = sval(&vm.pop());
2460 let label = (!label.is_empty()).then_some(label);
2461 with_host(|h| h.signal = Some(host::Signal::Continue(label)));
2462 vm.ip = vm.chunk.ops.len();
2463 Value::Undef
2464}
2465
2466fn b_sig_unwind(vm: &mut VM, _: u8) -> Value {
2478 let cont_tag = sval(&vm.pop());
2479 let brk_tag = sval(&vm.pop());
2480 let sig = match with_host(|h| h.signal.clone()) {
2481 Some(s) => s,
2482 None => return Value::Int(host::unwind::NONE),
2483 };
2484 let propagate = |vm: &mut VM| {
2486 vm.ip = vm.chunk.ops.len();
2487 Value::Int(host::unwind::NONE)
2488 };
2489 match &sig {
2490 host::Signal::Return(_) => propagate(vm),
2491 host::Signal::Break(label) => {
2492 if brk_tag == host::unwind::NO_LOOP {
2493 return propagate(vm);
2494 }
2495 let mine = match label {
2496 None => true, Some(l) => brk_tag == *l,
2498 };
2499 if mine {
2500 with_host(|h| h.signal = None);
2501 }
2502 Value::Int(host::unwind::BREAK)
2505 }
2506 host::Signal::Continue(label) => {
2507 let mine = match label {
2508 None => cont_tag != host::unwind::NO_LOOP,
2511 Some(l) => cont_tag == *l,
2512 };
2513 if mine {
2514 with_host(|h| h.signal = None);
2515 return Value::Int(host::unwind::CONTINUE);
2516 }
2517 if brk_tag == host::unwind::NO_LOOP {
2518 return propagate(vm);
2519 }
2520 Value::Int(host::unwind::BREAK)
2523 }
2524 }
2525}
2526
2527fn b_throw(vm: &mut VM, _: u8) -> Value {
2528 let v = vm.pop();
2529 let msg = with_host(|h| {
2530 h.exc = Some(v.clone());
2531 error_display(h, &v)
2533 });
2534 abort(vm, msg)
2535}
2536
2537fn error_display(h: &host::JsHost, v: &Value) -> String {
2538 if let Some(JsObj::Object(props)) = h.get(v) {
2539 let name = props
2540 .get("name")
2541 .map(|x| h.str_of(x))
2542 .unwrap_or_else(|| "Error".into());
2543 if let Some(m) = props.get("message") {
2544 return format!("Uncaught {name}: {}", h.str_of(m));
2545 }
2546 }
2547 format!("Uncaught {}", h.str_of(v))
2548}
2549
2550fn b_try(vm: &mut VM, _: u8) -> Value {
2551 let id = match vm.pop() {
2552 Value::Int(n) => n as usize,
2553 _ => return abort(vm, "internal: TRY id".into()),
2554 };
2555 let (has_handler, catch_bind, has_finalizer) = match with_host(|h| h.try_shape(id)) {
2559 Some(t) => t,
2560 None => return abort(vm, "internal: unknown try id".into()),
2561 };
2562 let mut pending: Option<String> = None;
2563 let scope = with_host(|h| h.scope_snapshot());
2567
2568 with_host(|h| h.push_scope()); let body_res = host::run_chunk_keyed(host::try_key(id, 0), || {
2570 with_host(|h| h.try_chunk(id, 0)).expect("try block exists")
2571 });
2572 with_host(|h| h.restore_scope(scope.clone()));
2573 let signal_after = with_host(|h| h.signal.is_some());
2574 if let Err(e) = body_res {
2575 if signal_after {
2576 pending = Some(e);
2577 } else if has_handler {
2578 let thrown =
2580 with_host(|h| h.exc.clone()).unwrap_or_else(|| with_host(|h| synth_error(h, &e)));
2581 with_host(|h| {
2582 h.error = None;
2583 h.exc = None;
2584 });
2585 with_host(|h| h.push_scope());
2587 if let Some(name) = &catch_bind {
2588 with_host(|h| h.declare_name(name, thrown));
2589 }
2590 let hres = host::run_chunk_keyed(host::try_key(id, 1), || {
2591 with_host(|h| h.try_chunk(id, 1)).expect("handler exists")
2592 });
2593 with_host(|h| h.restore_scope(scope.clone()));
2594 if let Err(e2) = hres {
2595 pending = Some(e2);
2596 }
2597 } else {
2598 pending = Some(e);
2599 }
2600 }
2601
2602 if has_finalizer {
2604 let sig_before = with_host(|h| h.signal.take());
2605 with_host(|h| h.push_scope()); let fres = host::run_chunk_keyed(host::try_key(id, 2), || {
2607 with_host(|h| h.try_chunk(id, 2)).expect("finalizer exists")
2608 });
2609 with_host(|h| h.restore_scope(scope.clone()));
2610 match fres {
2611 Ok(_) => {
2612 if with_host(|h| h.signal.is_none()) {
2613 with_host(|h| h.signal = sig_before);
2616 } else {
2617 pending = None;
2622 with_host(|h| {
2623 h.error = None;
2624 h.exc = None;
2625 });
2626 }
2627 }
2628 Err(e) => pending = Some(e),
2629 }
2630 }
2631
2632 if let Some(e) = pending {
2633 return abort(vm, e);
2634 }
2635 Value::Undef
2636}
2637
2638pub(crate) fn synth_error(h: &mut host::JsHost, e: &str) -> Value {
2641 h.ensure_error_protos();
2642 let (head, rest) = match e.split_once(": ") {
2645 Some((n, m)) => (n, m.to_string()),
2646 None => ("", e.to_string()),
2647 };
2648 let (base, code) = match head.split_once(" [") {
2649 Some((n, c)) if c.ends_with(']') => (n, Some(c[..c.len() - 1].to_string())),
2650 _ => (head, None),
2651 };
2652 let (name, mut message) = if host::ERROR_NAMES.contains(&base) {
2653 (base.to_string(), rest)
2654 } else {
2655 ("Error".to_string(), e.to_string())
2656 };
2657 let mut code = code;
2663 let mut bracketed = code.is_some();
2666 if let Some(rest) = message.strip_prefix(host::CODE_MARK) {
2667 if let Some((c, m)) = rest.split_once('\u{1}') {
2668 code = Some(c.to_string());
2669 bracketed = false;
2670 message = m.to_string();
2671 }
2672 }
2673 let mut props: IndexMap<String, Value> = IndexMap::new();
2674 let mv = h.new_str(message.clone());
2675 props.insert("message".into(), mv);
2676 if let Some(c) = &code {
2677 let cv = h.new_str(c.clone());
2678 props.insert("code".into(), cv);
2679 if bracketed {
2680 props.insert("@@nodeError".into(), Value::Bool(true));
2683 }
2684 }
2685 let label = match (&code, bracketed) {
2686 (Some(c), true) => format!("{name} [{c}]"),
2687 _ => name.clone(),
2688 };
2689 let frames = h.stack_frames();
2690 let stack = if message.is_empty() {
2691 format!("{label}{frames}")
2692 } else {
2693 format!("{label}: {message}{frames}")
2694 };
2695 let sv = h.new_str(stack);
2696 props.insert("stack".into(), sv);
2697 for (k, v) in syscall_error_fields(&message) {
2703 let sv = match v {
2704 SysField::Str(s) => h.new_str(s),
2705 SysField::Num(n) => Value::Float(n),
2706 };
2707 props.insert(k.into(), sv);
2708 }
2709 let obj = h.new_object(props);
2710 if let Some(p) = host::error_proto_of(h, &name) {
2711 h.set_proto(&obj, p);
2712 }
2713 h.hide_prop(&obj, "message");
2716 h.hide_prop(&obj, "stack");
2717 obj
2718}
2719
2720enum SysField {
2721 Str(String),
2722 Num(f64),
2723}
2724
2725fn syscall_error_fields(message: &str) -> Vec<(&'static str, SysField)> {
2729 let (code, rest) = match message.split_once(": ") {
2730 Some((c, r))
2731 if c.len() >= 2
2732 && c.starts_with('E')
2733 && c.bytes()
2734 .all(|b| b.is_ascii_uppercase() || b.is_ascii_digit()) =>
2735 {
2736 (c, r)
2737 }
2738 _ => return Vec::new(),
2739 };
2740 let mut out: Vec<(&'static str, SysField)> = vec![
2741 ("errno", SysField::Num(errno_for(code))),
2742 ("code", SysField::Str(code.to_string())),
2743 ];
2744 if let Some((_, tail)) = rest.split_once(", ") {
2746 let (syscall, path) = match tail.split_once(" '") {
2747 Some((s, p)) => (s, p.strip_suffix('\'')),
2748 None => (tail, None),
2749 };
2750 out.push(("syscall", SysField::Str(syscall.to_string())));
2751 if let Some(p) = path {
2752 out.push(("path", SysField::Str(p.to_string())));
2753 }
2754 }
2755 out
2756}
2757
2758fn errno_for(code: &str) -> f64 {
2762 let n: i32 = match code {
2763 "ENOENT" => 2,
2764 "EACCES" => 13,
2765 "EEXIST" => 17,
2766 "ENOTDIR" => 20,
2767 "EISDIR" => 21,
2768 "EINVAL" => 22,
2769 "EPIPE" => 32,
2770 "ENOTEMPTY" => 66,
2771 _ => 5, };
2773 -f64::from(n)
2774}
2775
2776fn b_getiter(vm: &mut VM, _: u8) -> Value {
2779 let v = vm.pop();
2780 if with_host(|h| h.is_generator_val(&v)) {
2782 return v;
2783 }
2784 if with_host(|h| h.kind_of(&v)) == Some(ObjKind::Proxy) {
2787 return match crate::proxy::iterate(&v) {
2788 Ok(Some(items)) => with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })),
2789 Ok(None) => abort(vm, "internal: kind_of said Proxy".into()),
2790 Err(e) => abort(vm, e),
2791 };
2792 }
2793 if let Some(iter_fn) = with_host(|h| host::lookup_chain(h, &v, "@@iterator")) {
2795 if with_host(|h| host::is_callable(h, &iter_fn)) {
2796 return match host::invoke(&iter_fn, Vec::new(), Some(v.clone())) {
2797 Ok(it) => it,
2798 Err(e) => abort(vm, e),
2799 };
2800 }
2801 }
2802 match with_host(|h| h.iter_vec(&v)) {
2803 Ok(items) => with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })),
2804 Err(e) => abort(vm, e),
2805 }
2806}
2807
2808fn b_forin_keys(vm: &mut VM, _: u8) -> Value {
2809 let v = vm.pop();
2810 if with_host(|h| h.kind_of(&v)) == Some(ObjKind::Proxy) {
2814 return match crate::proxy::own_enum_string_keys(&v) {
2815 Ok(keys) => with_host(|h| {
2816 let out: Vec<Value> = keys.into_iter().map(|k| h.new_str(k)).collect();
2817 h.new_array(out)
2818 }),
2819 Err(e) => abort(vm, e),
2820 };
2821 }
2822 let keys = with_host(|h| h.enum_keys(&v));
2823 with_host(|h| h.new_array(keys))
2824}
2825
2826fn b_foriter(vm: &mut VM, _: u8) -> Value {
2827 let it = match vm.stack.last() {
2828 Some(v) => v.clone(),
2829 None => return abort(vm, "internal: FORITER with empty stack".into()),
2830 };
2831 let eager = with_host(|h| {
2833 if let Some(JsObj::Iter { items, idx }) = h.get_mut(&it) {
2834 if *idx < items.len() {
2835 let v = items[*idx].clone();
2836 *idx += 1;
2837 return Some(Some(v));
2838 }
2839 return Some(None);
2840 }
2841 None
2842 });
2843 if let Some(step) = eager {
2844 return match step {
2845 Some(v) => {
2846 vm.push(v);
2847 Value::Bool(true)
2848 }
2849 None => Value::Bool(false),
2850 };
2851 }
2852 if with_host(|h| h.is_generator_val(&it)) {
2854 return match host::gen_resume(&it, Value::Undef) {
2855 Ok(host::GenStep::Yield(v)) => {
2856 vm.push(v);
2857 Value::Bool(true)
2858 }
2859 Ok(host::GenStep::Done(_)) => Value::Bool(false),
2860 Err(e) => abort(vm, e),
2861 };
2862 }
2863 match host::call_method(&it, "next", Vec::new()) {
2865 Ok(step) => {
2866 let done = get_property(&step, "done")
2867 .map(|d| with_host(|h| h.truthy(&d)))
2868 .unwrap_or(true);
2869 if done {
2870 Value::Bool(false)
2871 } else {
2872 match get_property(&step, "value") {
2873 Ok(v) => {
2874 vm.push(v);
2875 Value::Bool(true)
2876 }
2877 Err(e) => abort(vm, e),
2878 }
2879 }
2880 }
2881 Err(e) => abort(vm, e),
2882 }
2883}
2884
2885fn b_unpack(vm: &mut VM, _: u8) -> Value {
2886 let star = match vm.pop() {
2887 Value::Int(n) => n,
2888 _ => -1,
2889 };
2890 let count = match vm.pop() {
2891 Value::Int(n) => n as usize,
2892 _ => 0,
2893 };
2894 let iterable = vm.pop();
2895 let items = match host::iter_all(&iterable) {
2896 Ok(v) => v,
2897 Err(e) => return abort(vm, e),
2898 };
2899 let ordered: Vec<Value> = if star < 0 {
2900 (0..count)
2901 .map(|i| items.get(i).cloned().unwrap_or(Value::Undef))
2902 .collect()
2903 } else {
2904 let si = star as usize;
2905 let after = count.saturating_sub(si + 1);
2906 let rest_end = items.len().saturating_sub(after).max(si);
2907 let mut out: Vec<Value> = Vec::with_capacity(count);
2908 for i in 0..si {
2909 out.push(items.get(i).cloned().unwrap_or(Value::Undef));
2910 }
2911 let rest: Vec<Value> = items
2912 .get(si..rest_end)
2913 .map(|s| s.to_vec())
2914 .unwrap_or_default();
2915 out.push(with_host(|h| h.new_array(rest)));
2916 for j in 0..after {
2917 out.push(items.get(rest_end + j).cloned().unwrap_or(Value::Undef));
2918 }
2919 out
2920 };
2921 if ordered.is_empty() {
2922 return Value::Undef;
2923 }
2924 for it in ordered[1..].iter().rev().cloned() {
2925 vm.push(it);
2926 }
2927 ordered[0].clone()
2928}
2929
2930fn b_build_args(vm: &mut VM, argc: u8) -> Value {
2931 let flat = pop_n(vm, argc as usize);
2932 let mut out = Vec::new();
2933 let mut holes: rustc_hash::FxHashSet<usize> = rustc_hash::FxHashSet::default();
2939 let mut i = 0;
2940 while i + 1 < flat.len() {
2941 let val = flat[i + 1].clone();
2942 match flat[i] {
2943 Value::Int(1) => match host::iter_all(&val) {
2944 Ok(items) => out.extend(items),
2945 Err(e) => return abort(vm, e),
2946 },
2947 Value::Int(2) => {
2948 holes.insert(out.len());
2949 out.push(Value::Undef);
2950 }
2951 _ => out.push(val),
2952 }
2953 i += 2;
2954 }
2955 with_host(|h| {
2956 let arr = h.new_array(out);
2957 h.install_holes(&arr, holes);
2958 arr
2959 })
2960}
2961
2962fn b_call(vm: &mut VM, argc: u8) -> Value {
2965 let mut args = pop_n(vm, argc as usize);
2966 let name = sval(&args.remove(0));
2967 let r = host::call_named(&name, args);
2968 let r = r.map_err(|e| {
2972 let shown = global_binding(&name)
2973 .map(|v| with_host(|h| h.str_of(&v)))
2974 .unwrap_or_default();
2975 host::name_call_site(vm, &shown, e)
2976 });
2977 finish(vm, r)
2978}
2979
2980fn call_key_of(v: &Value) -> String {
2990 if let Value::Str(s) = v {
2991 return (**s).clone();
2992 }
2993 with_host(|h| h.str_of(v))
2994}
2995
2996fn index_element_call(recv: &Value, name: &str, args: &[Value]) -> Option<Result<Value, String>> {
2997 if name.is_empty() || !name.bytes().all(|b| b.is_ascii_digit()) {
2998 return None;
2999 }
3000 let f = get_property(recv, name).ok()?;
3001 with_host(|h| host::is_callable(h, &f))
3002 .then(|| host::invoke(&f, args.to_vec(), Some(recv.clone())))
3003}
3004
3005fn b_call_method(vm: &mut VM, argc: u8) -> Value {
3006 let mut args = pop_n(vm, argc as usize);
3007 let recv = args.remove(0);
3008 let name = call_key_of(&args.remove(0));
3009 if let Some(r) = index_element_call(&recv, &name, &args) {
3010 return finish(vm, r);
3011 }
3012 let r = host::call_method(&recv, &name, args);
3013 let r = r.map_err(|e| host::name_call_site(vm, &name, e));
3017 finish(vm, r)
3018}
3019
3020fn b_call_value(vm: &mut VM, argc: u8) -> Value {
3021 let mut args = pop_n(vm, argc as usize);
3022 let callable = args.remove(0);
3023 let r = host::invoke(&callable, args, None);
3024 let r = r.map_err(|e| {
3028 let shown = with_host(|h| h.str_of(&callable));
3029 host::name_call_site(vm, &shown, e)
3030 });
3031 finish(vm, r)
3032}
3033
3034fn b_new(vm: &mut VM, argc: u8) -> Value {
3035 let mut args = pop_n(vm, argc as usize);
3036 let ctor = args.remove(0);
3037 let r = host::construct(&ctor, args);
3038 let r = r.map_err(|e| {
3041 let shown = with_host(|h| h.str_of(&ctor));
3042 host::name_call_site(vm, &shown, e)
3043 });
3044 finish(vm, r)
3045}
3046
3047fn b_apply(vm: &mut VM, _: u8) -> Value {
3048 let args_arr = vm.pop();
3049 let callable = vm.pop();
3050 let args = host::iter_all(&args_arr).unwrap_or_default();
3051 let r = host::invoke(&callable, args, None);
3052 finish(vm, r)
3053}
3054
3055fn b_apply_method(vm: &mut VM, _: u8) -> Value {
3056 let args_arr = vm.pop();
3057 let name = call_key_of(&vm.pop());
3058 let recv = vm.pop();
3059 let args = host::iter_all(&args_arr).unwrap_or_default();
3060 if let Some(r) = index_element_call(&recv, &name, &args) {
3061 return finish(vm, r);
3062 }
3063 let r = host::call_method(&recv, &name, args);
3064 finish(vm, r)
3065}
3066
3067pub fn numeric_hook(op: NumOp, a: &Value, b: &Value) -> Result<Value, String> {
3078 use NumOp::*;
3079 let (a, b) = match op {
3080 Eq | Ne => {
3083 let (pa, pb) = with_host(|h| (host::is_primitive(h, a), host::is_primitive(h, b)));
3084 match (pa, pb) {
3085 (false, true) if coerces_against_object(b) => {
3086 (host::to_primitive(a, "default")?, b.clone())
3087 }
3088 (true, false) if coerces_against_object(a) => {
3089 (a.clone(), host::to_primitive(b, "default")?)
3090 }
3091 _ => (a.clone(), b.clone()),
3092 }
3093 }
3094 Add => (
3097 host::to_primitive(a, "default")?,
3098 host::to_primitive(b, "default")?,
3099 ),
3100 _ => (
3101 host::to_primitive(a, "number")?,
3102 host::to_primitive(b, "number")?,
3103 ),
3104 };
3105 reject_symbol_operand(op, &a, &b)?;
3106 with_host(|h| h.arith(op, &a, &b))
3107}
3108
3109fn reject_symbol_operand(op: NumOp, a: &Value, b: &Value) -> Result<(), String> {
3120 use NumOp::*;
3121 if matches!(op, Eq | Ne) {
3122 return Ok(());
3123 }
3124 let (sym, concat) = with_host(|h| {
3125 let is_sym = |v: &Value| matches!(h.get(v), Some(JsObj::Symbol { .. }));
3126 let is_str =
3127 |v: &Value| matches!(v, Value::Str(_)) || matches!(h.get(v), Some(JsObj::Str(_)));
3128 (is_sym(a) || is_sym(b), is_str(a) || is_str(b))
3129 });
3130 if !sym {
3131 return Ok(());
3132 }
3133 Err(host::type_error(if matches!(op, Add) && concat {
3134 "Cannot convert a Symbol value to a string"
3135 } else {
3136 "Cannot convert a Symbol value to a number"
3137 }))
3138}
3139
3140fn coerces_against_object(v: &Value) -> bool {
3145 match v {
3146 Value::Undef => false,
3147 Value::Bool(_) | Value::Int(_) | Value::Float(_) | Value::Str(_) => true,
3148 _ => with_host(|h| !h.is_null(v)),
3149 }
3150}
3151
3152fn is_namespace(name: &str) -> bool {
3156 matches!(
3157 name,
3158 "console"
3159 | "Math"
3160 | "JSON"
3161 | "Object"
3162 | "Array"
3163 | "Number"
3164 | "String"
3165 | "Boolean"
3166 | "Symbol"
3167 | "Reflect"
3168 | "Promise"
3169 | "process"
3170 | "Buffer"
3171 | "URL"
3172 | "URLSearchParams"
3173 )
3174}
3175
3176const GLOBAL_FUNCS: &[&str] = &[
3177 "parseInt",
3178 "parseFloat",
3179 "isNaN",
3180 "isFinite",
3181 "encodeURIComponent",
3182 "decodeURIComponent",
3183 "encodeURI",
3184 "decodeURI",
3185 "escape",
3188 "unescape",
3189 "eval",
3190 "String",
3191 "Number",
3192 "Boolean",
3193 "Array",
3194 "Object",
3195 "Function",
3196 "Symbol",
3197 "Map",
3198 "Set",
3199 "WeakMap",
3200 "WeakSet",
3201 "Promise",
3202 "Error",
3203 "TypeError",
3204 "RangeError",
3205 "SyntaxError",
3206 "ReferenceError",
3207 "EvalError",
3208 "URIError",
3209 "AggregateError",
3210 "BigInt",
3211 "RegExp",
3212 "Date",
3213 "ArrayBuffer",
3214 "Uint8Array",
3215 "Int8Array",
3216 "Uint8ClampedArray",
3217 "Int16Array",
3218 "Uint16Array",
3219 "Int32Array",
3220 "Uint32Array",
3221 "Float32Array",
3222 "Float64Array",
3223 "BigInt64Array",
3224 "BigUint64Array",
3225 "WeakRef",
3226 "FinalizationRegistry",
3227 "TextEncoder",
3228 "TextDecoder",
3229 "fetch",
3231 "Headers",
3232 "Request",
3233 "Response",
3234 "Blob",
3235 "File",
3236 "FormData",
3237 "AbortController",
3238 "AbortSignal",
3239 "queueMicrotask",
3240 "setTimeout",
3241 "setInterval",
3242 "setImmediate",
3243 "clearTimeout",
3244 "clearInterval",
3245 "clearImmediate",
3246 "structuredClone",
3247 "Proxy",
3248 "require",
3249 "__cjs_require",
3252 "__cjs_resolve",
3253 "__cjs_cache",
3254];
3255
3256const NS_METHODS: &[&str] = &[
3257 "console.log",
3258 "console.error",
3259 "console.warn",
3260 "console.info",
3261 "console.debug",
3262 "Math.floor",
3263 "Math.ceil",
3264 "Math.round",
3265 "Math.trunc",
3266 "Math.abs",
3267 "Math.sign",
3268 "Math.max",
3269 "Math.min",
3270 "Math.pow",
3271 "Math.sqrt",
3272 "Math.cbrt",
3273 "Math.random",
3274 "Math.hypot",
3275 "Math.clz32",
3276 "Math.fround",
3277 "Math.imul",
3278 "Math.sinh",
3279 "Math.cosh",
3280 "Math.tanh",
3281 "Math.asinh",
3282 "Math.acosh",
3283 "Math.atanh",
3284 "Math.log1p",
3285 "Math.expm1",
3286 "Math.log",
3287 "Math.log2",
3288 "Math.log10",
3289 "Math.exp",
3290 "Math.sin",
3291 "Math.cos",
3292 "Math.tan",
3293 "Math.atan",
3294 "Math.atan2",
3295 "Math.asin",
3296 "Math.acos",
3297 "JSON.stringify",
3298 "JSON.parse",
3299 "Object.keys",
3300 "Object.values",
3301 "Object.entries",
3302 "Object.assign",
3303 "Object.freeze",
3304 "Object.is",
3305 "Object.fromEntries",
3306 "Object.getPrototypeOf",
3307 "Object.setPrototypeOf",
3308 "Object.create",
3309 "Object.getOwnPropertyNames",
3310 "Object.getOwnPropertySymbols",
3311 "Object.defineProperty",
3312 "Object.getOwnPropertyDescriptor",
3313 "Object.getOwnPropertyDescriptors",
3314 "Object.defineProperties",
3315 "Object.isFrozen",
3316 "Object.isSealed",
3317 "Object.seal",
3318 "Object.preventExtensions",
3319 "Object.isExtensible",
3320 "Object.hasOwn",
3321 "Object.groupBy",
3322 "Array.isArray",
3323 "Array.from",
3324 "Array.fromAsync",
3325 "Array.of",
3326 "Number.isInteger",
3327 "Number.isNaN",
3328 "Number.isFinite",
3329 "Number.isSafeInteger",
3330 "Number.parseInt",
3331 "Number.parseFloat",
3332 "String.fromCharCode",
3333 "String.fromCodePoint",
3334 "String.raw",
3335 "Symbol.for",
3336 "Symbol.keyFor",
3337 "BigInt.asIntN",
3338 "BigInt.asUintN",
3339 "Proxy.revocable",
3340 "Reflect.ownKeys",
3341 "Reflect.has",
3342 "Reflect.get",
3343 "Reflect.set",
3344 "Reflect.getPrototypeOf",
3345 "Reflect.setPrototypeOf",
3346 "Reflect.getOwnPropertyDescriptor",
3347 "Reflect.defineProperty",
3348 "Reflect.deleteProperty",
3349 "Reflect.apply",
3350 "Reflect.construct",
3351 "Reflect.isExtensible",
3352 "Reflect.preventExtensions",
3353 "Promise.resolve",
3354 "Promise.reject",
3355 "Promise.all",
3356 "Promise.allSettled",
3357 "Promise.race",
3358 "Promise.any",
3359 "Promise.withResolvers",
3360 "Map.groupBy",
3361 "Response.json",
3362 "Response.error",
3363 "Response.redirect",
3364 "AbortSignal.abort",
3365 "AbortSignal.timeout",
3366 "process.nextTick",
3367 "Error.captureStackTrace",
3368 "require.resolve",
3369];
3370
3371pub fn is_known_builtin(name: &str) -> bool {
3372 GLOBAL_FUNCS.contains(&name)
3373 || NS_METHODS.contains(&name)
3374 || is_namespace(name)
3375 || crate::stdlib::is_method(name)
3376}
3377
3378pub fn dynamic_function(src: &str) -> Result<Value, String> {
3395 let f = crate::eval_in_global_scope(&format!("({src})"))?;
3396 with_host(|h| {
3397 let s = h.new_str(src.to_string());
3398 h.set_fn_prop(&f, "@@source", s);
3399 });
3400 Ok(f)
3401}
3402
3403pub fn function_ctor(args: &[Value]) -> Result<Value, String> {
3419 let parts: Vec<String> = args.iter().map(|a| with_host(|h| h.str_of(a))).collect();
3420 let (params, body) = match parts.split_last() {
3421 Some((body, params)) => (params.join(","), body.clone()),
3422 None => (String::new(), String::new()),
3423 };
3424 dynamic_function(&format!("function anonymous({params}\n) {{\n{body}\n}}"))
3425}
3426
3427pub fn eval_source(arg: Option<&Value>, direct: bool) -> Result<Value, String> {
3436 let v = arg.cloned().unwrap_or(Value::Undef);
3437 let is_string =
3438 matches!(v, Value::Str(_)) || with_host(|h| matches!(h.get(&v), Some(JsObj::Str(_))));
3439 if !is_string {
3440 return Ok(v);
3441 }
3442 let src = with_host(|h| h.str_of(&v));
3443 let chunk = crate::load_merged(crate::compile_completion(&src)?);
3444 if direct {
3445 host::run_chunk_on(chunk)
3446 } else {
3447 host::run_chunk_in_global_scope(chunk)
3448 }
3449}
3450
3451pub fn call_builtin_function(name: &str, args: Vec<Value>) -> Result<Value, String> {
3453 if name == "require" {
3456 let spec = with_host(|h| h.str_of(&arg0(&args)));
3457 return crate::module::require(&spec, &crate::module::entry_dir());
3458 }
3459 if name == "__cjs_require" {
3462 let spec = with_host(|h| h.str_of(&arg0(&args)));
3463 let from = with_host(|h| h.str_of(args.get(1).unwrap_or(&Value::Undef)));
3464 return crate::module::require(&spec, std::path::Path::new(&from));
3465 }
3466 if name == "require.resolve" {
3468 let spec = with_host(|h| h.str_of(&arg0(&args)));
3469 if crate::stdlib::resolve(&spec).is_some() {
3470 return Ok(with_host(|h| h.new_str(spec)));
3471 }
3472 return match crate::module::resolve(&spec, &crate::module::entry_dir()) {
3473 Some(p) => Ok(with_host(|h| h.new_str(p.to_string_lossy().to_string()))),
3474 None => Err(crate::host::plain_coded_error(
3475 "Error",
3476 "MODULE_NOT_FOUND",
3477 &format!("Cannot find module '{spec}'"),
3478 )),
3479 };
3480 }
3481 if name == "__cjs_resolve" {
3484 let spec = with_host(|h| h.str_of(&arg0(&args)));
3485 let from = with_host(|h| h.str_of(args.get(1).unwrap_or(&Value::Undef)));
3486 if crate::stdlib::resolve(&spec).is_some() {
3487 return Ok(with_host(|h| h.new_str(spec)));
3488 }
3489 return match crate::module::resolve(&spec, std::path::Path::new(&from)) {
3490 Some(p) => Ok(with_host(|h| h.new_str(p.to_string_lossy().to_string()))),
3491 None => Err(crate::host::plain_coded_error(
3492 "Error",
3493 "MODULE_NOT_FOUND",
3494 &format!("Cannot find module '{spec}'"),
3495 )),
3496 };
3497 }
3498 if name == "Error.captureStackTrace" {
3503 let target = arg0(&args);
3504 let prep = with_host(|h| h.builtin_static("Error", "prepareStackTrace"));
3505 let stack = match prep {
3506 Some(f)
3507 if matches!(
3508 with_host(|h| h.get(&f).cloned()),
3509 Some(JsObj::Func(_)) | Some(JsObj::Builtin(_)) | Some(JsObj::BoundFunc { .. })
3510 ) =>
3511 {
3512 let sites = crate::module::callsite_stack(10)?;
3513 host::invoke(&f, vec![target.clone(), sites], None)?
3514 }
3515 _ => with_host(|h| h.new_str("")),
3516 };
3517 let _ = set_property(&target, "stack", stack);
3518 return Ok(Value::Undef);
3519 }
3520 if let Some(r) = crate::stdlib::call(name, &args) {
3522 return r;
3523 }
3524 match name {
3525 "console.log" | "console.info" | "console.debug" => {
3526 print_line(&args, false);
3527 Ok(Value::Undef)
3528 }
3529 "console.error" | "console.warn" => {
3530 print_line(&args, true);
3531 Ok(Value::Undef)
3532 }
3533 "parseInt" | "Number.parseInt" => Ok(Value::Float(parse_int(&args))),
3534 "parseFloat" | "Number.parseFloat" => Ok(Value::Float(parse_float(&args))),
3535 "isNaN" => Ok(Value::Bool(arg_num(&args, 0).is_nan())),
3536 "isFinite" => Ok(Value::Bool(arg_num(&args, 0).is_finite())),
3537 "encodeURIComponent" => uri_encode(&with_host(|h| h.str_of(&arg0(&args))), false),
3538 "encodeURI" => uri_encode(&with_host(|h| h.str_of(&arg0(&args))), true),
3539 "decodeURIComponent" => uri_decode(&with_host(|h| h.str_of(&arg0(&args))), false),
3540 "decodeURI" => uri_decode(&with_host(|h| h.str_of(&arg0(&args))), true),
3541 "escape" => legacy_escape(&with_host(|h| h.str_of(&arg0(&args)))),
3542 "unescape" => legacy_unescape(&with_host(|h| h.str_of(&arg0(&args)))),
3543 "eval" => eval_source(args.first(), false),
3548 "Function" => function_ctor(&args),
3552 "Buffer" => {
3562 crate::stdlib::process::emit_deprecation_warning(
3563 "DEP0005",
3564 "Buffer() is deprecated due to security and usability issues. \
3565 Please use the Buffer.alloc(), Buffer.allocUnsafe(), or \
3566 Buffer.from() methods instead.",
3567 );
3568 crate::stdlib::construct("Buffer", &args)
3569 .unwrap_or_else(|| Err(host::type_error("Buffer is not a function")))
3570 }
3571 "Number.isInteger" => Ok(Value::Bool(is_integer(arg0(&args)))),
3572 "Number.isSafeInteger" => Ok(Value::Bool(is_safe_integer(arg0(&args)))),
3573 "Number.isNaN" => Ok(Value::Bool(
3574 matches!(arg0(&args), Value::Float(f) if f.is_nan()),
3575 )),
3576 "Number.isFinite" => Ok(Value::Bool(
3577 matches!(arg0(&args), Value::Float(f) if f.is_finite())
3578 || matches!(arg0(&args), Value::Int(_)),
3579 )),
3580 "String" => {
3581 if args.is_empty() {
3582 Ok(with_host(|h| h.new_str("")))
3583 } else {
3584 host::string_ctor_value(&args[0])
3587 }
3588 }
3589 "Number" => Ok(Value::Float(if args.is_empty() {
3590 0.0
3591 } else {
3592 host::to_number_value(&args[0])?
3594 })),
3595 "BigInt" => bigint_ctor(&arg0(&args)),
3596 "RegExp" => regexp_ctor(&args),
3597 "BigInt.asIntN" | "BigInt.asUintN" => bigint_as_n(name.ends_with("asUintN"), &args),
3598 "Boolean" => Ok(Value::Bool(with_host(|h| h.truthy(&arg0(&args))))),
3599 "String.fromCharCode" => Ok(with_host(|h| {
3603 let units: Vec<u16> = args
3604 .iter()
3605 .map(|a| crate::utf16::to_uint16(h.to_number(a)))
3606 .collect();
3607 let s = crate::utf16::to_string_lossy(&units);
3608 h.new_str(s)
3609 })),
3610 "String.fromCodePoint" => {
3613 let mut s = String::new();
3614 for a in &args {
3615 let n = with_host(|h| h.to_number(a));
3616 let cp = if n.is_finite() && n.trunc() == n && (0.0..=0x10FFFF as f64).contains(&n)
3617 {
3618 char::from_u32(n as u32)
3619 } else {
3620 None
3621 };
3622 match cp {
3623 Some(c) => s.push(c),
3624 None => {
3625 return Err(format!(
3626 "RangeError: Invalid code point {}",
3627 with_host(|h| h.str_of(a))
3628 ))
3629 }
3630 }
3631 }
3632 Ok(new_s(s))
3633 }
3634 "String.raw" => string_raw(&args),
3635 "Array" => construct_builtin("Array", args),
3637 "Array.of" => Ok(with_host(|h| h.new_array(args))),
3638 "Array.isArray" => {
3641 let v = arg0(&args);
3642 let subject = crate::proxy::ultimate_target(&v).unwrap_or(v);
3643 Ok(Value::Bool(matches!(
3644 with_host(|h| h.get(&subject).cloned()),
3645 Some(JsObj::Array(_))
3646 )))
3647 }
3648 "Array.from" => array_from(args),
3649 "Array.fromAsync" => array_from_async(args),
3650 "Object" => Ok(object_call(args)),
3651 "Object.keys" => object_keys(args, 0),
3652 "Object.values" => object_keys(args, 1),
3653 "Object.entries" => object_keys(args, 2),
3654 "Object.assign" => object_assign(args),
3655 "Object.freeze" => {
3656 let v = arg0(&args);
3657 with_host(|h| h.seal_object(&v, true));
3658 Ok(v)
3659 }
3660 "Object.seal" => {
3661 let v = arg0(&args);
3662 with_host(|h| h.seal_object(&v, false));
3663 Ok(v)
3664 }
3665 "Object.preventExtensions" => {
3666 let v = arg0(&args);
3667 if crate::proxy::prevent_extensions(&v)? {
3668 return Ok(v);
3669 }
3670 with_host(|h| h.prevent_extensions(&v));
3671 Ok(v)
3672 }
3673 "Object.isFrozen" => Ok(Value::Bool(with_host(|h| h.is_sealed(&arg0(&args), true)))),
3674 "Object.isSealed" => Ok(Value::Bool(with_host(|h| h.is_sealed(&arg0(&args), false)))),
3675 "Object.isExtensible" => {
3676 let v = arg0(&args);
3677 match crate::proxy::is_extensible(&v)? {
3678 Some(b) => Ok(Value::Bool(b)),
3679 None => Ok(Value::Bool(with_host(|h| h.is_extensible(&v)))),
3680 }
3681 }
3682 "Object.is" => {
3685 let a = arg0(&args);
3686 let b = args.get(1).cloned().unwrap_or(Value::Undef);
3687 let num = |v: &Value| match v {
3688 Value::Int(n) => Some(*n as f64),
3689 Value::Float(f) => Some(*f),
3690 _ => None,
3691 };
3692 let r = match (num(&a), num(&b)) {
3693 (Some(x), Some(y)) => {
3694 if x.is_nan() && y.is_nan() {
3695 true
3696 } else if x == 0.0 && y == 0.0 {
3697 x.is_sign_negative() == y.is_sign_negative()
3698 } else {
3699 x == y
3700 }
3701 }
3702 _ => with_host(|h| h.strict_eq(&a, &b)),
3703 };
3704 Ok(Value::Bool(r))
3705 }
3706 "Object.fromEntries" => object_from_entries(args),
3707 "Object.getPrototypeOf" | "Reflect.getPrototypeOf" => {
3710 let v = arg0(&args);
3711 match crate::proxy::get_prototype_of(&v)? {
3712 Some(p) => Ok(p),
3713 None => Ok(prototype_of(&v)),
3714 }
3715 }
3716 "Object.setPrototypeOf" => {
3717 let obj = arg0(&args);
3718 let proto = args.get(1).cloned().unwrap_or(Value::Undef);
3719 if with_host(|h| h.kind_of(&obj)) == Some(ObjKind::Proxy) {
3720 reject_bad_prototype(&proto)?;
3721 crate::proxy::set_prototype_of(&obj, &proto)?;
3722 return Ok(obj);
3723 }
3724 if with_host(|h| matches!(obj, Value::Undef) || h.is_null(&obj)) {
3730 return Err(host::type_error(
3731 "Object.setPrototypeOf called on null or undefined",
3732 ));
3733 }
3734 reject_bad_prototype(&proto)?;
3735 if with_host(|h| is_object_like(h, &obj)) {
3736 let cur = prototype_of(&obj);
3743 let same = with_host(|h| h.strict_eq(&cur, &proto));
3744 if !same && !with_host(|h| h.is_extensible(&obj)) {
3745 return Err(host::type_error("#<Object> is not extensible"));
3746 }
3747 with_host(|h| h.set_proto(&obj, proto));
3748 }
3749 Ok(obj)
3750 }
3751 "Object.create" => object_create(args),
3752 "Object.getOwnPropertyNames" => object_keys(args, 3),
3753 "Object.getOwnPropertySymbols" => {
3754 let v = arg0(&args);
3755 require_object_coercible(&v)?;
3756 let syms = proxy_or_own_symbol_keys(&v)?;
3757 Ok(with_host(|h| h.new_array(syms)))
3758 }
3759 "Object.hasOwn" => {
3761 let obj = arg0(&args);
3762 let key = args.get(1).cloned().unwrap_or(Value::Undef);
3763 object_builtin_method(&obj, "hasOwnProperty", vec![key])
3764 }
3765 "Object.defineProperty" => object_define_property(args),
3766 "Object.getOwnPropertyDescriptor" => object_get_own_descriptor(args),
3767 "Object.getOwnPropertyDescriptors" => object_get_own_descriptors(args),
3768 "Object.defineProperties" => object_define_properties(args),
3769 "Object.groupBy" => object_group_by(args),
3772 "Symbol" => Ok(with_host(|h| {
3773 let desc = args
3774 .first()
3775 .filter(|a| !matches!(a, Value::Undef))
3776 .map(|a| h.str_of(a));
3777 h.new_symbol(desc)
3778 })),
3779 "Symbol.for" => Ok(with_host(|h| {
3780 let key = h.str_of(&arg0(&args));
3781 h.symbol_for(&key)
3782 })),
3783 "Symbol.keyFor" => Ok(with_host(|h| h.symbol_registry_key(&arg0(&args)))),
3788 "Map" | "WeakMap" | "Set" | "WeakSet" | "Promise" => construct_builtin(name, args),
3789 "Proxy" => Err(host::type_error("Constructor Proxy requires 'new'")),
3791 "Proxy.revocable" => crate::proxy::revocable(&args),
3792 "Reflect.ownKeys" => {
3798 let v = arg0(&args);
3799 let names = object_keys(args, 3)?;
3800 let syms = proxy_or_own_symbol_keys(&v)?;
3801 if syms.is_empty() {
3802 return Ok(names);
3803 }
3804 let mut all = with_host(|h| h.iter_vec(&names)).unwrap_or_default();
3805 all.extend(syms);
3806 Ok(with_host(|h| h.new_array(all)))
3807 }
3808 "Reflect.getOwnPropertyDescriptor" => object_get_own_descriptor(args),
3809 "Reflect.defineProperty" => {
3810 object_define_property(args)?;
3811 Ok(Value::Bool(true))
3812 }
3813 "Reflect.deleteProperty" => {
3814 let obj = arg0(&args);
3815 let k = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
3816 Ok(Value::Bool(delete_property(&obj, &k)?))
3817 }
3818 "Reflect.setPrototypeOf" => {
3819 let obj = arg0(&args);
3820 let p = args.get(1).cloned().unwrap_or(Value::Undef);
3821 if with_host(|h| h.kind_of(&obj)) == Some(ObjKind::Proxy) {
3822 crate::proxy::set_prototype_of(&obj, &p)?;
3823 return Ok(Value::Bool(true));
3824 }
3825 with_host(|h| h.set_proto(&obj, p));
3826 Ok(Value::Bool(true))
3827 }
3828 "Reflect.isExtensible" => {
3829 let v = arg0(&args);
3830 match crate::proxy::is_extensible(&v)? {
3831 Some(b) => Ok(Value::Bool(b)),
3832 None => Ok(Value::Bool(with_host(|h| h.is_extensible(&v)))),
3833 }
3834 }
3835 "Reflect.preventExtensions" => {
3836 let v = arg0(&args);
3837 if crate::proxy::prevent_extensions(&v)? {
3838 return Ok(Value::Bool(true));
3839 }
3840 with_host(|h| h.prevent_extensions(&v));
3841 Ok(Value::Bool(true))
3842 }
3843 "Reflect.apply" => {
3845 let f = arg0(&args);
3846 let this = args.get(1).cloned();
3847 let list = with_host(|h| h.iter_vec(&args.get(2).cloned().unwrap_or(Value::Undef)))
3848 .unwrap_or_default();
3849 host::invoke(&f, list, this.filter(|t| !with_host(|h| h.is_nullish(t))))
3850 }
3851 "Reflect.construct" => {
3852 let f = arg0(&args);
3853 let list = with_host(|h| h.iter_vec(&args.get(1).cloned().unwrap_or(Value::Undef)))
3854 .unwrap_or_default();
3855 host::construct(&f, list)
3856 }
3857 "Reflect.has" => {
3858 let obj = arg0(&args);
3859 let k = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
3860 Ok(Value::Bool(has_property(&obj, &k)?))
3861 }
3862 "Reflect.get" => {
3865 let obj = arg0(&args);
3866 let k = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
3867 let receiver = args.get(2).cloned().unwrap_or_else(|| obj.clone());
3868 get_property_recv(&obj, &k, &receiver)
3869 }
3870 "Reflect.set" => {
3871 let obj = arg0(&args);
3872 let k = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
3873 let v = args.get(2).cloned().unwrap_or(Value::Undef);
3874 let _ = set_property(&obj, &k, v);
3875 Ok(Value::Bool(true))
3876 }
3877 "JSON.stringify" => json_stringify(args),
3878 "JSON.parse" => json_parse(args),
3879 "structuredClone" => Ok(deep_clone(&arg0(&args))),
3880 "fetch" => crate::stdlib::fetch::fetch(&args),
3881 _ if name.starts_with("@@aborttimeout:") => {
3884 let idx: u32 = name["@@aborttimeout:".len()..].parse().unwrap_or(0);
3885 crate::stdlib::fetch::fire_timeout_abort(idx)
3886 }
3887 "@@streamWriteCallback" => Ok(Value::Undef),
3892 "queueMicrotask" | "process.nextTick" => {
3893 let cb = arg0(&args);
3894 let rest = args.get(1..).map(|s| s.to_vec()).unwrap_or_default();
3895 enqueue_microtask(name == "process.nextTick", cb, rest);
3896 Ok(Value::Undef)
3897 }
3898 "setTimeout" | "setInterval" | "setImmediate" => Ok(schedule_timer(name, args)),
3899 "clearTimeout" | "clearInterval" | "clearImmediate" => {
3900 clear_timer(&arg0(&args));
3901 Ok(Value::Undef)
3902 }
3903 "Promise.resolve" => promise_resolve(arg0(&args)),
3904 "Promise.reject" => promise_reject(arg0(&args)),
3905 "Promise.all" => promise_all(args, AllMode::All),
3906 "Promise.allSettled" => promise_all(args, AllMode::AllSettled),
3907 "Promise.race" => promise_race(args, false),
3908 "Promise.any" => promise_race(args, true),
3909 "Promise.withResolvers" => promise_with_resolvers(),
3912 "Map.groupBy" => map_group_by(args),
3915 n if host::ERROR_NAMES.contains(&n) => Ok(make_error(name, &args)),
3916 _ if name.starts_with("Math.") => math_fn(&name[5..], &args),
3917 _ if name.starts_with("@@presolve:") => {
3919 let id: u32 = name[11..].parse().unwrap_or(0);
3920 host::resolve_promise_val(id, arg0(&args));
3921 Ok(Value::Undef)
3922 }
3923 _ if name.starts_with("@@preject:") => {
3924 let id: u32 = name[10..].parse().unwrap_or(0);
3925 host::reject_promise_val(id, arg0(&args));
3926 Ok(Value::Undef)
3927 }
3928 _ if name.starts_with("@@prevoke:") => {
3931 let i: u32 = name[10..].parse().unwrap_or(0);
3932 Ok(crate::proxy::revoke(i))
3933 }
3934 _ if name.starts_with("@@finpass:") => {
3935 let i: u32 = name[10..].parse().unwrap_or(0);
3937 let cb = Value::Obj(i);
3938 host::invoke(&cb, Vec::new(), None)?;
3939 Ok(arg0(&args))
3940 }
3941 _ if name.starts_with("@@finthrow:") => {
3942 let i: u32 = name[11..].parse().unwrap_or(0);
3944 let cb = Value::Obj(i);
3945 host::invoke(&cb, Vec::new(), None)?;
3946 let reason = arg0(&args);
3947 with_host(|h| h.exc = Some(reason.clone()));
3948 Err(with_host(|h| error_string(h, &reason)))
3949 }
3950 _ => Err(host::type_error(&format!("{name} is not a function"))),
3951 }
3952}
3953
3954fn bigint_convert_error(v: &Value) -> String {
3961 let shown = with_host(|h| h.str_of(v));
3962 host::type_error(&format!("Cannot convert {shown} to a BigInt"))
3963}
3964
3965fn bigint_ctor(v: &Value) -> Result<Value, String> {
3966 use num_bigint::BigInt;
3967 let big = match v {
3968 Value::Bool(b) => BigInt::from(*b as i64),
3969 Value::Int(n) => BigInt::from(*n),
3970 Value::Float(f) => {
3971 if !f.is_finite() || f.fract() != 0.0 {
3972 let disp = with_host(|h| h.str_of(v));
3973 return Err(format!(
3974 "RangeError: The number {disp} cannot be converted to a BigInt because it is not an integer"
3975 ));
3976 }
3977 match BigInt::parse_bytes(format!("{f:.0}").as_bytes(), 10) {
3986 Some(b) => b,
3987 None => return Err(bigint_convert_error(v)),
3988 }
3989 }
3990 Value::Str(s) => match host::parse_bigint_str(s) {
3991 Some(b) => b,
3992 None => return Err(format!("SyntaxError: Cannot convert {s} to a BigInt")),
3993 },
3994 Value::Obj(_) => match with_host(|h| h.get(v).cloned()) {
3995 Some(JsObj::BigInt(b)) => b,
3996 Some(JsObj::Str(s)) => match host::parse_bigint_str(&s) {
3997 Some(b) => b,
3998 None => return Err(format!("SyntaxError: Cannot convert {s} to a BigInt")),
3999 },
4000 _ => return Err(bigint_convert_error(v)),
4001 },
4002 _ => return Err(bigint_convert_error(v)),
4003 };
4004 Ok(with_host(|h| h.new_bigint(big)))
4005}
4006
4007fn regexp_ctor(args: &[Value]) -> Result<Value, String> {
4010 let (source, existing_flags) = match with_host(|h| h.get(&arg0(args)).cloned()) {
4011 Some(JsObj::RegExp(r)) => (r.source.clone(), Some(r.flags.clone())),
4012 _ => {
4013 let a0 = arg0(args);
4014 let src = if matches!(a0, Value::Undef) {
4015 String::new()
4016 } else {
4017 with_host(|h| h.str_of(&a0))
4018 };
4019 (src, None)
4020 }
4021 };
4022 let flags = match args.get(1) {
4023 Some(v) if !matches!(v, Value::Undef) => with_host(|h| h.str_of(v)),
4024 _ => existing_flags.unwrap_or_default(),
4025 };
4026 let src = if source.is_empty() {
4028 "(?:)".to_string()
4029 } else {
4030 source
4031 };
4032 crate::regexp::build_regexp(&src, &flags)
4033}
4034
4035fn bigint_as_n(unsigned: bool, args: &[Value]) -> Result<Value, String> {
4038 use num_bigint::BigInt;
4039 use num_traits::Signed;
4040 let bits = with_host(|h| h.to_number(&arg0(args))) as i64;
4041 if bits < 0 {
4042 return Err("RangeError: Invalid value: not (convertible to) a safe integer".into());
4043 }
4044 let x = match with_host(|h| h.as_bigint(&args.get(1).cloned().unwrap_or(Value::Undef))) {
4045 Some(b) => b,
4046 None => return Err(host::type_error("Cannot convert to a BigInt")),
4047 };
4048 let bits = bits as u32;
4049 if bits == 0 {
4050 return Ok(with_host(|h| h.new_bigint(BigInt::from(0))));
4051 }
4052 let modulus = BigInt::from(1) << bits; let mut r = &x % &modulus;
4055 if r.is_negative() {
4056 r += &modulus;
4057 }
4058 if !unsigned {
4059 let half = BigInt::from(1) << (bits - 1);
4060 if r >= half {
4061 r -= &modulus;
4062 }
4063 }
4064 Ok(with_host(|h| h.new_bigint(r)))
4065}
4066
4067fn string_raw(args: &[Value]) -> Result<Value, String> {
4070 let call_site = arg0(args);
4071 let raw = get_property(&call_site, "raw")?;
4072 let raws = with_host(|h| h.iter_vec(&raw)).unwrap_or_default();
4073 let mut out = String::new();
4074 for (i, r) in raws.iter().enumerate() {
4075 out.push_str(&with_host(|h| h.str_of(r)));
4076 if i + 1 < raws.len() {
4077 if let Some(sub) = args.get(i + 1) {
4078 out.push_str(&with_host(|h| h.str_of(sub)));
4079 }
4080 }
4081 }
4082 Ok(with_host(|h| h.new_str(out)))
4083}
4084
4085fn object_call(args: Vec<Value>) -> Value {
4088 let a = arg0(&args);
4089 if matches!(
4090 with_host(|h| h.get(&a).cloned()),
4091 Some(JsObj::Object(_)) | Some(JsObj::Array(_))
4092 ) {
4093 a
4094 } else {
4095 with_host(|h| h.new_object(IndexMap::new()))
4096 }
4097}
4098
4099pub fn construct_builtin(name: &str, args: Vec<Value>) -> Result<Value, String> {
4101 if let Some(r) = crate::stdlib::construct(name, &args) {
4103 return r;
4104 }
4105 match name {
4106 "Array" => {
4107 if args.len() == 1 {
4113 if let Value::Float(_) | Value::Int(_) = args[0] {
4114 let n = host::to_array_length(&args[0])?;
4115 return Ok(with_host(|h| {
4118 let a = h.new_array(vec![Value::Undef; n]);
4119 h.mark_hole_range(&a, 0..n);
4120 a
4121 }));
4122 }
4123 }
4124 Ok(with_host(|h| h.new_array(args)))
4125 }
4126 "Object" => Ok(object_call(args)),
4127 "Map" | "WeakMap" => {
4128 let weak = name == "WeakMap";
4129 let m = with_host(|h| {
4130 h.alloc(JsObj::Map {
4131 entries: indexmap::IndexMap::new(),
4132 weak,
4133 })
4134 });
4135 if let Some(init) = args
4136 .first()
4137 .filter(|a| !matches!(a, Value::Undef) && !with_host(|h| h.is_null(a)))
4138 {
4139 let pairs = host::iter_all(init)?;
4140 for p in pairs {
4141 let kv = host::iter_all(&p)?;
4142 let k = kv.first().cloned().unwrap_or(Value::Undef);
4143 let v = kv.get(1).cloned().unwrap_or(Value::Undef);
4144 map_method(&m, "set", vec![k, v])?;
4145 }
4146 }
4147 Ok(m)
4148 }
4149 "Set" | "WeakSet" => {
4150 let weak = name == "WeakSet";
4151 let s = with_host(|h| {
4152 h.alloc(JsObj::Set {
4153 entries: indexmap::IndexMap::new(),
4154 weak,
4155 })
4156 });
4157 if let Some(init) = args
4158 .first()
4159 .filter(|a| !matches!(a, Value::Undef) && !with_host(|h| h.is_null(a)))
4160 {
4161 let vals = host::iter_all(init)?;
4162 for v in vals {
4163 set_method(&s, "add", vec![v])?;
4164 }
4165 }
4166 Ok(s)
4167 }
4168 "Promise" => new_promise(arg0(&args)),
4169 "Proxy" => crate::proxy::create(&args),
4170 "Function" => function_ctor(&args),
4175 "RegExp" => regexp_ctor(&args),
4176 "BigInt" => Err(host::type_error("BigInt is not a constructor")),
4177 "Error" => Ok(make_error(name, &args)),
4178 n if host::ERROR_NAMES.contains(&n) => Ok(make_error(name, &args)),
4179 _ => Err(host::type_error(&format!("{name} is not a constructor"))),
4180 }
4181}
4182
4183fn make_error(name: &str, args: &[Value]) -> Value {
4184 let agg = name == "AggregateError";
4187 let (errors, args) = if agg {
4188 (
4189 Some(args.first().cloned().unwrap_or(Value::Undef)),
4190 args.get(1..).unwrap_or(&[]),
4191 )
4192 } else {
4193 (None, args)
4194 };
4195 with_host(|h| {
4196 h.ensure_error_protos();
4197 let mut props: IndexMap<String, Value> = IndexMap::new();
4198 let msg = args
4199 .first()
4200 .filter(|a| !matches!(a, Value::Undef))
4201 .map(|a| h.str_of(a));
4202 if let Some(m) = &msg {
4203 let mv = h.new_str(m.clone());
4204 props.insert("message".into(), mv);
4205 }
4206 let frames = h.stack_frames();
4209 let stack = match &msg {
4210 Some(m) if !m.is_empty() => format!("{name}: {m}{frames}"),
4211 _ => format!("{name}{frames}"),
4212 };
4213 let sv = h.new_str(stack);
4214 props.insert("stack".into(), sv);
4215 if let Some(errs) = errors {
4216 let items = h.iter_vec(&errs).unwrap_or_default();
4218 let arr = h.new_array(items);
4219 props.insert("errors".into(), arr);
4220 }
4221 let opts = args.get(1);
4224 if let Some(cause) = opts.and_then(|o| match h.get(o) {
4225 Some(JsObj::Object(p)) => p.get("cause").cloned(),
4226 _ => None,
4227 }) {
4228 props.insert("cause".into(), cause);
4229 }
4230 let e = h.new_object(props);
4231 if let Some(p) = host::error_proto_of(h, name) {
4232 h.set_proto(&e, p);
4233 }
4234 for k in ["message", "stack", "errors", "cause"] {
4238 h.hide_prop(&e, k);
4239 }
4240 e
4241 })
4242}
4243
4244fn print_line(args: &[Value], stderr: bool) {
4245 let line: String = crate::stdlib::util::format(args);
4248 with_host(|h| h.write_out(&format!("{line}\n"), stderr));
4249}
4250
4251fn arg0(args: &[Value]) -> Value {
4252 args.first().cloned().unwrap_or(Value::Undef)
4253}
4254fn arg_num(args: &[Value], i: usize) -> f64 {
4255 with_host(|h| h.to_number(&args.get(i).cloned().unwrap_or(Value::Undef)))
4256}
4257
4258fn is_integer(v: Value) -> bool {
4259 match v {
4260 Value::Int(_) => true,
4261 Value::Float(f) => f.is_finite() && f.fract() == 0.0,
4262 _ => false,
4263 }
4264}
4265fn is_safe_integer(v: Value) -> bool {
4266 match v {
4267 Value::Float(f) => f.is_finite() && f.fract() == 0.0 && f.abs() <= 9007199254740991.0,
4268 Value::Int(_) => true,
4269 _ => false,
4270 }
4271}
4272
4273fn uri_encode(s: &str, uri: bool) -> Result<Value, String> {
4277 const UNRESERVED: &[u8] =
4279 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()";
4280 const RESERVED: &[u8] = b";,/?:@&=+$#";
4282 let mut out = String::with_capacity(s.len());
4283 for &b in s.as_bytes() {
4284 if UNRESERVED.contains(&b) || (uri && RESERVED.contains(&b)) {
4285 out.push(b as char);
4286 } else {
4287 out.push('%');
4288 out.push(
4289 char::from_digit((b >> 4) as u32, 16)
4290 .unwrap()
4291 .to_ascii_uppercase(),
4292 );
4293 out.push(
4294 char::from_digit((b & 0xf) as u32, 16)
4295 .unwrap()
4296 .to_ascii_uppercase(),
4297 );
4298 }
4299 }
4300 Ok(with_host(|h| h.new_str(out)))
4301}
4302
4303fn uri_decode(s: &str, uri: bool) -> Result<Value, String> {
4307 const RESERVED: &[u8] = b";,/?:@&=+$#";
4308 let bytes = s.as_bytes();
4309 let mut out: Vec<u8> = Vec::with_capacity(bytes.len());
4310 let mut i = 0;
4311 while i < bytes.len() {
4312 if bytes[i] == b'%' {
4313 if i + 2 >= bytes.len() {
4314 return Err("URIError: URI malformed".into());
4315 }
4316 let hi = (bytes[i + 1] as char).to_digit(16);
4317 let lo = (bytes[i + 2] as char).to_digit(16);
4318 match (hi, lo) {
4319 (Some(h), Some(l)) => {
4320 let byte = (h * 16 + l) as u8;
4321 if uri && RESERVED.contains(&byte) {
4323 out.extend_from_slice(&bytes[i..i + 3]);
4324 } else {
4325 out.push(byte);
4326 }
4327 i += 3;
4328 }
4329 _ => return Err("URIError: URI malformed".into()),
4330 }
4331 } else {
4332 out.push(bytes[i]);
4333 i += 1;
4334 }
4335 }
4336 match String::from_utf8(out) {
4337 Ok(decoded) => Ok(with_host(|h| h.new_str(decoded))),
4338 Err(_) => Err("URIError: URI malformed".into()),
4339 }
4340}
4341
4342fn legacy_escape(s: &str) -> Result<Value, String> {
4353 const KEEP: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./";
4354 let mut out = String::with_capacity(s.len());
4355 for u in s.encode_utf16() {
4356 if u < 0x100 {
4357 if KEEP.contains(&(u as u8)) {
4358 out.push(u as u8 as char);
4359 } else {
4360 out.push_str(&format!("%{u:02X}"));
4361 }
4362 } else {
4363 out.push_str(&format!("%u{u:04X}"));
4364 }
4365 }
4366 Ok(with_host(|h| h.new_str(out)))
4367}
4368
4369fn legacy_unescape(s: &str) -> Result<Value, String> {
4377 let b = s.as_bytes();
4378 let hex = |i: usize, n: usize| -> Option<u16> {
4379 if i + n > b.len() {
4380 return None;
4381 }
4382 let mut v: u16 = 0;
4383 for &c in &b[i..i + n] {
4384 v = v.checked_mul(16)? + (c as char).to_digit(16)? as u16;
4385 }
4386 Some(v)
4387 };
4388 let units: Vec<u16> = s.encode_utf16().collect();
4389 let mut out: Vec<u16> = Vec::with_capacity(units.len());
4390 let mut i = 0;
4391 while i < b.len() {
4392 if b[i] == b'%' {
4395 if let Some(u) = hex(i + 1, 2) {
4396 out.push(u);
4397 i += 3;
4398 continue;
4399 }
4400 if b.get(i + 1) == Some(&b'u') {
4401 if let Some(u) = hex(i + 2, 4) {
4402 out.push(u);
4403 i += 6;
4404 continue;
4405 }
4406 }
4407 }
4408 let c = s[i..].chars().next().unwrap_or('%');
4409 let mut buf = [0u16; 2];
4410 out.extend_from_slice(c.encode_utf16(&mut buf));
4411 i += c.len_utf8();
4412 }
4413 Ok(with_host(|h| {
4414 h.new_str(crate::utf16::to_string_lossy(&out))
4415 }))
4416}
4417
4418fn parse_int(args: &[Value]) -> f64 {
4419 let s = with_host(|h| h.str_of(&arg0(args)));
4420 let radix_arg = args
4424 .get(1)
4425 .map(|r| with_host(|h| host::to_int32(h.to_number(r))));
4426 let radix = match radix_arg {
4427 Some(0) | None => None,
4428 Some(r) if (2..=36).contains(&r) => Some(r as u32),
4429 Some(_) => return f64::NAN,
4430 };
4431 let t = crate::utf16::js_trim_start(&s);
4432 let (neg, digits) = match t.strip_prefix('-') {
4433 Some(rest) => (true, rest),
4434 None => (false, t.strip_prefix('+').unwrap_or(t)),
4435 };
4436 let (radix, digits) = match radix {
4437 Some(16) => (
4438 16u32,
4439 digits
4440 .strip_prefix("0x")
4441 .or_else(|| digits.strip_prefix("0X"))
4442 .unwrap_or(digits),
4443 ),
4444 Some(r) => (r, digits),
4445 None => {
4446 if let Some(hex) = digits
4447 .strip_prefix("0x")
4448 .or_else(|| digits.strip_prefix("0X"))
4449 {
4450 (16, hex)
4451 } else {
4452 (10, digits)
4453 }
4454 }
4455 };
4456 let valid: String = digits.chars().take_while(|c| c.is_digit(radix)).collect();
4457 if valid.is_empty() {
4458 return f64::NAN;
4459 }
4460 let n = if radix == 10 {
4466 valid.parse::<f64>().unwrap_or(f64::NAN)
4471 } else {
4472 let mut n = 0.0f64;
4473 for c in valid.chars() {
4474 n = n * radix as f64 + c.to_digit(radix).unwrap_or(0) as f64;
4475 }
4476 n
4477 };
4478 if neg {
4479 -n
4480 } else {
4481 n
4482 }
4483}
4484
4485fn parse_float(args: &[Value]) -> f64 {
4486 let s = with_host(|h| h.str_of(&arg0(args)));
4487 let t = crate::utf16::js_trim_start(&s);
4488 let inf_body = t
4490 .strip_prefix('+')
4491 .or_else(|| t.strip_prefix('-'))
4492 .unwrap_or(t);
4493 if inf_body.starts_with("Infinity") {
4494 return if t.starts_with('-') {
4495 f64::NEG_INFINITY
4496 } else {
4497 f64::INFINITY
4498 };
4499 }
4500 let mut end = 0;
4507 let bytes = t.as_bytes();
4508 let mut seen_dot = false;
4509 let mut seen_e = false;
4510 let mut digits_before_dot = false;
4511 for (i, &c) in bytes.iter().enumerate() {
4512 match c {
4513 b'0'..=b'9' => {
4514 if !seen_dot && !seen_e {
4515 digits_before_dot = true;
4516 }
4517 end = i + 1;
4518 }
4519 b'+' | b'-' if i == 0 || bytes[i - 1] == b'e' || bytes[i - 1] == b'E' => {}
4522 b'.' if !seen_dot && !seen_e => {
4524 seen_dot = true;
4525 if digits_before_dot {
4526 end = i + 1;
4527 }
4528 }
4529 b'e' | b'E' if !seen_e && end > 0 => seen_e = true,
4530 _ => break,
4531 }
4532 }
4533 if end == 0 {
4534 return f64::NAN;
4535 }
4536 t[..end].parse::<f64>().unwrap_or(f64::NAN)
4537}
4538
4539pub(crate) fn js_pow(base: f64, exp: f64) -> f64 {
4545 if exp == 0.0 {
4546 return 1.0;
4547 }
4548 if base.is_nan() || exp.is_nan() {
4549 return f64::NAN;
4550 }
4551 if base.abs() == 1.0 && exp.is_infinite() {
4552 return f64::NAN;
4553 }
4554 base.powf(exp)
4555}
4556
4557fn math_fn(fname: &str, args: &[Value]) -> Result<Value, String> {
4558 if fname != "random"
4564 && args
4565 .iter()
4566 .any(|a| with_host(|h| matches!(h.get(a), Some(JsObj::BigInt(_)))))
4567 {
4568 return Err(host::type_error(
4569 "Cannot convert a BigInt value to a number",
4570 ));
4571 }
4572 let x = arg_num(args, 0);
4573 let r = match fname {
4574 "floor" => x.floor(),
4575 "ceil" => x.ceil(),
4576 "round" => {
4585 if !x.is_finite() || x == 0.0 {
4586 x
4587 } else if x > 0.0 && x < 0.5 {
4588 0.0
4589 } else if (-0.5..0.0).contains(&x) {
4590 -0.0
4591 } else {
4592 let f = x.floor();
4595 if x - f >= 0.5 {
4596 f + 1.0
4597 } else {
4598 f
4599 }
4600 }
4601 }
4602 "trunc" => x.trunc(),
4603 "abs" => x.abs(),
4604 "sign" => {
4605 if x.is_nan() {
4606 f64::NAN
4607 } else if x > 0.0 {
4608 1.0
4609 } else if x < 0.0 {
4610 -1.0
4611 } else {
4612 x
4613 }
4614 }
4615 "sqrt" => x.sqrt(),
4616 "cbrt" => x.cbrt(),
4617 "exp" => x.exp(),
4618 "log" => x.ln(),
4619 "log2" => x.log2(),
4620 "log10" => x.log10(),
4621 "sin" => x.sin(),
4622 "cos" => x.cos(),
4623 "tan" => x.tan(),
4624 "asin" => x.asin(),
4625 "acos" => x.acos(),
4626 "atan" => x.atan(),
4627 "atan2" => x.atan2(arg_num(args, 1)),
4628 "pow" => js_pow(x, arg_num(args, 1)),
4634 "sinh" => x.sinh(),
4636 "cosh" => x.cosh(),
4637 "tanh" => x.tanh(),
4638 "asinh" => x.asinh(),
4639 "acosh" => x.acosh(),
4640 "atanh" => x.atanh(),
4641 "log1p" => x.ln_1p(),
4642 "expm1" => x.exp_m1(),
4643 "imul" => (host::to_int32(x).wrapping_mul(host::to_int32(arg_num(args, 1)))) as f64,
4646 "hypot" => {
4647 let xs: Vec<f64> = args.iter().map(|a| with_host(|h| h.to_number(a))).collect();
4650 let mut max = 0.0f64;
4651 for x in &xs {
4652 if x.abs() > max {
4653 max = x.abs();
4654 }
4655 }
4656 if xs.iter().any(|x| x.is_infinite()) {
4657 f64::INFINITY
4658 } else if max == 0.0 || !max.is_finite() {
4659 max
4660 } else {
4661 let s: f64 = xs.iter().map(|x| (x / max) * (x / max)).sum();
4662 max * s.sqrt()
4663 }
4664 }
4665 "random" => pseudo_random(),
4666 "max" => {
4667 if args.is_empty() {
4668 f64::NEG_INFINITY
4669 } else {
4670 let mut m = f64::NEG_INFINITY;
4671 for a in args {
4672 let n = with_host(|h| h.to_number(a));
4673 if n.is_nan() {
4674 return Ok(Value::Float(f64::NAN));
4675 }
4676 if n > m || (n == m && n == 0.0 && n.is_sign_positive()) {
4680 m = n;
4681 }
4682 }
4683 m
4684 }
4685 }
4686 "min" => {
4687 if args.is_empty() {
4688 f64::INFINITY
4689 } else {
4690 let mut m = f64::INFINITY;
4691 for a in args {
4692 let n = with_host(|h| h.to_number(a));
4693 if n.is_nan() {
4694 return Ok(Value::Float(f64::NAN));
4695 }
4696 if n < m || (n == m && n == 0.0 && n.is_sign_negative()) {
4699 m = n;
4700 }
4701 }
4702 m
4703 }
4704 }
4705 "clz32" => {
4707 let u = if x.is_finite() {
4708 x.trunc().rem_euclid(4294967296.0) as u32
4709 } else {
4710 0
4711 };
4712 u.leading_zeros() as f64
4713 }
4714 "fround" => (x as f32) as f64,
4716 _ => return Err(host::type_error(&format!("Math.{fname} is not a function"))),
4717 };
4718 Ok(Value::Float(r))
4719}
4720
4721fn pseudo_random() -> f64 {
4724 use std::cell::Cell;
4725 thread_local!(static SEED: Cell<u64> = const { Cell::new(0x2545F4914F6CDD1D) });
4726 SEED.with(|s| {
4727 let mut x = s.get();
4728 x ^= x << 13;
4729 x ^= x >> 7;
4730 x ^= x << 17;
4731 s.set(x);
4732 (x >> 11) as f64 / (1u64 << 53) as f64
4733 })
4734}
4735
4736fn object_keys(args: Vec<Value>, mode: u8) -> Result<Value, String> {
4739 let v = arg0(&args);
4740 require_object_coercible(&v)?;
4741 if with_host(|h| h.kind_of(&v)) == Some(ObjKind::Proxy) {
4745 if mode == 3 {
4746 let keys = crate::proxy::own_keys(&v)?.unwrap_or_default();
4747 return Ok(with_host(|h| {
4748 let out: Vec<Value> = keys
4749 .into_iter()
4750 .filter(|k| !host::is_symbol_key(k))
4751 .map(|k| h.new_str(k))
4752 .collect();
4753 h.new_array(out)
4754 }));
4755 }
4756 let entries = crate::proxy::own_enum_entries(&v)?;
4757 return Ok(with_host(|h| {
4758 let out: Vec<Value> = entries
4759 .into_iter()
4760 .map(|(k, val)| match mode {
4761 0 => h.new_str(k),
4762 1 => val,
4763 _ => {
4764 let ks = h.new_str(k);
4765 h.new_array(vec![ks, val])
4766 }
4767 })
4768 .collect();
4769 h.new_array(out)
4770 }));
4771 }
4772 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(&v).cloned()) {
4775 if let Some(names) = builtin_proto_method_names(&ns) {
4776 return Ok(with_host(|h| {
4777 let out: Vec<Value> = names
4778 .iter()
4779 .map(|name| match mode {
4780 1 => h.alloc(JsObj::Builtin(format!(
4781 "@proto:{}:{name}",
4782 ns.trim_end_matches(".prototype")
4783 ))),
4784 2 => {
4785 let ks = h.new_str(*name);
4786 let val = h.alloc(JsObj::Builtin(format!(
4787 "@proto:{}:{name}",
4788 ns.trim_end_matches(".prototype")
4789 )));
4790 h.new_array(vec![ks, val])
4791 }
4792 _ => h.new_str(*name),
4793 })
4794 .collect();
4795 h.new_array(out)
4796 }));
4797 }
4798 let mut names = crate::stdlib::namespace_keys(&ns);
4802 if names.is_empty() && mode == 3 {
4807 let prefix = format!("{ns}.");
4808 names = NS_METHODS
4809 .iter()
4810 .filter_map(|q| q.strip_prefix(&prefix))
4811 .map(|m| m.to_string())
4812 .collect();
4813 }
4814 if !names.is_empty() {
4815 let entries: Vec<(String, Value)> = names
4816 .into_iter()
4817 .map(|k| {
4818 let val = namespace_property(&ns, &k);
4819 (k, val)
4820 })
4821 .collect();
4822 return Ok(with_host(|h| {
4823 let out: Vec<Value> = entries
4824 .into_iter()
4825 .map(|(k, val)| match mode {
4826 1 => val,
4827 2 => {
4828 let ks = h.new_str(k);
4829 h.new_array(vec![ks, val])
4830 }
4831 _ => h.new_str(k),
4832 })
4833 .collect();
4834 h.new_array(out)
4835 }));
4836 }
4837 }
4838 let entries: Vec<(String, Value)> = with_host(|h| {
4841 if mode == 3 {
4842 return h
4845 .own_key_names(&v, false)
4846 .into_iter()
4847 .map(|k| (k, Value::Undef))
4848 .collect();
4849 }
4850 Vec::new()
4851 });
4852 let entries = if mode == 3 {
4853 entries
4854 } else {
4855 host::own_enum_entries_deep(&v)
4856 };
4857 Ok(with_host(|h| {
4858 let out: Vec<Value> = entries
4859 .into_iter()
4860 .map(|(k, val)| match mode {
4861 0 | 3 => h.new_str(k),
4862 1 => val,
4863 _ => {
4864 let ks = h.new_str(k);
4865 h.new_array(vec![ks, val])
4866 }
4867 })
4868 .collect();
4869 h.new_array(out)
4870 }))
4871}
4872
4873fn object_assign(args: Vec<Value>) -> Result<Value, String> {
4874 let target = arg0(&args);
4875 require_object_coercible(&target)?;
4878 for src in args.iter().skip(1) {
4879 let entries = host::own_enum_entries_deep(src);
4882 let syms = with_host(|h| h.own_symbol_entries(src));
4883 let filled = with_host(|h| {
4886 if let Some(JsObj::Object(p)) = h.get_mut(&target) {
4887 for (k, v) in entries.iter().cloned().chain(syms.iter().cloned()) {
4888 p.insert(k, v);
4889 }
4890 host::canonicalize_own_keys(p);
4891 return true;
4892 }
4893 false
4894 });
4895 if !filled {
4902 for (k, v) in entries.into_iter().chain(syms) {
4903 set_property(&target, &k, v)?;
4904 }
4905 }
4906 }
4907 Ok(target)
4908}
4909
4910fn object_from_entries(args: Vec<Value>) -> Result<Value, String> {
4911 let pairs = with_host(|h| h.iter_vec(&arg0(&args))).unwrap_or_default();
4912 let mut props: IndexMap<String, Value> = IndexMap::new();
4913 for p in pairs {
4914 let kv = with_host(|h| h.iter_vec(&p)).unwrap_or_default();
4915 let key = with_host(|h| h.str_of(&kv.first().cloned().unwrap_or(Value::Undef)));
4916 let val = kv.get(1).cloned().unwrap_or(Value::Undef);
4917 props.insert(key, val);
4918 }
4919 Ok(with_host(|h| h.new_object(props)))
4920}
4921
4922fn object_group_by(args: Vec<Value>) -> Result<Value, String> {
4926 let items = host::iter_all(&arg0(&args))?;
4927 let cb = args.get(1).cloned().unwrap_or(Value::Undef);
4928 let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
4929 for (i, item) in items.into_iter().enumerate() {
4930 let key_v = host::invoke(&cb, vec![item.clone(), Value::Float(i as f64)], None)?;
4931 let key = with_host(|h| h.property_key(&key_v));
4932 groups.entry(key).or_default().push(item);
4933 }
4934 let props: IndexMap<String, Value> = with_host(|h| {
4935 groups
4936 .into_iter()
4937 .map(|(k, v)| (k, h.new_array(v)))
4938 .collect()
4939 });
4940 let obj = with_host(|h| h.new_object(props));
4941 with_host(|h| {
4943 let nv = h.null();
4944 h.set_proto(&obj, nv);
4945 });
4946 Ok(obj)
4947}
4948
4949fn map_group_by(args: Vec<Value>) -> Result<Value, String> {
4952 let items = host::iter_all(&arg0(&args))?;
4953 let cb = args.get(1).cloned().unwrap_or(Value::Undef);
4954 let m = with_host(|h| {
4955 h.alloc(JsObj::Map {
4956 entries: IndexMap::new(),
4957 weak: false,
4958 })
4959 });
4960 for (i, item) in items.into_iter().enumerate() {
4961 let key_v = host::invoke(&cb, vec![item.clone(), Value::Float(i as f64)], None)?;
4962 let existing = map_method(&m, "get", vec![key_v.clone()])?;
4963 if matches!(existing, Value::Undef) {
4964 let arr = with_host(|h| h.new_array(vec![item]));
4965 map_method(&m, "set", vec![key_v, arr])?;
4966 } else {
4967 with_host(|h| {
4968 if let Some(JsObj::Array(a)) = h.get_mut(&existing) {
4969 a.push(item);
4970 }
4971 });
4972 }
4973 }
4974 Ok(m)
4975}
4976
4977fn array_from_async(args: Vec<Value>) -> Result<Value, String> {
4993 thread_local! {
4994 static IMPL: std::cell::RefCell<Option<Value>> = const { std::cell::RefCell::new(None) };
4995 }
4996 const SRC: &str = "(async function (items, mapFn, thisArg) {\n\
4997 const out = []; let i = 0;\n\
4998 const step = async (v) => { const a = await v; out.push(mapFn ? await mapFn.call(thisArg, a, i) : a); i++; };\n\
4999 const iterable = items != null && (typeof items[Symbol.asyncIterator] === 'function'\n\
5000 || typeof items[Symbol.iterator] === 'function' || typeof items.next === 'function');\n\
5001 if (iterable) {\n\
5002 for await (const v of items) { out.push(mapFn ? await mapFn.call(thisArg, v, i) : v); i++; }\n\
5003 return out;\n\
5004 }\n\
5005 const len = items == null ? 0 : (Math.trunc(Number(items.length)) || 0);\n\
5006 while (i < len) { await step(items[i]); }\n\
5007 return out;\n\
5008 })";
5009 let f = IMPL.with(|c| c.borrow().clone());
5010 let f = match f {
5011 Some(f) => f,
5012 None => {
5013 let f = crate::eval_in_global_scope(SRC)?;
5014 IMPL.with(|c| *c.borrow_mut() = Some(f.clone()));
5015 f
5016 }
5017 };
5018 host::invoke(&f, args, None)
5019}
5020
5021fn array_from(args: Vec<Value>) -> Result<Value, String> {
5022 let src = arg0(&args);
5025 let items = match host::iter_all(&src) {
5026 Ok(v) => v,
5027 Err(_) => array_like_items(&src),
5028 };
5029 if let Some(cb) = args.get(1).cloned() {
5030 let mut out = Vec::with_capacity(items.len());
5031 for (i, it) in items.into_iter().enumerate() {
5032 out.push(host::invoke(&cb, vec![it, Value::Float(i as f64)], None)?);
5033 }
5034 return Ok(with_host(|h| h.new_array(out)));
5035 }
5036 Ok(with_host(|h| h.new_array(items)))
5037}
5038
5039fn array_like_items(src: &Value) -> Vec<Value> {
5041 let len = get_property(src, "length")
5042 .ok()
5043 .map(|l| with_host(|h| h.to_number(&l)))
5044 .unwrap_or(0.0);
5045 if !len.is_finite() || len <= 0.0 {
5046 return Vec::new();
5047 }
5048 (0..len as usize)
5049 .map(|i| get_property(src, &i.to_string()).unwrap_or(Value::Undef))
5050 .collect()
5051}
5052
5053fn json_stringify(args: Vec<Value>) -> Result<Value, String> {
5056 let replacer = args
5060 .get(1)
5061 .filter(|r| with_host(|h| host::is_callable(h, r)))
5062 .cloned();
5063 let root = arg0(&args);
5071 let wrapper = with_host(|h| {
5072 let mut m: IndexMap<String, Value> = IndexMap::new();
5073 m.insert(String::new(), root.clone());
5074 h.new_object(m)
5075 });
5076 let v = apply_to_json(&wrapper, "", &root, &mut Vec::new(), replacer.as_ref())?;
5077 if with_host(|h| json_has_bigint(h, &v)) {
5080 return Err(host::type_error("Do not know how to serialize a BigInt"));
5081 }
5082 let indent = match args.get(2) {
5083 Some(Value::Float(f)) => " ".repeat((*f as usize).min(10)),
5084 Some(other) => with_host(|h| h.as_str(other)).unwrap_or_default(),
5085 None => String::new(),
5086 };
5087 let keys: Option<Vec<String>> = args.get(1).and_then(|r| {
5089 with_host(|h| match h.get(r) {
5090 Some(JsObj::Array(items)) => {
5091 Some(items.iter().map(|k| h.str_of(k)).collect::<Vec<_>>())
5092 }
5093 _ => None,
5094 })
5095 });
5096 let s = with_host(|h| json_str(h, &v, &indent, 0, keys.as_deref()));
5097 match s {
5098 Some(s) => Ok(with_host(|h| h.new_str(s))),
5099 None => Ok(Value::Undef),
5100 }
5101}
5102
5103fn apply_to_json(
5118 holder: &Value,
5119 key: &str,
5120 v: &Value,
5121 path: &mut Vec<Value>,
5122 rep: Option<&Value>,
5123) -> Result<Value, String> {
5124 let mut v = v.clone();
5125 if matches!(v, Value::Obj(_)) {
5126 let tag = crate::stdlib::native_tag(&v);
5127 let has_to_json = with_host(|h| match host::lookup_chain(h, &v, "toJSON") {
5128 Some(f) => host::is_callable(h, &f),
5129 None => false,
5130 }) || tag
5131 .as_deref()
5132 .map(crate::stdlib::has_to_json)
5133 .unwrap_or(false);
5134 if has_to_json {
5135 let k = with_host(|h| h.new_str(key.to_string()));
5136 v = host::call_method(&v, "toJSON", vec![k])?;
5137 }
5138 }
5139 if let Some(rep) = rep {
5140 let k = with_host(|h| h.new_str(key.to_string()));
5141 v = host::invoke(rep, vec![k, v.clone()], Some(holder.clone()))?;
5142 }
5143 json_walk_children(&v, path, rep)
5144}
5145
5146fn json_visible_key(k: &str) -> bool {
5150 !k.starts_with("@@") && !k.starts_with('#')
5151}
5152
5153fn json_walk_children(
5156 v: &Value,
5157 path: &mut Vec<Value>,
5158 rep: Option<&Value>,
5159) -> Result<Value, String> {
5160 if !matches!(v, Value::Obj(_)) {
5161 return Ok(v.clone());
5162 }
5163 if with_host(|h| path.iter().any(|p| h.strict_eq(p, v))) {
5165 return Err(host::type_error("Converting circular structure to JSON"));
5166 }
5167 if with_host(|h| h.kind_of(v)) == Some(ObjKind::Proxy) {
5171 let snap = crate::proxy::json_snapshot(v)?;
5172 path.push(v.clone());
5173 let out = json_walk_children(&snap, path, rep);
5174 path.pop();
5175 return out;
5176 }
5177 let obj = with_host(|h| h.get(v).cloned());
5178 path.push(v.clone());
5179 let out = (|| match obj {
5180 Some(JsObj::Array(items)) => {
5181 let mut out = Vec::with_capacity(items.len());
5182 let mut changed = false;
5183 for (i, it) in items.iter().enumerate() {
5184 let nv = apply_to_json(v, &i.to_string(), it, path, rep)?;
5185 changed |= !with_host(|h| h.strict_eq(&nv, it));
5186 out.push(nv);
5187 }
5188 if changed {
5191 Ok(with_host(|h| h.new_array(out)))
5192 } else {
5193 Ok(v.clone())
5194 }
5195 }
5196 Some(JsObj::Object(props)) => {
5197 let has_accessor = with_host(|h| {
5202 h.own_accessor_keys(v)
5203 .iter()
5204 .any(|k| h.prop_attrs(v, k).enumerable)
5205 });
5206 if has_accessor {
5207 let mut next: IndexMap<String, Value> = IndexMap::new();
5208 for (k, val) in host::own_enum_entries_deep(v) {
5209 let nv = if json_visible_key(&k) {
5210 apply_to_json(v, &k, &val, path, rep)?
5211 } else {
5212 val
5213 };
5214 next.insert(k, nv);
5215 }
5216 return Ok(with_host(|h| h.new_object(next)));
5217 }
5218 let mut next: IndexMap<String, Value> = IndexMap::new();
5221 let mut changed = false;
5222 for (k, val) in &props {
5223 let nv = if json_visible_key(k) {
5224 apply_to_json(v, k, val, path, rep)?
5225 } else {
5226 val.clone()
5227 };
5228 changed |= !with_host(|h| h.strict_eq(&nv, val));
5229 next.insert(k.clone(), nv);
5230 }
5231 if changed {
5232 Ok(with_host(|h| {
5233 let o = h.new_object(next);
5234 h.copy_prop_attrs(v, &o);
5235 o
5236 }))
5237 } else {
5238 Ok(v.clone())
5239 }
5240 }
5241 _ => Ok(v.clone()),
5242 })();
5243 path.pop();
5244 out
5245}
5246
5247fn json_has_bigint(h: &host::JsHost, v: &Value) -> bool {
5250 match h.get(v) {
5251 Some(JsObj::BigInt(_)) => true,
5252 Some(JsObj::Array(items)) => items.iter().any(|x| json_has_bigint(h, x)),
5253 Some(JsObj::Object(props)) => props
5254 .iter()
5255 .filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
5256 .any(|(_, val)| json_has_bigint(h, val)),
5257 _ => false,
5258 }
5259}
5260
5261fn json_str(
5262 h: &host::JsHost,
5263 v: &Value,
5264 indent: &str,
5265 depth: usize,
5266 keys: Option<&[String]>,
5267) -> Option<String> {
5268 let sep = if indent.is_empty() { ":" } else { ": " };
5269 match v {
5270 Value::Undef => None,
5271 Value::Bool(b) => Some(if *b { "true".into() } else { "false".into() }),
5272 Value::Int(n) => Some(n.to_string()),
5273 Value::Float(f) => Some(if f.is_finite() {
5274 host::fmt_number(*f)
5275 } else {
5276 "null".into()
5277 }),
5278 Value::Str(s) => Some(json_quote(s)),
5279 Value::Obj(_) => match h.get(v) {
5280 Some(JsObj::Str(s)) => Some(json_quote(s)),
5281 Some(JsObj::Null) => Some("null".into()),
5282 Some(JsObj::Map { .. }) | Some(JsObj::Set { .. }) => Some("{}".into()),
5284 Some(JsObj::Func(_))
5286 | Some(JsObj::Builtin(_))
5287 | Some(JsObj::BoundMethod { .. })
5288 | Some(JsObj::BoundFunc { .. })
5289 | Some(JsObj::Class(_))
5290 | Some(JsObj::Symbol { .. })
5291 | Some(JsObj::Generator { .. }) => None,
5292 Some(JsObj::Array(items)) => {
5293 if items.is_empty() {
5294 return Some("[]".into());
5295 }
5296 let parts: Vec<String> = items
5297 .iter()
5298 .map(|x| {
5299 json_str(h, x, indent, depth + 1, keys).unwrap_or_else(|| "null".into())
5300 })
5301 .collect();
5302 Some(wrap(&parts, "[", "]", indent, depth))
5303 }
5304 Some(JsObj::Object(props)) => {
5305 let parts: Vec<String> = match keys {
5307 Some(allow) => allow
5308 .iter()
5309 .filter_map(|k| {
5310 props.get(k).and_then(|val| {
5311 json_str(h, val, indent, depth + 1, keys)
5312 .map(|vs| format!("{}{sep}{vs}", json_quote(k)))
5313 })
5314 })
5315 .collect(),
5316 None => h
5317 .own_enum_entries(v)
5318 .iter()
5319 .filter_map(|(k, val)| {
5320 json_str(h, val, indent, depth + 1, keys)
5321 .map(|vs| format!("{}{sep}{vs}", json_quote(k)))
5322 })
5323 .collect(),
5324 };
5325 if parts.is_empty() {
5326 return Some("{}".into());
5327 }
5328 Some(wrap(&parts, "{", "}", indent, depth))
5329 }
5330 _ => Some("null".into()),
5331 },
5332 _ => Some("null".into()),
5333 }
5334}
5335
5336fn wrap(parts: &[String], open: &str, close: &str, indent: &str, depth: usize) -> String {
5337 if indent.is_empty() {
5338 format!("{open}{}{close}", parts.join(","))
5339 } else {
5340 let pad = indent.repeat(depth + 1);
5341 let pad_close = indent.repeat(depth);
5342 format!(
5343 "{open}\n{pad}{}\n{pad_close}{close}",
5344 parts.join(&format!(",\n{pad}"))
5345 )
5346 }
5347}
5348
5349fn json_quote(s: &str) -> String {
5350 let mut out = String::from("\"");
5351 for c in s.chars() {
5352 match c {
5353 '"' => out.push_str("\\\""),
5354 '\\' => out.push_str("\\\\"),
5355 '\n' => out.push_str("\\n"),
5356 '\t' => out.push_str("\\t"),
5357 '\r' => out.push_str("\\r"),
5358 '\u{8}' => out.push_str("\\b"),
5365 '\u{c}' => out.push_str("\\f"),
5366 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
5367 _ => out.push(c),
5368 }
5369 }
5370 out.push('"');
5371 out
5372}
5373
5374fn json_parse(args: Vec<Value>) -> Result<Value, String> {
5375 let s = with_host(|h| h.str_of(&arg0(&args)));
5376 let mut p = JsonParser {
5377 chars: s.chars().collect(),
5378 pos: 0,
5379 };
5380 p.skip_ws();
5381 if p.peek().is_none() {
5382 return Err("SyntaxError: Unexpected end of JSON input".into());
5383 }
5384 let v = p.parse_value()?;
5385 let value_end = p.pos;
5386 p.skip_ws();
5387 if let Some(c) = p.peek() {
5390 let after_number = value_end > 0
5397 && p.pos == value_end
5398 && p.chars[value_end - 1].is_ascii_digit()
5399 && c.is_ascii_digit();
5400 return Err(if after_number {
5401 p.err_at("Unexpected number", p.pos)
5402 } else {
5403 p.err_trailing(p.pos)
5404 });
5405 }
5406 if let Some(reviver) = args
5408 .get(1)
5409 .filter(|r| with_host(|h| host::is_callable(h, r)))
5410 .cloned()
5411 {
5412 return json_revive("", v, &reviver);
5413 }
5414 Ok(v)
5415}
5416
5417fn json_revive(key: &str, val: Value, reviver: &Value) -> Result<Value, String> {
5420 match with_host(|h| h.get(&val).cloned()) {
5421 Some(JsObj::Array(items)) => {
5422 for i in 0..items.len() {
5423 let elem = with_host(|h| match h.get(&val) {
5424 Some(JsObj::Array(it)) => it[i].clone(),
5425 _ => Value::Undef,
5426 });
5427 let nv = json_revive(&i.to_string(), elem, reviver)?;
5428 with_host(|h| {
5429 if let Some(JsObj::Array(it)) = h.get_mut(&val) {
5430 it[i] = nv;
5431 }
5432 });
5433 }
5434 }
5435 Some(JsObj::Object(props)) => {
5436 let keys: Vec<String> = props
5437 .keys()
5438 .filter(|k| !k.starts_with("@@"))
5439 .cloned()
5440 .collect();
5441 for k in keys {
5442 let elem = with_host(|h| match h.get(&val) {
5443 Some(JsObj::Object(p)) => p.get(&k).cloned().unwrap_or(Value::Undef),
5444 _ => Value::Undef,
5445 });
5446 let nv = json_revive(&k, elem, reviver)?;
5447 with_host(|h| {
5448 if let Some(JsObj::Object(p)) = h.get_mut(&val) {
5449 if matches!(nv, Value::Undef) {
5450 p.shift_remove(&k);
5451 } else {
5452 p.insert(k.clone(), nv);
5453 }
5454 }
5455 });
5456 }
5457 }
5458 _ => {}
5459 }
5460 let kv = with_host(|h| h.new_str(key.to_string()));
5461 host::invoke(reviver, vec![kv, val], None)
5462}
5463
5464struct JsonParser {
5465 chars: Vec<char>,
5466 pos: usize,
5467}
5468impl JsonParser {
5469 fn peek(&self) -> Option<char> {
5470 self.chars.get(self.pos).copied()
5471 }
5472
5473 fn at(&self, pos: usize) -> String {
5477 let mut line = 1usize;
5478 let mut col = 1usize;
5479 for c in &self.chars[..pos.min(self.chars.len())] {
5480 if *c == '\n' {
5481 line += 1;
5482 col = 1;
5483 } else {
5484 col += 1;
5485 }
5486 }
5487 format!(" at position {pos} (line {line} column {col})")
5488 }
5489
5490 fn err_at(&self, what: &str, pos: usize) -> String {
5492 format!("SyntaxError: {what} in JSON{}", self.at(pos))
5493 }
5494
5495 fn err_trailing(&self, pos: usize) -> String {
5497 format!(
5498 "SyntaxError: Unexpected non-whitespace character after JSON{}",
5499 self.at(pos)
5500 )
5501 }
5502
5503 fn err_token(&self, pos: usize) -> String {
5508 const MAX_WHOLE: usize = 20;
5509 const CONTEXT: usize = 10;
5510 let len = self.chars.len();
5511 let Some(c) = self.chars.get(pos) else {
5512 return "SyntaxError: Unexpected end of JSON input".into();
5513 };
5514 let whole: String = self.chars.iter().collect();
5517 if matches!(
5518 whole.as_str(),
5519 "undefined" | "NaN" | "Infinity" | "-Infinity"
5520 ) {
5521 return format!("SyntaxError: \"{whole}\" is not valid JSON");
5522 }
5523 let snippet = if len <= MAX_WHOLE {
5524 format!("\"{whole}\"")
5525 } else {
5526 let start = pos.saturating_sub(CONTEXT);
5527 let end = (pos + CONTEXT).min(len);
5528 let body: String = self.chars[start..end].iter().collect();
5529 let head = if start > 0 { "..." } else { "" };
5530 let tail = if end < len { "..." } else { "" };
5531 format!("{head}\"{body}\"{tail}")
5532 };
5533 format!("SyntaxError: Unexpected token '{c}', {snippet} is not valid JSON")
5534 }
5535
5536 fn skip_ws(&mut self) {
5537 while matches!(
5538 self.peek(),
5539 Some(' ') | Some('\n') | Some('\t') | Some('\r')
5540 ) {
5541 self.pos += 1;
5542 }
5543 }
5544 fn parse_value(&mut self) -> Result<Value, String> {
5545 self.skip_ws();
5546 match self.peek() {
5547 Some('{') => self.parse_object(),
5548 Some('[') => self.parse_array(),
5549 Some('"') => {
5550 let s = self.parse_string()?;
5551 Ok(with_host(|h| h.new_str(s)))
5552 }
5553 Some('t') | Some('f') => self.parse_bool(),
5554 Some('n') => {
5555 self.expect_lit("null")?;
5556 Ok(with_host(|h| h.null()))
5557 }
5558 Some(c) if c == '-' || c.is_ascii_digit() => self.parse_number(),
5559 None => Err("SyntaxError: Unexpected end of JSON input".into()),
5560 _ => Err(self.err_token(self.pos)),
5561 }
5562 }
5563 fn expect_lit(&mut self, lit: &str) -> Result<(), String> {
5564 for ch in lit.chars() {
5565 match self.peek() {
5566 Some(c) if c == ch => self.pos += 1,
5567 None => return Err("SyntaxError: Unexpected end of JSON input".into()),
5570 _ => return Err(self.err_token(self.pos)),
5571 }
5572 }
5573 Ok(())
5574 }
5575 fn parse_bool(&mut self) -> Result<Value, String> {
5576 if self.peek() == Some('t') {
5577 self.expect_lit("true")?;
5578 Ok(Value::Bool(true))
5579 } else {
5580 self.expect_lit("false")?;
5581 Ok(Value::Bool(false))
5582 }
5583 }
5584 fn parse_number(&mut self) -> Result<Value, String> {
5589 let start = self.pos;
5590 if self.peek() == Some('-') {
5591 self.pos += 1;
5592 if !matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
5593 return Err(self.err_at("No number after minus sign", self.pos));
5594 }
5595 }
5596 if self.peek() == Some('0') {
5597 self.pos += 1;
5598 } else {
5599 while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
5600 self.pos += 1;
5601 }
5602 }
5603 if self.peek() == Some('.') {
5604 self.pos += 1;
5605 if !matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
5606 return Err(self.err_at("Unterminated fractional number", self.pos));
5607 }
5608 while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
5609 self.pos += 1;
5610 }
5611 }
5612 if matches!(self.peek(), Some('e') | Some('E')) {
5613 self.pos += 1;
5614 if matches!(self.peek(), Some('+') | Some('-')) {
5615 self.pos += 1;
5616 }
5617 if !matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
5618 return Err(self.err_at("Exponent part is missing a number", self.pos));
5619 }
5620 while matches!(self.peek(), Some(c) if c.is_ascii_digit()) {
5621 self.pos += 1;
5622 }
5623 }
5624 let s: String = self.chars[start..self.pos].iter().collect();
5625 s.parse::<f64>()
5626 .map(Value::Float)
5627 .map_err(|_| self.err_at("Unexpected number", start))
5628 }
5629 fn parse_string(&mut self) -> Result<String, String> {
5630 self.pos += 1; let mut out = String::new();
5632 loop {
5633 match self.peek() {
5634 None => return Err(self.err_at("Unterminated string", self.pos)),
5635 Some('"') => {
5636 self.pos += 1;
5637 break;
5638 }
5639 Some('\\') => {
5640 self.pos += 1;
5641 match self.peek() {
5642 Some('n') => out.push('\n'),
5643 Some('t') => out.push('\t'),
5644 Some('r') => out.push('\r'),
5645 Some('"') => out.push('"'),
5646 Some('\\') => out.push('\\'),
5647 Some('/') => out.push('/'),
5648 Some('b') => out.push('\u{08}'),
5649 Some('f') => out.push('\u{0C}'),
5650 Some('u') => {
5651 let h: String = self.chars
5652 [self.pos + 1..(self.pos + 5).min(self.chars.len())]
5653 .iter()
5654 .collect();
5655 if let Ok(n) = u32::from_str_radix(&h, 16) {
5656 if let Some(ch) = char::from_u32(n) {
5657 out.push(ch);
5658 }
5659 }
5660 self.pos += 4;
5661 }
5662 _ => {}
5663 }
5664 self.pos += 1;
5665 }
5666 Some(c) if (c as u32) < 0x20 => {
5669 return Err(self.err_at("Bad control character in string literal", self.pos))
5670 }
5671 Some(c) => {
5672 out.push(c);
5673 self.pos += 1;
5674 }
5675 }
5676 }
5677 Ok(out)
5678 }
5679 fn parse_array(&mut self) -> Result<Value, String> {
5680 self.pos += 1; let mut items = Vec::new();
5682 self.skip_ws();
5683 if self.peek() == Some(']') {
5684 self.pos += 1;
5685 return Ok(with_host(|h| h.new_array(items)));
5686 }
5687 loop {
5688 items.push(self.parse_value()?);
5689 self.skip_ws();
5690 match self.peek() {
5691 Some(',') => {
5692 self.pos += 1;
5693 }
5694 Some(']') => {
5695 self.pos += 1;
5696 break;
5697 }
5698 _ => return Err(self.err_at("Expected ',' or ']' after array element", self.pos)),
5699 }
5700 }
5701 Ok(with_host(|h| h.new_array(items)))
5702 }
5703 fn parse_object(&mut self) -> Result<Value, String> {
5704 self.pos += 1; let mut props: IndexMap<String, Value> = IndexMap::new();
5706 self.skip_ws();
5707 if self.peek() == Some('}') {
5708 self.pos += 1;
5709 return Ok(with_host(|h| h.new_object(props)));
5710 }
5711 loop {
5712 self.skip_ws();
5713 if self.peek() != Some('"') {
5714 return Err(if props.is_empty() {
5718 self.err_at("Expected property name or '}'", self.pos)
5719 } else {
5720 self.err_at("Expected double-quoted property name", self.pos)
5721 });
5722 }
5723 let key = self.parse_string()?;
5724 self.skip_ws();
5725 if self.peek() != Some(':') {
5726 return Err(match self.peek() {
5727 None => "SyntaxError: Unexpected end of JSON input".into(),
5728 _ => self.err_at("Expected ':' after property name", self.pos),
5729 });
5730 }
5731 self.pos += 1;
5732 let val = self.parse_value()?;
5733 props.insert(key, val);
5734 self.skip_ws();
5735 match self.peek() {
5736 Some(',') => {
5737 self.pos += 1;
5738 }
5739 Some('}') => {
5740 self.pos += 1;
5741 break;
5742 }
5743 _ => return Err(self.err_at("Expected ',' or '}' after property value", self.pos)),
5744 }
5745 }
5746 Ok(with_host(|h| h.new_object(props)))
5747 }
5748}
5749
5750fn is_array_method(name: &str) -> bool {
5753 matches!(
5754 name,
5755 "push"
5756 | "pop"
5757 | "shift"
5758 | "unshift"
5759 | "map"
5760 | "filter"
5761 | "forEach"
5762 | "join"
5763 | "slice"
5764 | "indexOf"
5765 | "lastIndexOf"
5766 | "includes"
5767 | "reduce"
5768 | "concat"
5769 | "reverse"
5770 | "sort"
5771 | "find"
5772 | "findIndex"
5773 | "some"
5774 | "every"
5775 | "flat"
5776 | "fill"
5777 | "splice"
5778 | "keys"
5779 | "values"
5780 | "entries"
5781 | "flatMap"
5782 | "at"
5783 | "toString"
5784 | "reduceRight"
5785 | "findLast"
5786 | "findLastIndex"
5787 | "copyWithin"
5788 )
5789}
5790fn is_string_method(name: &str) -> bool {
5791 matches!(
5792 name,
5793 "toUpperCase"
5794 | "toLowerCase"
5795 | "charAt"
5796 | "charCodeAt"
5797 | "codePointAt"
5798 | "indexOf"
5799 | "lastIndexOf"
5800 | "includes"
5801 | "slice"
5802 | "substring"
5803 | "substr"
5804 | "split"
5805 | "trim"
5806 | "trimStart"
5807 | "trimEnd"
5808 | "replace"
5809 | "replaceAll"
5810 | "repeat"
5811 | "startsWith"
5812 | "endsWith"
5813 | "padStart"
5814 | "padEnd"
5815 | "concat"
5816 | "at"
5817 | "toString"
5818 | "toLocaleString"
5819 | "valueOf"
5820 | "match"
5821 | "matchAll"
5822 | "search"
5823 | "normalize"
5824 | "localeCompare"
5825 | "toLocaleUpperCase"
5826 | "toLocaleLowerCase"
5827 | "isWellFormed"
5828 | "toWellFormed"
5829 )
5830}
5831
5832fn is_regexp_arg(v: &Value) -> bool {
5834 with_host(|h| h.kind_of(v)) == Some(ObjKind::RegExp)
5835}
5836
5837fn replace_str_fn(s: &str, pat: &str, repl: &Value, all: bool) -> Result<String, String> {
5840 if pat.is_empty() {
5841 return Ok(s.to_string());
5842 }
5843 let mut out = String::new();
5844 let mut rest = s;
5845 let mut base = 0usize;
5846 while let Some(pos) = rest.find(pat) {
5847 out.push_str(&rest[..pos]);
5848 let offset = base + pos;
5849 let m = with_host(|h| h.new_str(pat.to_string()));
5850 let str_arg = with_host(|h| h.new_str(s.to_string()));
5851 let r = host::invoke(repl, vec![m, Value::Float(offset as f64), str_arg], None)?;
5852 out.push_str(&with_host(|h| h.str_of(&r)));
5853 let consumed = pos + pat.len();
5854 base += consumed;
5855 rest = &rest[consumed..];
5856 if !all {
5857 break;
5858 }
5859 }
5860 out.push_str(rest);
5861 Ok(out)
5862}
5863fn is_number_method(name: &str) -> bool {
5864 matches!(
5865 name,
5866 "toFixed" | "toExponential" | "toString" | "toPrecision" | "toLocaleString" | "valueOf"
5867 )
5868}
5869
5870pub fn call_type_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
5872 if name == "valueOf"
5877 && matches!(
5878 with_host(|h| h.kind_of(recv)),
5879 Some(
5880 ObjKind::Array
5881 | ObjKind::Map
5882 | ObjKind::Set
5883 | ObjKind::Generator
5884 | ObjKind::Promise
5885 | ObjKind::Iter
5886 | ObjKind::RegExp
5887 )
5888 )
5889 {
5890 return Ok(recv.clone());
5891 }
5892 match with_host(|h| h.kind_of(recv)) {
5895 Some(ObjKind::Array) => array_method(recv, name, args),
5896 Some(ObjKind::Str) => {
5897 let s = peek(recv, |o| match o {
5900 JsObj::Str(s) => Some(s.clone()),
5901 _ => None,
5902 })
5903 .unwrap_or_default();
5904 string_method(&s, name, args)
5905 }
5906 Some(ObjKind::Map) => map_method(recv, name, args),
5907 Some(ObjKind::Set) => set_method(recv, name, args),
5908 Some(ObjKind::Generator) => generator_method(recv, name, args),
5909 Some(ObjKind::Promise) => promise_method(recv, name, args),
5910 Some(ObjKind::Iter) => iter_method(recv, name, args),
5911 Some(ObjKind::Symbol) => symbol_method(recv, name, args),
5912 Some(ObjKind::BigInt) => {
5913 let b = peek(recv, |o| match o {
5914 JsObj::BigInt(b) => Some(b.clone()),
5915 _ => None,
5916 })
5917 .unwrap_or_default();
5918 bigint_method(&b, name, args)
5919 }
5920 Some(ObjKind::RegExp) => crate::regexp::regexp_method(recv, name, args),
5921 Some(ObjKind::Func) | Some(ObjKind::Class) | Some(ObjKind::BoundFunc) => {
5922 match function_builtin_method(recv, name, &args)? {
5923 Some(v) => Ok(v),
5924 None => Err(host::type_error(&format!("{name} is not a function"))),
5925 }
5926 }
5927 Some(ObjKind::Object) => {
5928 if let Some(f) = peek(recv, |o| match o {
5929 JsObj::Object(p) => p.get(name).cloned(),
5930 _ => None,
5931 }) {
5932 host::invoke(&f, args, Some(recv.clone()))
5933 } else if name == "hasOwnProperty" {
5934 let k = with_host(|h| h.str_of(&arg0(&args)));
5935 let has = peek(recv, |o| match o {
5936 JsObj::Object(p) => Some(p.contains_key(&k)),
5937 _ => None,
5938 })
5939 .unwrap_or(false);
5940 Ok(Value::Bool(has))
5941 } else if name == "toString" {
5942 Ok(with_host(|h| h.new_str("[object Object]")))
5943 } else {
5944 Err(host::type_error(&format!("{} is not a function", name)))
5945 }
5946 }
5947 _ => {
5948 if let Value::Float(_) | Value::Int(_) = recv {
5950 return number_method(with_host(|h| h.to_number(recv)), name, args);
5951 }
5952 if let Some(s) = with_host(|h| h.as_str(recv)) {
5953 return string_method(&s, name, args);
5954 }
5955 if let Value::Bool(b) = recv {
5962 return match name {
5963 "toString" | "toLocaleString" => {
5964 Ok(new_s(if *b { "true" } else { "false" }.to_string()))
5965 }
5966 "valueOf" => Ok(Value::Bool(*b)),
5967 _ => Err(host::type_error(&format!("{name} is not a function"))),
5968 };
5969 }
5970 Err(host::type_error(&format!("{} is not a function", name)))
5971 }
5972 }
5973}
5974
5975fn array_items(recv: &Value) -> Vec<Value> {
5979 with_host(|h| match h.get(recv) {
5980 Some(JsObj::Array(items)) => items.clone(),
5981 _ => Vec::new(),
5982 })
5983}
5984
5985fn hole_set(recv: &Value) -> rustc_hash::FxHashSet<usize> {
5995 with_host(|h| h.hole_indices(recv)).into_iter().collect()
5996}
5997
5998fn array_len(recv: &Value) -> usize {
6000 peek(recv, |o| match o {
6001 JsObj::Array(items) => Some(items.len()),
6002 _ => None,
6003 })
6004 .unwrap_or(0)
6005}
6006
6007fn array_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
6008 array_method_on(recv, recv, name, args)
6009}
6010
6011const ARRAY_MUTATORS: &[&str] = &[
6014 "push",
6015 "pop",
6016 "shift",
6017 "unshift",
6018 "splice",
6019 "sort",
6020 "reverse",
6021 "fill",
6022 "copyWithin",
6023];
6024
6025fn array_generic(recv: &Value, method: &str, args: Vec<Value>) -> Result<Value, String> {
6038 let len = match get_property(recv, "length") {
6039 Ok(v) => host::to_array_length(&v).unwrap_or(0),
6040 Err(_) => 0,
6041 };
6042 let dense = with_host(|h| h.as_str(recv)).is_some();
6046 let mut items = Vec::with_capacity(len);
6047 let mut holes: rustc_hash::FxHashSet<usize> = rustc_hash::FxHashSet::default();
6048 for i in 0..len {
6049 let k = i.to_string();
6050 if dense || has_property(recv, &k)? {
6051 items.push(get_property(recv, &k)?);
6052 } else {
6053 holes.insert(i);
6054 items.push(Value::Undef);
6055 }
6056 }
6057 let tmp = with_host(|h| {
6058 let a = h.new_array(items);
6059 h.install_holes(&a, holes);
6060 a
6061 });
6062 let out = array_method_on(&tmp, recv, method, args)?;
6063 if ARRAY_MUTATORS.contains(&method) {
6064 let result = with_host(|h| match h.get(&tmp) {
6065 Some(JsObj::Array(items)) => items.clone(),
6066 _ => Vec::new(),
6067 });
6068 for (i, v) in result.iter().enumerate() {
6069 set_property(recv, &i.to_string(), v.clone())?;
6070 }
6071 set_property(recv, "length", Value::Float(result.len() as f64))?;
6072 }
6073 Ok(out)
6074}
6075
6076fn array_method_on(
6083 recv: &Value,
6084 this_value: &Value,
6085 name: &str,
6086 args: Vec<Value>,
6087) -> Result<Value, String> {
6088 match name {
6089 "push" => {
6090 let len = with_host(|h| {
6093 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
6094 items.extend(args.iter().cloned());
6095 items.len()
6096 } else {
6097 0
6098 }
6099 });
6100 Ok(Value::Float(len as f64))
6101 }
6102 "pop" => Ok(with_host(|h| {
6103 let popped = if let Some(JsObj::Array(items)) = h.get_mut(recv) {
6104 items.pop().unwrap_or(Value::Undef)
6105 } else {
6106 Value::Undef
6107 };
6108 let len = match h.get(recv) {
6109 Some(JsObj::Array(items)) => items.len(),
6110 _ => 0,
6111 };
6112 h.truncate_holes(recv, len);
6113 popped
6114 })),
6115 "shift" => Ok(with_host(|h| {
6116 let shifted = if let Some(JsObj::Array(items)) = h.get_mut(recv) {
6117 if items.is_empty() {
6118 Value::Undef
6119 } else {
6120 items.remove(0)
6121 }
6122 } else {
6123 Value::Undef
6124 };
6125 h.remap_holes(recv, |i| i.checked_sub(1));
6126 shifted
6127 })),
6128 "unshift" => {
6129 with_host(|h| {
6130 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
6131 for (i, a) in args.iter().enumerate() {
6132 items.insert(i, a.clone());
6133 }
6134 }
6135 let n = args.len();
6136 h.remap_holes(recv, |i| Some(i + n));
6137 });
6138 Ok(Value::Float(array_len(recv) as f64))
6139 }
6140 "join" => {
6141 let sep = if args.is_empty() {
6142 ",".to_string()
6143 } else {
6144 with_host(|h| h.str_of(&args[0]))
6145 };
6146 join_array(recv, &sep)
6147 }
6148 "toLocaleString" => {
6153 if !host::join_stack_push(recv) {
6156 return Ok(with_host(|h| h.new_str(String::new())));
6157 }
6158 let items = array_items(recv);
6159 let mut parts: Vec<String> = Vec::with_capacity(items.len());
6160 for it in &items {
6161 if with_host(|h| h.is_nullish(it)) {
6162 parts.push(String::new());
6163 continue;
6164 }
6165 let v = match host::call_method(it, "toLocaleString", Vec::new()) {
6166 Ok(v) => v,
6167 Err(e) => {
6168 host::join_stack_pop();
6169 return Err(e);
6170 }
6171 };
6172 parts.push(with_host(|h| h.str_of(&v)));
6173 }
6174 host::join_stack_pop();
6175 Ok(with_host(|h| h.new_str(parts.join(","))))
6176 }
6177 "indexOf" => {
6181 let items = array_items(recv);
6182 let holes = hole_set(recv);
6183 let target = arg0(&args);
6184 let idx = with_host(|h| {
6185 items
6186 .iter()
6187 .enumerate()
6188 .position(|(i, x)| !holes.contains(&i) && h.strict_eq(x, &target))
6189 });
6190 Ok(Value::Float(idx.map(|i| i as f64).unwrap_or(-1.0)))
6191 }
6192 "lastIndexOf" => {
6193 let items = array_items(recv);
6194 let holes = hole_set(recv);
6195 let target = arg0(&args);
6196 let idx = with_host(|h| {
6197 items
6198 .iter()
6199 .enumerate()
6200 .rposition(|(i, x)| !holes.contains(&i) && h.strict_eq(x, &target))
6201 });
6202 Ok(Value::Float(idx.map(|i| i as f64).unwrap_or(-1.0)))
6203 }
6204 "includes" => {
6205 let items = array_items(recv);
6207 let target = arg0(&args);
6208 let tnan = matches!(target, Value::Float(f) if f.is_nan());
6209 Ok(Value::Bool(with_host(|h| {
6210 items.iter().any(|x| {
6211 (tnan && matches!(x, Value::Float(f) if f.is_nan())) || h.strict_eq(x, &target)
6212 })
6213 })))
6214 }
6215 "slice" => {
6216 let items = array_items(recv);
6217 let (lo, hi) = slice_bounds(&args, items.len());
6218 Ok(with_host(|h| {
6219 let out = h.new_array(items[lo..hi].to_vec());
6220 h.copy_holes(recv, &out, |i| (i >= lo && i < hi).then(|| i - lo));
6221 out
6222 }))
6223 }
6224 "concat" => {
6225 let mut out = array_items(recv);
6226 let mut holes = hole_set(recv);
6229 let mut sources: Vec<(Value, usize)> = Vec::new();
6230 for a in &args {
6231 match with_host(|h| h.get(a).cloned()) {
6232 Some(JsObj::Array(items)) => {
6233 sources.push((a.clone(), out.len()));
6234 out.extend(items);
6235 }
6236 _ => out.push(a.clone()),
6237 }
6238 }
6239 for (src, base) in sources {
6240 holes.extend(
6241 with_host(|h| h.hole_indices(&src))
6242 .into_iter()
6243 .map(|i| i + base),
6244 );
6245 }
6246 Ok(with_host(|h| {
6247 let arr = h.new_array(out);
6248 h.install_holes(&arr, holes);
6249 arr
6250 }))
6251 }
6252 "reverse" => {
6253 let len = array_len(recv);
6254 with_host(|h| {
6255 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
6256 items.reverse();
6257 }
6258 h.remap_holes(recv, |i| Some(len - 1 - i));
6259 });
6260 Ok(this_value.clone())
6261 }
6262 "fill" => {
6263 let val = arg0(&args);
6265 let len = array_len(recv) as i64;
6266 let norm =
6267 |v: i64| -> usize { (if v < 0 { (len + v).max(0) } else { v.min(len) }) as usize };
6268 let start = if args.len() >= 2 {
6269 norm(arg_num(&args, 1) as i64)
6270 } else {
6271 0
6272 };
6273 let end = if args.len() >= 3 {
6274 norm(arg_num(&args, 2) as i64)
6275 } else {
6276 len as usize
6277 };
6278 with_host(|h| {
6279 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
6280 for it in items.iter_mut().take(end).skip(start) {
6281 *it = val.clone();
6282 }
6283 }
6284 h.remap_holes(recv, |i| (i < start || i >= end).then_some(i));
6286 });
6287 Ok(this_value.clone())
6288 }
6289 "copyWithin" => {
6290 let items = array_items(recv);
6292 let len = items.len() as i64;
6293 let norm =
6294 |v: i64| -> usize { (if v < 0 { (len + v).max(0) } else { v.min(len) }) as usize };
6295 let target = norm(arg_num(&args, 0) as i64);
6296 let start = if args.len() >= 2 {
6297 norm(arg_num(&args, 1) as i64)
6298 } else {
6299 0
6300 };
6301 let end = if args.len() >= 3 {
6302 norm(arg_num(&args, 2) as i64)
6303 } else {
6304 len as usize
6305 };
6306 let slice: Vec<Value> = items[start..end.max(start)].to_vec();
6307 let copied = slice.len();
6308 let src_holes = hole_set(recv);
6312 with_host(|h| {
6313 if let Some(JsObj::Array(a)) = h.get_mut(recv) {
6314 for (k, v) in slice.into_iter().enumerate() {
6315 if target + k < a.len() {
6316 a[target + k] = v;
6317 }
6318 }
6319 }
6320 let len = len as usize;
6321 let mut holes: rustc_hash::FxHashSet<usize> = src_holes
6322 .iter()
6323 .copied()
6324 .filter(|i| *i < target || *i >= (target + copied).min(len))
6325 .collect();
6326 for k in 0..copied {
6327 if target + k < len && src_holes.contains(&(start + k)) {
6328 holes.insert(target + k);
6329 }
6330 }
6331 h.install_holes(recv, holes);
6332 });
6333 Ok(this_value.clone())
6334 }
6335 "at" => {
6336 let items = array_items(recv);
6337 let mut i = arg_num(&args, 0) as i64;
6338 if i < 0 {
6339 i += items.len() as i64;
6340 }
6341 Ok(if i >= 0 && (i as usize) < items.len() {
6342 items[i as usize].clone()
6343 } else {
6344 Value::Undef
6345 })
6346 }
6347 "map" => {
6351 let items = array_items(recv);
6352 let holes = hole_set(recv);
6353 let cb = arg0(&args);
6354 let mut out = Vec::with_capacity(items.len());
6355 for (i, it) in items.iter().enumerate() {
6356 if holes.contains(&i) {
6357 out.push(Value::Undef);
6358 continue;
6359 }
6360 out.push(host::invoke(
6361 &cb,
6362 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6363 None,
6364 )?);
6365 }
6366 Ok(with_host(|h| {
6367 let arr = h.new_array(out);
6368 h.install_holes(&arr, holes);
6369 arr
6370 }))
6371 }
6372 "flatMap" => {
6373 let items = array_items(recv);
6374 let cb = arg0(&args);
6375 let holes = hole_set(recv);
6376 let mut out = Vec::new();
6377 for (i, it) in items.iter().enumerate() {
6378 if holes.contains(&i) {
6379 continue;
6380 }
6381 let r = host::invoke(
6382 &cb,
6383 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6384 None,
6385 )?;
6386 match with_host(|h| h.get(&r).cloned()) {
6387 Some(JsObj::Array(inner)) => out.extend(inner),
6388 _ => out.push(r),
6389 }
6390 }
6391 Ok(with_host(|h| h.new_array(out)))
6392 }
6393 "filter" => {
6394 let items = array_items(recv);
6395 let holes = hole_set(recv);
6396 let cb = arg0(&args);
6397 let mut out = Vec::new();
6398 for (i, it) in items.iter().enumerate() {
6399 if holes.contains(&i) {
6400 continue;
6401 }
6402 let keep = host::invoke(
6403 &cb,
6404 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6405 None,
6406 )?;
6407 if with_host(|h| h.truthy(&keep)) {
6408 out.push(it.clone());
6409 }
6410 }
6411 Ok(with_host(|h| h.new_array(out)))
6412 }
6413 "forEach" => {
6414 let items = array_items(recv);
6415 let holes = hole_set(recv);
6416 let cb = arg0(&args);
6417 for (i, it) in items.iter().enumerate() {
6418 if holes.contains(&i) {
6419 continue;
6420 }
6421 host::invoke(
6422 &cb,
6423 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6424 None,
6425 )?;
6426 }
6427 Ok(Value::Undef)
6428 }
6429 "find" => {
6430 let items = array_items(recv);
6431 let cb = arg0(&args);
6432 for (i, it) in items.iter().enumerate() {
6433 let m = host::invoke(
6434 &cb,
6435 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6436 None,
6437 )?;
6438 if with_host(|h| h.truthy(&m)) {
6439 return Ok(it.clone());
6440 }
6441 }
6442 Ok(Value::Undef)
6443 }
6444 "findIndex" => {
6445 let items = array_items(recv);
6446 let cb = arg0(&args);
6447 for (i, it) in items.iter().enumerate() {
6448 let m = host::invoke(
6449 &cb,
6450 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6451 None,
6452 )?;
6453 if with_host(|h| h.truthy(&m)) {
6454 return Ok(Value::Float(i as f64));
6455 }
6456 }
6457 Ok(Value::Float(-1.0))
6458 }
6459 "some" => {
6460 let items = array_items(recv);
6461 let holes = hole_set(recv);
6462 let cb = arg0(&args);
6463 for (i, it) in items.iter().enumerate() {
6464 if holes.contains(&i) {
6465 continue;
6466 }
6467 let m = host::invoke(
6468 &cb,
6469 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6470 None,
6471 )?;
6472 if with_host(|h| h.truthy(&m)) {
6473 return Ok(Value::Bool(true));
6474 }
6475 }
6476 Ok(Value::Bool(false))
6477 }
6478 "every" => {
6479 let items = array_items(recv);
6480 let holes = hole_set(recv);
6481 let cb = arg0(&args);
6482 for (i, it) in items.iter().enumerate() {
6483 if holes.contains(&i) {
6484 continue;
6485 }
6486 let m = host::invoke(
6487 &cb,
6488 vec![it.clone(), Value::Float(i as f64), this_value.clone()],
6489 None,
6490 )?;
6491 if !with_host(|h| h.truthy(&m)) {
6492 return Ok(Value::Bool(false));
6493 }
6494 }
6495 Ok(Value::Bool(true))
6496 }
6497 "reduce" => {
6498 let items = array_items(recv);
6499 let holes = hole_set(recv);
6500 let cb = arg0(&args);
6501 let mut acc;
6502 let mut start = 0;
6503 if args.len() >= 2 {
6504 acc = args[1].clone();
6505 } else {
6506 match (0..items.len()).find(|i| !holes.contains(i)) {
6509 Some(i) => {
6510 acc = items[i].clone();
6511 start = i + 1;
6512 }
6513 None => {
6514 return Err(host::type_error(
6515 "Reduce of empty array with no initial value",
6516 ))
6517 }
6518 }
6519 }
6520 for (i, it) in items.iter().enumerate().skip(start) {
6521 if holes.contains(&i) {
6522 continue;
6523 }
6524 acc = host::invoke(
6525 &cb,
6526 vec![acc, it.clone(), Value::Float(i as f64), this_value.clone()],
6527 None,
6528 )?;
6529 }
6530 Ok(acc)
6531 }
6532 "reduceRight" => {
6533 let items = array_items(recv);
6534 let holes = hole_set(recv);
6535 let cb = arg0(&args);
6536 let n = items.len();
6537 let mut acc;
6538 let mut i = n; if args.len() >= 2 {
6540 acc = args[1].clone();
6541 } else {
6542 match (0..n).rev().find(|i| !holes.contains(i)) {
6543 Some(k) => {
6544 acc = items[k].clone();
6545 i = k;
6546 }
6547 None => {
6548 return Err(host::type_error(
6549 "Reduce of empty array with no initial value",
6550 ))
6551 }
6552 }
6553 }
6554 while i > 0 {
6555 i -= 1;
6556 if holes.contains(&i) {
6557 continue;
6558 }
6559 acc = host::invoke(
6560 &cb,
6561 vec![
6562 acc,
6563 items[i].clone(),
6564 Value::Float(i as f64),
6565 this_value.clone(),
6566 ],
6567 None,
6568 )?;
6569 }
6570 Ok(acc)
6571 }
6572 "findLast" => {
6573 let items = array_items(recv);
6574 let cb = arg0(&args);
6575 for i in (0..items.len()).rev() {
6576 let m = host::invoke(
6577 &cb,
6578 vec![items[i].clone(), Value::Float(i as f64), this_value.clone()],
6579 None,
6580 )?;
6581 if with_host(|h| h.truthy(&m)) {
6582 return Ok(items[i].clone());
6583 }
6584 }
6585 Ok(Value::Undef)
6586 }
6587 "findLastIndex" => {
6588 let items = array_items(recv);
6589 let cb = arg0(&args);
6590 for i in (0..items.len()).rev() {
6591 let m = host::invoke(
6592 &cb,
6593 vec![items[i].clone(), Value::Float(i as f64), this_value.clone()],
6594 None,
6595 )?;
6596 if with_host(|h| h.truthy(&m)) {
6597 return Ok(Value::Float(i as f64));
6598 }
6599 }
6600 Ok(Value::Float(-1.0))
6601 }
6602 "sort" => {
6606 let all = array_items(recv);
6607 let holes = hole_set(recv);
6608 let mut items: Vec<Value> = all
6609 .iter()
6610 .enumerate()
6611 .filter(|(i, _)| !holes.contains(i))
6612 .map(|(_, v)| v.clone())
6613 .collect();
6614 sort_values(&mut items, args.first())?;
6615 let present = items.len();
6616 items.resize(all.len(), Value::Undef);
6617 with_host(|h| {
6618 if let Some(JsObj::Array(a)) = h.get_mut(recv) {
6619 *a = items;
6620 }
6621 h.install_holes(recv, (present..all.len()).collect());
6622 });
6623 Ok(this_value.clone())
6624 }
6625 "toSorted" => {
6627 let mut items = array_items(recv);
6628 sort_values(&mut items, args.first())?;
6629 Ok(with_host(|h| h.new_array(items)))
6630 }
6631 "toReversed" => {
6632 let mut items = array_items(recv);
6633 items.reverse();
6634 Ok(with_host(|h| h.new_array(items)))
6635 }
6636 "toSpliced" => {
6637 let mut items = array_items(recv);
6638 let len = items.len();
6639 let start = {
6640 let s = arg_num(&args, 0);
6641 if s < 0.0 {
6642 ((len as f64 + s).max(0.0)) as usize
6643 } else {
6644 (s as usize).min(len)
6645 }
6646 };
6647 let delete = if args.len() >= 2 {
6648 (arg_num(&args, 1).max(0.0) as usize).min(len - start)
6649 } else if args.is_empty() {
6650 0
6651 } else {
6652 len - start
6653 };
6654 let inserts: Vec<Value> = args.iter().skip(2).cloned().collect();
6655 items.splice(start..start + delete, inserts);
6656 Ok(with_host(|h| h.new_array(items)))
6657 }
6658 "with" => {
6659 let mut items = array_items(recv);
6660 let len = items.len() as i64;
6661 let rel = arg_num(&args, 0) as i64;
6662 let idx = if rel < 0 { len + rel } else { rel };
6663 if idx < 0 || idx >= len {
6664 return Err(host::range_error(&format!("Invalid index : {rel}")));
6665 }
6666 items[idx as usize] = args.get(1).cloned().unwrap_or(Value::Undef);
6667 Ok(with_host(|h| h.new_array(items)))
6668 }
6669 "flat" => {
6670 let raw = if args.is_empty() {
6673 1.0
6674 } else {
6675 arg_num(&args, 0)
6676 };
6677 let depth = if raw.is_nan() {
6678 0.0
6679 } else if raw.is_infinite() {
6680 raw
6681 } else {
6682 raw.trunc()
6683 };
6684 let mut out = Vec::new();
6685 flatten_into(recv, depth, &mut out)?;
6686 Ok(with_host(|h| h.new_array(out)))
6687 }
6688 "keys" => {
6689 let n = array_len(recv);
6690 let items: Vec<Value> = (0..n).map(|i| Value::Float(i as f64)).collect();
6691 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
6692 }
6693 "values" | "@@iterator" => {
6694 let items = array_items(recv);
6695 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
6696 }
6697 "entries" => {
6698 let items = array_items(recv);
6699 let pairs: Vec<Value> = items
6700 .into_iter()
6701 .enumerate()
6702 .map(|(i, v)| with_host(|h| h.new_array(vec![Value::Float(i as f64), v])))
6703 .collect();
6704 Ok(with_host(|h| {
6705 h.alloc(JsObj::Iter {
6706 items: pairs,
6707 idx: 0,
6708 })
6709 }))
6710 }
6711 "splice" => array_splice(recv, args),
6712 "toString" => join_array(recv, ","),
6717 _ if is_object_builtin_method(name) => object_builtin_method(recv, name, args),
6722 _ => Err(host::type_error(&format!("{name} is not a function"))),
6723 }
6724}
6725
6726fn join_array(recv: &Value, sep: &str) -> Result<Value, String> {
6733 if !host::join_stack_push(recv) {
6734 return Ok(with_host(|h| h.new_str(String::new())));
6735 }
6736 let parts = join_parts(&array_items(recv));
6737 host::join_stack_pop();
6738 let s = parts?.join(sep);
6739 Ok(with_host(|h| h.new_str(s)))
6740}
6741
6742fn join_parts(items: &[Value]) -> Result<Vec<String>, String> {
6752 let fast = with_host(|h| {
6753 items
6754 .iter()
6755 .map(|x| match x {
6756 Value::Undef => Some(String::new()),
6757 _ if h.is_null(x) => Some(String::new()),
6758 _ if matches!(h.get(x), Some(JsObj::Symbol { .. })) => None,
6762 _ if host::is_primitive(h, x) => Some(h.str_of(x)),
6763 _ => None,
6764 })
6765 .collect::<Vec<_>>()
6766 });
6767 if fast.iter().all(Option::is_some) {
6768 return Ok(fast.into_iter().flatten().collect());
6769 }
6770 let mut out = Vec::with_capacity(items.len());
6771 for (x, p) in items.iter().zip(fast) {
6772 match p {
6773 Some(s) => out.push(s),
6774 None => {
6775 let s = host::to_string_value(x)?;
6776 out.push(with_host(|h| h.str_of(&s)));
6777 }
6778 }
6779 }
6780 Ok(out)
6781}
6782
6783pub(crate) fn sort_values(items: &mut [Value], cmp: Option<&Value>) -> Result<(), String> {
6794 let cmp = match cmp {
6798 Some(Value::Undef) => None,
6799 Some(v) if !with_host(|h| host::is_callable(h, v)) => {
6800 let shown = with_host(|h| h.inspect(v));
6801 return Err(host::type_error(&format!(
6802 "The comparison function must be either a function or undefined: {shown}"
6803 )));
6804 }
6805 other => other,
6806 };
6807 let mut defined = 0;
6814 for i in 0..items.len() {
6815 if !matches!(items[i], Value::Undef) {
6816 items.swap(defined, i);
6817 defined += 1;
6818 }
6819 }
6820 merge_sort(&mut items[..defined], cmp)
6821}
6822
6823fn sort_compare(a: &Value, b: &Value, cmp: Option<&Value>) -> Result<f64, String> {
6827 match cmp {
6828 Some(cb) => {
6829 let v = host::invoke(cb, vec![a.clone(), b.clone()], None)?;
6830 Ok(with_host(|h| h.to_number(&v)))
6831 }
6832 None => {
6833 let x = with_host(|h| h.str_of(a));
6837 let y = with_host(|h| h.str_of(b));
6838 if crate::utf16::cmp_units(&x, &y) == std::cmp::Ordering::Greater {
6839 Ok(1.0)
6840 } else {
6841 Ok(-1.0)
6842 }
6843 }
6844 }
6845}
6846
6847fn merge_sort(items: &mut [Value], cmp: Option<&Value>) -> Result<(), String> {
6851 let n = items.len();
6852 if n < 2 {
6853 return Ok(());
6854 }
6855 let mut src = items.to_vec();
6856 let mut dst = src.clone();
6857 let mut width = 1;
6858 while width < n {
6859 let mut lo = 0;
6860 while lo < n {
6861 let mid = (lo + width).min(n);
6862 let hi = (lo + 2 * width).min(n);
6863 merge(&src[lo..mid], &src[mid..hi], &mut dst[lo..hi], cmp)?;
6864 lo = hi;
6865 }
6866 std::mem::swap(&mut src, &mut dst);
6867 width *= 2;
6868 }
6869 items.clone_from_slice(&src);
6870 Ok(())
6871}
6872
6873fn merge(
6877 left: &[Value],
6878 right: &[Value],
6879 out: &mut [Value],
6880 cmp: Option<&Value>,
6881) -> Result<(), String> {
6882 let (mut i, mut j, mut k) = (0, 0, 0);
6883 while i < left.len() && j < right.len() {
6884 if sort_compare(&left[i], &right[j], cmp)? > 0.0 {
6885 out[k] = right[j].clone();
6886 j += 1;
6887 } else {
6888 out[k] = left[i].clone();
6889 i += 1;
6890 }
6891 k += 1;
6892 }
6893 for v in left[i..].iter().chain(&right[j..]) {
6894 out[k] = v.clone();
6895 k += 1;
6896 }
6897 Ok(())
6898}
6899
6900fn flatten_into(src: &Value, depth: f64, out: &mut Vec<Value>) -> Result<(), String> {
6912 if host::stack_exhausted() {
6913 return Err(host::stack_overflow_error());
6914 }
6915 let items = array_items(src);
6916 let holes = hole_set(src);
6917 for (i, it) in items.into_iter().enumerate() {
6918 if holes.contains(&i) {
6919 continue;
6920 }
6921 let nested = depth > 0.0 && with_host(|h| h.kind_of(&it)) == Some(ObjKind::Array);
6922 if nested {
6923 flatten_into(&it, depth - 1.0, out)?;
6924 } else {
6925 out.push(it);
6926 }
6927 }
6928 Ok(())
6929}
6930
6931fn array_splice(recv: &Value, args: Vec<Value>) -> Result<Value, String> {
6932 let len = array_len(recv);
6933 let start = {
6934 let s = arg_num(&args, 0);
6935 if s < 0.0 {
6936 ((len as f64 + s).max(0.0)) as usize
6937 } else {
6938 (s as usize).min(len)
6939 }
6940 };
6941 let delete = if args.len() >= 2 {
6942 (arg_num(&args, 1).max(0.0) as usize).min(len - start)
6943 } else {
6944 len - start
6945 };
6946 let inserts: Vec<Value> = args.iter().skip(2).cloned().collect();
6947 let inserted = inserts.len();
6948 let holes = hole_set(recv);
6951 let removed = with_host(|h| {
6952 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
6953 let removed: Vec<Value> = items.splice(start..start + delete, inserts).collect();
6954 removed
6955 } else {
6956 Vec::new()
6957 }
6958 });
6959 Ok(with_host(|h| {
6960 h.install_holes(
6961 recv,
6962 holes
6963 .iter()
6964 .filter_map(|&i| {
6965 if i < start {
6966 Some(i)
6967 } else if i < start + delete {
6968 None
6969 } else {
6970 Some(i - delete + inserted)
6971 }
6972 })
6973 .collect(),
6974 );
6975 let out = h.new_array(removed);
6976 h.install_holes(
6977 &out,
6978 holes
6979 .iter()
6980 .filter(|&&i| i >= start && i < start + delete)
6981 .map(|&i| i - start)
6982 .collect(),
6983 );
6984 out
6985 }))
6986}
6987
6988fn slice_bounds(args: &[Value], len: usize) -> (usize, usize) {
6989 let norm = |v: f64| -> usize {
6990 if v < 0.0 {
6991 ((len as f64 + v).max(0.0)) as usize
6992 } else {
6993 (v as usize).min(len)
6994 }
6995 };
6996 let lo = if args.is_empty() || matches!(args[0], Value::Undef) {
6997 0
6998 } else {
6999 norm(arg_num(args, 0))
7000 };
7001 let hi = if args.len() < 2 || matches!(args[1], Value::Undef) {
7002 len
7003 } else {
7004 norm(arg_num(args, 1))
7005 };
7006 (lo, hi.max(lo))
7009}
7010
7011fn string_method(s: &str, name: &str, args: Vec<Value>) -> Result<Value, String> {
7012 let u = crate::utf16::Units::of(s);
7016 match name {
7017 "@@iterator" => {
7022 let items: Vec<Value> = s.chars().map(|c| new_s(c.to_string())).collect();
7023 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
7024 }
7025 "toUpperCase" => Ok(new_s(s.to_uppercase())),
7026 "toLowerCase" => Ok(new_s(s.to_lowercase())),
7027 "toLocaleString" => Ok(new_s(s.to_string())),
7040 "toLocaleUpperCase" => Ok(new_s(s.to_uppercase())),
7041 "toLocaleLowerCase" => Ok(new_s(s.to_lowercase())),
7042 "localeCompare" => {
7045 let other = with_host(|h| h.str_of(&arg0(&args)));
7046 let (la, lb) = (s.to_lowercase(), other.to_lowercase());
7047 let r = match la.cmp(&lb) {
7048 std::cmp::Ordering::Less => -1.0,
7049 std::cmp::Ordering::Greater => 1.0,
7050 std::cmp::Ordering::Equal => {
7051 let mut t = 0.0;
7052 for (ca, cb) in s.chars().zip(other.chars()) {
7053 if ca != cb {
7054 t = if ca.is_lowercase() { -1.0 } else { 1.0 };
7055 break;
7056 }
7057 }
7058 t
7059 }
7060 };
7061 Ok(Value::Float(r))
7062 }
7063 "normalize" => {
7073 use unicode_normalization::UnicodeNormalization;
7074 let form = match args.first() {
7075 Some(v) if !matches!(v, Value::Undef) => with_host(|h| h.str_of(v)),
7076 _ => "NFC".to_string(),
7077 };
7078 let out = match form.as_str() {
7079 "NFC" => s.nfc().collect::<String>(),
7080 "NFD" => s.nfd().collect::<String>(),
7081 "NFKC" => s.nfkc().collect::<String>(),
7082 "NFKD" => s.nfkd().collect::<String>(),
7083 _ => {
7084 return Err(host::range_error(
7085 "The normalization form should be one of NFC, NFD, NFKC, NFKD.",
7086 ))
7087 }
7088 };
7089 Ok(new_s(out))
7090 }
7091 "isWellFormed" => Ok(Value::Bool(true)),
7100 "toWellFormed" => Ok(new_s(s.to_string())),
7101 "trim" => Ok(new_s(crate::utf16::js_trim(s).to_string())),
7103 "trimStart" => Ok(new_s(crate::utf16::js_trim_start(s).to_string())),
7104 "trimEnd" => Ok(new_s(crate::utf16::js_trim_end(s).to_string())),
7105 "toString" | "valueOf" => Ok(new_s(s.to_string())),
7106 "charAt" => {
7107 let at = unit_pos(arg_num(&args, 0)).and_then(|i| u.unit_str(i));
7108 Ok(new_s(at.unwrap_or_default()))
7109 }
7110 "at" => {
7111 let n = arg_num(&args, 0);
7112 let i = if n.is_nan() {
7115 Some(0i64)
7116 } else if n.is_finite() {
7117 let i = n.trunc() as i64;
7118 Some(if i < 0 { i + u.len() as i64 } else { i })
7119 } else {
7120 None
7121 };
7122 match i
7123 .and_then(|i| usize::try_from(i).ok())
7124 .and_then(|i| u.unit_str(i))
7125 {
7126 Some(c) => Ok(new_s(c)),
7127 None => Ok(Value::Undef),
7128 }
7129 }
7130 "charCodeAt" => {
7137 let unit = unit_pos(arg_num(&args, 0)).and_then(|i| u.unit(i));
7138 Ok(Value::Float(unit.map(f64::from).unwrap_or(f64::NAN)))
7139 }
7140 "codePointAt" => match unit_pos(arg_num(&args, 0)).and_then(|i| u.code_point(i)) {
7141 Some(cp) => Ok(Value::Float(f64::from(cp))),
7142 None => Ok(Value::Undef),
7143 },
7144 "indexOf" => {
7148 let needle = needle_units(&args);
7149 let from = clamp_pos(arg_num(&args, 1), u.len());
7150 Ok(Value::Float(
7151 search_from(u.as_slice(), needle.as_slice(), from)
7152 .map(|i| i as f64)
7153 .unwrap_or(-1.0),
7154 ))
7155 }
7156 "lastIndexOf" => {
7157 let needle = needle_units(&args);
7158 let n = arg_num(&args, 1);
7160 let upto = if n.is_nan() {
7161 u.len()
7162 } else {
7163 clamp_pos(n, u.len())
7164 };
7165 Ok(Value::Float(
7166 search_last(u.as_slice(), needle.as_slice(), upto)
7167 .map(|i| i as f64)
7168 .unwrap_or(-1.0),
7169 ))
7170 }
7171 "includes" => {
7172 let needle = needle_units(&args);
7173 let from = clamp_pos(arg_num(&args, 1), u.len());
7174 Ok(Value::Bool(
7175 search_from(u.as_slice(), needle.as_slice(), from).is_some(),
7176 ))
7177 }
7178 "startsWith" => {
7179 let needle = needle_units(&args);
7180 let from = clamp_pos(arg_num(&args, 1), u.len());
7181 Ok(Value::Bool(
7182 u.as_slice()[from..].starts_with(needle.as_slice()),
7183 ))
7184 }
7185 "endsWith" => {
7186 let needle = needle_units(&args);
7187 let end = if args.len() < 2 || matches!(args[1], Value::Undef) {
7189 u.len()
7190 } else {
7191 clamp_pos(arg_num(&args, 1), u.len())
7192 };
7193 Ok(Value::Bool(
7194 u.as_slice()[..end].ends_with(needle.as_slice()),
7195 ))
7196 }
7197 "slice" => {
7198 let (lo, hi) = slice_bounds(&args, u.len());
7199 Ok(new_s(u.slice(lo, hi)))
7200 }
7201 "substring" => {
7202 let mut a = arg_num(&args, 0).max(0.0) as usize;
7203 let mut b = if args.len() < 2 || matches!(args[1], Value::Undef) {
7204 u.len()
7205 } else {
7206 (arg_num(&args, 1).max(0.0) as usize).min(u.len())
7207 };
7208 a = a.min(u.len());
7209 if a > b {
7210 std::mem::swap(&mut a, &mut b);
7211 }
7212 Ok(new_s(u.slice(a, b)))
7213 }
7214 "substr" => {
7215 let len = u.len() as i64;
7217 let mut start = arg_num(&args, 0) as i64;
7218 if start < 0 {
7219 start = (len + start).max(0);
7220 }
7221 let start = (start as usize).min(u.len());
7222 let count = if args.len() >= 2 {
7223 arg_num(&args, 1).max(0.0) as usize
7224 } else {
7225 u.len()
7226 };
7227 let end = start.saturating_add(count).min(u.len());
7228 Ok(new_s(u.slice(start, end)))
7229 }
7230 "repeat" => {
7231 let n = arg_num(&args, 0);
7232 if n < 0.0 || !n.is_finite() {
7235 return Err(host::range_error(&format!(
7236 "Invalid count value: {}",
7237 host::fmt_number(n)
7238 )));
7239 }
7240 if n * crate::utf16::len(s) as f64 > host::MAX_STRING_LENGTH as f64 {
7245 return Err(host::invalid_string_length());
7246 }
7247 Ok(new_s(s.repeat(n as usize)))
7248 }
7249 "concat" => {
7250 let mut out = s.to_string();
7251 for a in &args {
7252 out.push_str(&with_host(|h| h.str_of(a)));
7253 }
7254 Ok(new_s(out))
7255 }
7256 "padStart" => Ok(new_s(pad(s, &args, true)?)),
7257 "padEnd" => Ok(new_s(pad(s, &args, false)?)),
7258 "match" => crate::regexp::str_match(s, &arg0(&args)),
7261 "matchAll" => crate::regexp::str_match_all(s, &arg0(&args)),
7262 "search" => {
7263 if is_regexp_arg(&arg0(&args)) {
7264 crate::regexp::str_search(s, &arg0(&args))
7265 } else {
7266 let needle = with_host(|h| h.str_of(&arg0(&args)));
7270 Ok(Value::Float(byte_to_unit_index(s, s.find(&needle))))
7271 }
7272 }
7273 "replace" => {
7274 let pat = arg0(&args);
7275 let repl = args.get(1).cloned().unwrap_or(Value::Undef);
7276 if is_regexp_arg(&pat) {
7277 crate::regexp::str_replace_regex(s, &pat, &repl, false)
7278 } else if with_host(|h| host::is_callable(h, &repl)) {
7279 Ok(new_s(replace_str_fn(
7280 s,
7281 &with_host(|h| h.str_of(&pat)),
7282 &repl,
7283 false,
7284 )?))
7285 } else {
7286 let from = with_host(|h| h.str_of(&pat));
7287 let to = with_host(|h| h.str_of(&repl));
7288 Ok(new_s(s.replacen(&from, &to, 1)))
7289 }
7290 }
7291 "replaceAll" => {
7292 let pat = arg0(&args);
7293 let repl = args.get(1).cloned().unwrap_or(Value::Undef);
7294 if is_regexp_arg(&pat) {
7295 crate::regexp::str_replace_regex(s, &pat, &repl, true)
7296 } else if with_host(|h| host::is_callable(h, &repl)) {
7297 Ok(new_s(replace_str_fn(
7298 s,
7299 &with_host(|h| h.str_of(&pat)),
7300 &repl,
7301 true,
7302 )?))
7303 } else {
7304 let from = with_host(|h| h.str_of(&pat));
7305 let to = with_host(|h| h.str_of(&repl));
7306 Ok(new_s(s.replace(&from, &to)))
7307 }
7308 }
7309 "split" => {
7310 if is_regexp_arg(&arg0(&args)) {
7311 let limit = args
7312 .get(1)
7313 .filter(|v| !matches!(v, Value::Undef))
7314 .map(|v| with_host(|h| h.to_number(v)) as usize);
7315 return crate::regexp::str_split_regex(s, &arg0(&args), limit);
7316 }
7317 let mut parts: Vec<Value> = if args.is_empty() || matches!(args[0], Value::Undef) {
7318 vec![new_s(s.to_string())]
7319 } else {
7320 let sep = with_host(|h| h.str_of(&args[0]));
7321 if sep.is_empty() {
7322 (0..u.len())
7325 .filter_map(|i| u.unit_str(i))
7326 .map(new_s)
7327 .collect()
7328 } else {
7329 s.split(&sep as &str)
7330 .map(|p| new_s(p.to_string()))
7331 .collect()
7332 }
7333 };
7334 if let Some(lim) = args.get(1).filter(|v| !matches!(v, Value::Undef)) {
7336 let n = with_host(|h| h.to_number(lim));
7337 if n.is_finite() && n >= 0.0 {
7338 parts.truncate(n as usize);
7339 }
7340 }
7341 Ok(with_host(|h| h.new_array(parts)))
7342 }
7343 _ => Err(host::type_error(&format!("{name} is not a function"))),
7344 }
7345}
7346
7347fn new_s(s: String) -> Value {
7348 with_host(|h| h.new_str(s))
7349}
7350
7351fn clamp_pos(n: f64, len: usize) -> usize {
7354 if n.is_nan() || n <= 0.0 {
7355 0
7356 } else if n >= len as f64 {
7357 len
7358 } else {
7359 n.trunc() as usize
7360 }
7361}
7362
7363fn unit_pos(n: f64) -> Option<usize> {
7367 if n.is_nan() {
7368 Some(0)
7369 } else if n < 0.0 || !n.is_finite() {
7370 None
7371 } else {
7372 Some(n.trunc() as usize)
7373 }
7374}
7375
7376fn needle_units(args: &[Value]) -> crate::utf16::Units {
7379 crate::utf16::Units::of(&with_host(|h| h.str_of(&arg0(args))))
7380}
7381
7382fn search_from(hay: &[u16], needle: &[u16], from: usize) -> Option<usize> {
7385 if needle.is_empty() {
7386 return Some(from.min(hay.len()));
7387 }
7388 if needle.len() > hay.len() {
7389 return None;
7390 }
7391 (from..=hay.len().saturating_sub(needle.len())).find(|&i| &hay[i..i + needle.len()] == needle)
7392}
7393
7394fn search_last(hay: &[u16], needle: &[u16], upto: usize) -> Option<usize> {
7396 if needle.is_empty() {
7397 return Some(upto.min(hay.len()));
7398 }
7399 if needle.len() > hay.len() {
7400 return None;
7401 }
7402 let last = hay.len() - needle.len();
7403 (0..=upto.min(last))
7404 .rev()
7405 .find(|&i| &hay[i..i + needle.len()] == needle)
7406}
7407
7408fn byte_to_unit_index(s: &str, byte: Option<usize>) -> f64 {
7411 match byte {
7412 Some(b) => crate::utf16::index_of_byte(s, b).get() as f64,
7413 None => -1.0,
7414 }
7415}
7416
7417fn pad(s: &str, args: &[Value], start: bool) -> Result<String, String> {
7418 let target_f = arg_num(args, 0);
7419 let target = if target_f.is_finite() && target_f > 0.0 {
7420 target_f as usize
7421 } else {
7422 0
7423 };
7424 let cur = crate::utf16::len(s);
7427 if cur >= target {
7428 return Ok(s.to_string());
7429 }
7430 let filler = if args.len() >= 2 {
7431 with_host(|h| h.str_of(&args[1]))
7432 } else {
7433 " ".to_string()
7434 };
7435 if filler.is_empty() {
7436 return Ok(s.to_string());
7437 }
7438 if target_f > host::MAX_STRING_LENGTH as f64 {
7442 return Err(host::invalid_string_length());
7443 }
7444 let need = target - cur;
7445 let fill = crate::utf16::Units::of(&filler);
7446 let units: Vec<u16> = (0..need)
7450 .filter_map(|i| fill.unit(i % fill.len()))
7451 .collect();
7452 let padding = crate::utf16::to_string_lossy(&units);
7453 Ok(if start {
7454 format!("{padding}{s}")
7455 } else {
7456 format!("{s}{padding}")
7457 })
7458}
7459
7460const RADIX_RANGE: &str = "toString() radix argument must be between 2 and 36";
7465
7466fn bigint_method(b: &num_bigint::BigInt, name: &str, args: Vec<Value>) -> Result<Value, String> {
7468 match name {
7469 "toString" => {
7470 let radix = match args.first() {
7471 None | Some(Value::Undef) => 10,
7472 Some(_) => {
7473 let t = arg_num(&args, 0).trunc();
7474 if !(2.0..=36.0).contains(&t) {
7475 return Err(host::range_error(RADIX_RANGE));
7476 }
7477 t as u32
7478 }
7479 };
7480 Ok(new_s(b.to_str_radix(radix)))
7481 }
7482 "toLocaleString" => {
7488 let digits = b.magnitude().to_string();
7489 let sign = if b.sign() == num_bigint::Sign::Minus {
7490 "-"
7491 } else {
7492 ""
7493 };
7494 Ok(new_s(format!("{sign}{}", group_thousands(&digits))))
7495 }
7496 "valueOf" => Ok(with_host(|h| h.new_bigint(b.clone()))),
7497 _ => Err(host::type_error(&format!("{name} is not a function"))),
7498 }
7499}
7500
7501fn number_method(n: f64, name: &str, args: Vec<Value>) -> Result<Value, String> {
7502 match name {
7503 "toFixed" => {
7504 let digits = arg_num(&args, 0);
7505 if !(0.0..=100.0).contains(&digits.trunc()) {
7506 return Err(host::range_error(
7507 "toFixed() digits argument must be between 0 and 100",
7508 ));
7509 }
7510 Ok(new_s(to_fixed(n, digits as usize)))
7511 }
7512 "toExponential" => {
7513 let f = match args.first() {
7515 None | Some(Value::Undef) => None,
7516 Some(_) => {
7517 let d = arg_num(&args, 0).trunc();
7518 if !(0.0..=100.0).contains(&d) {
7519 return Err(host::range_error(
7520 "toExponential() argument must be between 0 and 100",
7521 ));
7522 }
7523 Some(d as usize)
7524 }
7525 };
7526 Ok(new_s(to_exponential(n, f)))
7527 }
7528 "toString" => {
7529 let radix = match args.first() {
7533 None | Some(Value::Undef) => 10,
7534 Some(_) => {
7535 let r = arg_num(&args, 0);
7536 let t = r.trunc();
7537 if !(2.0..=36.0).contains(&t) {
7538 return Err(host::range_error(RADIX_RANGE));
7539 }
7540 t as u32
7541 }
7542 };
7543 if radix == 10 {
7544 Ok(new_s(host::fmt_number(n)))
7545 } else {
7546 Ok(new_s(to_radix(n, radix)))
7547 }
7548 }
7549 "toPrecision" => {
7550 match args.first() {
7552 None | Some(Value::Undef) => Ok(new_s(host::fmt_number(n))),
7553 Some(_) => {
7554 let p = arg_num(&args, 0).trunc();
7555 if !(1.0..=100.0).contains(&p) {
7556 return Err(host::range_error(
7557 "toPrecision() argument must be between 1 and 100",
7558 ));
7559 }
7560 Ok(new_s(to_precision(n, p as usize)))
7561 }
7562 }
7563 }
7564 "toLocaleString" => Ok(new_s(to_locale_string(n))),
7565 "valueOf" => Ok(Value::Float(n)),
7566 _ => Err(host::type_error(&format!("{name} is not a function"))),
7567 }
7568}
7569
7570fn to_locale_string(n: f64) -> String {
7577 if n.is_nan() {
7578 return "NaN".to_string();
7579 }
7580 if n.is_infinite() {
7581 return if n < 0.0 { "-∞" } else { "∞" }.to_string();
7582 }
7583 let neg = n.is_sign_negative();
7584 let fixed = expand_exponential(&to_fixed(n.abs(), 3));
7595 let trimmed = match fixed.split_once('.') {
7596 Some(_) => fixed.trim_end_matches('0').trim_end_matches('.'),
7597 None => fixed.as_str(),
7598 };
7599 let (int_part, frac_part) = match trimmed.split_once('.') {
7600 Some((i, f)) => (i, Some(f)),
7601 None => (trimmed, None),
7602 };
7603 let mut out = String::new();
7604 if neg {
7605 out.push('-'); }
7607 out.push_str(&group_thousands(int_part));
7608 if let Some(f) = frac_part {
7609 out.push('.');
7610 out.push_str(f);
7611 }
7612 out
7613}
7614
7615fn expand_exponential(s: &str) -> String {
7621 let Some((mantissa, exp)) = s.split_once(['e', 'E']) else {
7622 return s.to_string();
7623 };
7624 let Ok(exp) = exp.trim_start_matches('+').parse::<i32>() else {
7625 return s.to_string();
7626 };
7627 if exp <= 0 {
7628 return s.to_string();
7629 }
7630 let (int_digits, frac_digits) = match mantissa.split_once('.') {
7631 Some((i, f)) => (i.to_string(), f.to_string()),
7632 None => (mantissa.to_string(), String::new()),
7633 };
7634 let mut digits = int_digits;
7635 digits.push_str(&frac_digits);
7636 let zeros = exp as usize - frac_digits.len().min(exp as usize);
7639 digits.push_str(&"0".repeat(zeros));
7640 digits
7641}
7642
7643fn group_thousands(int_part: &str) -> String {
7645 let bytes = int_part.as_bytes();
7646 let n = bytes.len();
7647 let mut out = String::with_capacity(n + n / 3);
7648 for (i, &b) in bytes.iter().enumerate() {
7649 if i > 0 && (n - i) % 3 == 0 {
7650 out.push(',');
7651 }
7652 out.push(b as char);
7653 }
7654 out
7655}
7656
7657fn to_fixed(n: f64, f: usize) -> String {
7666 if !n.is_finite() {
7667 return host::fmt_number(n);
7668 }
7669 if n.abs() >= 1e21 {
7671 return host::fmt_number(n);
7672 }
7673 let neg = n < 0.0;
7674 let full = format!("{:.*}", f + 25, n.abs());
7677 let mut body = round_decimal_string(&full, f);
7678 if neg {
7679 body.insert(0, '-'); }
7681 body
7682}
7683
7684fn round_decimal_string(s: &str, f: usize) -> String {
7687 let (int_part, frac_part) = s.split_once('.').unwrap_or((s, ""));
7688 let mut digits: Vec<u8> = int_part
7689 .bytes()
7690 .chain(frac_part.bytes())
7691 .map(|b| b - b'0')
7692 .collect();
7693 let point = int_part.len(); let keep = point + f; if digits.get(keep).map(|&d| d >= 5).unwrap_or(false) {
7698 let mut i = keep;
7699 loop {
7700 if i == 0 {
7701 digits.insert(0, 1);
7702 return assemble_decimal(&digits, point + 1, f);
7704 }
7705 i -= 1;
7706 if digits[i] == 9 {
7707 digits[i] = 0;
7708 } else {
7709 digits[i] += 1;
7710 break;
7711 }
7712 }
7713 }
7714 assemble_decimal(&digits, point, f)
7715}
7716
7717fn assemble_decimal(digits: &[u8], point: usize, f: usize) -> String {
7720 let int_str: String = digits[..point].iter().map(|d| (d + b'0') as char).collect();
7721 let int_str = int_str.trim_start_matches('0');
7722 let int_str = if int_str.is_empty() { "0" } else { int_str };
7723 if f == 0 {
7724 return int_str.to_string();
7725 }
7726 let frac: String = digits[point..point + f]
7727 .iter()
7728 .map(|d| (d + b'0') as char)
7729 .collect();
7730 format!("{int_str}.{frac}")
7731}
7732
7733fn round_significant(a: f64, p: usize) -> (String, i32) {
7739 let sci = format!("{a:.*e}", p - 1 + 25);
7740 let (mant, exp_str) = sci.split_once('e').expect("LowerExp always has 'e'");
7741 let mut e: i32 = exp_str.parse().expect("LowerExp exponent is an integer");
7742 let all: Vec<u8> = mant
7743 .chars()
7744 .filter(|c| c.is_ascii_digit())
7745 .map(|c| c as u8 - b'0')
7746 .collect();
7747 let mut s: String = all[..p].iter().map(|d| (d + b'0') as char).collect();
7748 if all.get(p).map(|&d| d >= 5).unwrap_or(false) {
7749 let mut d: Vec<u8> = all[..p].to_vec();
7752 let mut i = p;
7753 loop {
7754 if i == 0 {
7755 d.insert(0, 1);
7756 d.truncate(p);
7757 e += 1;
7758 break;
7759 }
7760 i -= 1;
7761 if d[i] == 9 {
7762 d[i] = 0;
7763 } else {
7764 d[i] += 1;
7765 break;
7766 }
7767 }
7768 s = d.iter().map(|x| (x + b'0') as char).collect();
7769 }
7770 (s, e)
7771}
7772
7773fn to_exponential(n: f64, f: Option<usize>) -> String {
7779 if !n.is_finite() {
7780 return host::fmt_number(n);
7781 }
7782 let neg = n < 0.0;
7783 let a = n.abs();
7784 let (s, e) = if a == 0.0 {
7785 ("0".repeat(f.unwrap_or(0) + 1), 0)
7787 } else {
7788 match f {
7789 Some(f) => round_significant(a, f + 1),
7790 None => {
7791 let sci = format!("{a:e}");
7793 let (mant, exp_str) = sci.split_once('e').expect("LowerExp always has 'e'");
7794 let digits: String = mant.chars().filter(|c| c.is_ascii_digit()).collect();
7795 let trimmed = digits.trim_end_matches('0');
7796 let digits = if trimmed.is_empty() { "0" } else { trimmed };
7797 (digits.to_string(), exp_str.parse().unwrap_or(0))
7798 }
7799 }
7800 };
7801 let sign = if e >= 0 { '+' } else { '-' };
7802 let mag = e.abs();
7803 let body = if s.len() == 1 {
7804 format!("{s}e{sign}{mag}")
7805 } else {
7806 format!("{}.{}e{sign}{mag}", &s[..1], &s[1..])
7807 };
7808 if neg {
7809 format!("-{body}")
7810 } else {
7811 body
7812 }
7813}
7814
7815fn to_precision(n: f64, p: usize) -> String {
7820 if !n.is_finite() {
7821 return host::fmt_number(n);
7822 }
7823 if n == 0.0 {
7824 return if p == 1 {
7825 "0".into()
7826 } else {
7827 format!("0.{}", "0".repeat(p - 1))
7828 };
7829 }
7830 let neg = n < 0.0;
7831 let (s, e) = round_significant(n.abs(), p);
7832 let pp = p as i32;
7833
7834 let body = if e < -6 || e >= pp {
7835 let sign = if e >= 0 { '+' } else { '-' };
7837 let mag = e.abs();
7838 if p == 1 {
7839 format!("{s}e{sign}{mag}")
7840 } else {
7841 format!("{}.{}e{sign}{mag}", &s[..1], &s[1..])
7842 }
7843 } else if e >= 0 {
7844 let ip = (e + 1) as usize;
7846 if ip == p {
7847 s
7848 } else {
7849 format!("{}.{}", &s[..ip], &s[ip..])
7850 }
7851 } else {
7852 format!("0.{}{}", "0".repeat((-e - 1) as usize), s)
7854 };
7855 if neg {
7856 format!("-{body}")
7857 } else {
7858 body
7859 }
7860}
7861
7862fn to_radix(n: f64, radix: u32) -> String {
7868 if !n.is_finite() {
7869 return host::fmt_number(n);
7870 }
7871 let digits = b"0123456789abcdefghijklmnopqrstuvwxyz";
7872 let rf = radix as f64;
7873 let neg = n < 0.0;
7874 let value = n.abs();
7875
7876 let mut integer = value.floor();
7877 let mut fraction = value - integer;
7878
7879 let mut frac: Vec<u8> = Vec::new();
7881 let mut delta = 0.5 * (next_up(value) - value);
7883 delta = delta.max(next_up(0.0));
7884 if fraction >= delta {
7885 loop {
7886 fraction *= rf;
7888 delta *= rf;
7889 let digit = fraction as usize;
7890 frac.push(digits[digit]);
7891 fraction -= digit as f64;
7892 if (fraction > 0.5 || (fraction == 0.5 && (digit & 1) == 1)) && fraction + delta > 1.0 {
7894 loop {
7896 match frac.pop() {
7897 None => {
7898 integer += 1.0;
7900 break;
7901 }
7902 Some(c) => {
7903 let d = if c > b'9' {
7904 (c - b'a' + 10) as u32
7905 } else {
7906 (c - b'0') as u32
7907 };
7908 if d + 1 < radix {
7909 frac.push(digits[(d + 1) as usize]);
7910 break;
7911 }
7912 }
7914 }
7915 }
7916 break;
7917 }
7918 if fraction < delta {
7919 break;
7920 }
7921 }
7922 }
7923
7924 let mut int_out: Vec<u8> = Vec::new();
7926 while v8_exponent(integer / rf) > 0 {
7928 integer /= rf;
7929 int_out.push(b'0');
7930 }
7931 loop {
7932 let remainder = integer % rf;
7933 int_out.push(digits[remainder as usize]);
7934 integer = (integer - remainder) / rf;
7935 if integer <= 0.0 {
7936 break;
7937 }
7938 }
7939 int_out.reverse();
7940
7941 let mut out: Vec<u8> = Vec::new();
7942 if neg {
7943 out.push(b'-');
7944 }
7945 out.extend_from_slice(&int_out);
7946 if !frac.is_empty() {
7947 out.push(b'.');
7948 out.extend_from_slice(&frac);
7949 }
7950 String::from_utf8(out).unwrap()
7951}
7952
7953fn next_up(x: f64) -> f64 {
7955 f64::from_bits(x.to_bits() + 1)
7956}
7957
7958fn v8_exponent(x: f64) -> i32 {
7961 let biased = ((x.to_bits() >> 52) & 0x7ff) as i32;
7962 if biased == 0 {
7963 -1074 } else {
7965 biased - 1075
7966 }
7967}
7968
7969fn normalize_zero_key(v: Value) -> Value {
7976 match v {
7977 Value::Float(f) if f == 0.0 && f.is_sign_negative() => Value::Float(0.0),
7978 other => other,
7979 }
7980}
7981
7982fn map_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
7983 match name {
7984 "get" => {
7985 let key = with_host(|h| host::map_key(h, &arg0(&args)));
7986 Ok(with_host(|h| match h.get(recv) {
7987 Some(JsObj::Map { entries, .. }) => entries
7988 .get(&key)
7989 .map(|(_, v)| v.clone())
7990 .unwrap_or(Value::Undef),
7991 _ => Value::Undef,
7992 }))
7993 }
7994 "set" => {
7995 let kv = normalize_zero_key(arg0(&args));
7996 let vv = args.get(1).cloned().unwrap_or(Value::Undef);
7997 reject_non_object_weak_key(recv, &kv, "WeakMap")?;
7998 let key = with_host(|h| host::map_key(h, &kv));
7999 with_host(|h| {
8000 if let Some(JsObj::Map { entries, .. }) = h.get_mut(recv) {
8001 entries.insert(key, (kv, vv));
8002 }
8003 });
8004 Ok(recv.clone())
8005 }
8006 "has" => {
8007 let key = with_host(|h| host::map_key(h, &arg0(&args)));
8008 Ok(Value::Bool(with_host(
8009 |h| matches!(h.get(recv), Some(JsObj::Map { entries, .. }) if entries.contains_key(&key)),
8010 )))
8011 }
8012 "delete" => {
8013 let key = with_host(|h| host::map_key(h, &arg0(&args)));
8014 Ok(Value::Bool(with_host(|h| match h.get_mut(recv) {
8015 Some(JsObj::Map { entries, .. }) => entries.shift_remove(&key).is_some(),
8016 _ => false,
8017 })))
8018 }
8019 "clear" => {
8020 with_host(|h| {
8021 if let Some(JsObj::Map { entries, .. }) = h.get_mut(recv) {
8022 entries.clear();
8023 }
8024 });
8025 Ok(Value::Undef)
8026 }
8027 "forEach" => {
8028 let cb = arg0(&args);
8029 let pairs: Vec<(Value, Value)> = with_host(|h| match h.get(recv) {
8030 Some(JsObj::Map { entries, .. }) => entries.values().cloned().collect(),
8031 _ => Vec::new(),
8032 });
8033 for (k, v) in pairs {
8034 host::invoke(&cb, vec![v, k, recv.clone()], None)?;
8035 }
8036 Ok(Value::Undef)
8037 }
8038 "keys" | "values" | "entries" | "@@iterator" => {
8039 let items: Vec<Value> = with_host(|h| {
8040 let pairs: Vec<(Value, Value)> = match h.get(recv) {
8041 Some(JsObj::Map { entries, .. }) => entries.values().cloned().collect(),
8042 _ => Vec::new(),
8043 };
8044 pairs
8045 .into_iter()
8046 .map(|(k, v)| match name {
8047 "keys" => k,
8048 "values" => v,
8049 _ => h.new_array(vec![k, v]), })
8051 .collect()
8052 });
8053 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
8054 }
8055 _ => Err(host::type_error(&format!("map.{name} is not a function"))),
8056 }
8057}
8058
8059fn reject_non_object_weak_key(recv: &Value, key: &Value, kind: &str) -> Result<(), String> {
8062 let weak = with_host(|h| {
8063 matches!(
8064 h.get(recv),
8065 Some(JsObj::Map { weak: true, .. }) | Some(JsObj::Set { weak: true, .. })
8066 )
8067 });
8068 if !weak {
8069 return Ok(());
8070 }
8071 let is_object = with_host(|h| match key {
8072 Value::Obj(_) => !h.is_null(key) && h.as_str(key).is_none() && h.as_bigint(key).is_none(),
8073 _ => false,
8074 });
8075 if is_object {
8076 return Ok(());
8077 }
8078 Err(host::type_error(if kind == "WeakMap" {
8079 "Invalid value used as weak map key"
8080 } else {
8081 "Invalid value used in weak set"
8082 }))
8083}
8084
8085fn set_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
8086 match name {
8087 "add" => {
8088 let vv = normalize_zero_key(arg0(&args));
8089 reject_non_object_weak_key(recv, &vv, "WeakSet")?;
8090 let key = with_host(|h| host::map_key(h, &vv));
8091 with_host(|h| {
8092 if let Some(JsObj::Set { entries, .. }) = h.get_mut(recv) {
8093 entries.insert(key, vv);
8094 }
8095 });
8096 Ok(recv.clone())
8097 }
8098 "has" => {
8099 let key = with_host(|h| host::map_key(h, &arg0(&args)));
8100 Ok(Value::Bool(with_host(
8101 |h| matches!(h.get(recv), Some(JsObj::Set { entries, .. }) if entries.contains_key(&key)),
8102 )))
8103 }
8104 "delete" => {
8105 let key = with_host(|h| host::map_key(h, &arg0(&args)));
8106 Ok(Value::Bool(with_host(|h| match h.get_mut(recv) {
8107 Some(JsObj::Set { entries, .. }) => entries.shift_remove(&key).is_some(),
8108 _ => false,
8109 })))
8110 }
8111 "clear" => {
8112 with_host(|h| {
8113 if let Some(JsObj::Set { entries, .. }) = h.get_mut(recv) {
8114 entries.clear();
8115 }
8116 });
8117 Ok(Value::Undef)
8118 }
8119 "forEach" => {
8120 let cb = arg0(&args);
8121 let vals: Vec<Value> = with_host(|h| match h.get(recv) {
8122 Some(JsObj::Set { entries, .. }) => entries.values().cloned().collect(),
8123 _ => Vec::new(),
8124 });
8125 for v in vals {
8126 host::invoke(&cb, vec![v.clone(), v, recv.clone()], None)?;
8127 }
8128 Ok(Value::Undef)
8129 }
8130 "keys" | "values" | "entries" | "@@iterator" => {
8131 let items: Vec<Value> = with_host(|h| {
8132 let vals: Vec<Value> = match h.get(recv) {
8133 Some(JsObj::Set { entries, .. }) => entries.values().cloned().collect(),
8134 _ => Vec::new(),
8135 };
8136 if name == "entries" {
8137 vals.into_iter()
8138 .map(|v| h.new_array(vec![v.clone(), v]))
8139 .collect()
8140 } else {
8141 vals
8142 }
8143 });
8144 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
8145 }
8146 _ => Err(host::type_error(&format!("set.{name} is not a function"))),
8147 }
8148}
8149
8150fn generator_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
8151 if host::is_async_generator(recv) {
8155 return match name {
8161 "next" => Ok(host::async_gen_enqueue(
8162 recv,
8163 host::GenReq::Next(arg0(&args)),
8164 )),
8165 "return" => Ok(host::async_gen_enqueue(
8166 recv,
8167 host::GenReq::Return(arg0(&args)),
8168 )),
8169 "throw" => Ok(host::async_gen_enqueue(
8170 recv,
8171 host::GenReq::Throw(arg0(&args)),
8172 )),
8173 "@@asyncIterator" => Ok(recv.clone()),
8174 _ => Err(host::type_error(&format!(
8175 "asyncGenerator.{name} is not a function"
8176 ))),
8177 };
8178 }
8179 match name {
8180 "next" => {
8181 let send = arg0(&args);
8182 match host::gen_resume(recv, send)? {
8183 host::GenStep::Yield(v) => Ok(iter_result(v, false)),
8184 host::GenStep::Done(v) => Ok(iter_result(v, true)),
8185 }
8186 }
8187 "return" => {
8188 match host::gen_return(recv, arg0(&args))? {
8191 host::GenStep::Yield(v) => Ok(iter_result(v, false)),
8192 host::GenStep::Done(v) => Ok(iter_result(v, true)),
8193 }
8194 }
8195 "throw" => {
8196 match host::gen_throw(recv, arg0(&args))? {
8200 host::GenStep::Yield(v) => Ok(iter_result(v, false)),
8201 host::GenStep::Done(v) => Ok(iter_result(v, true)),
8202 }
8203 }
8204 _ => Err(host::type_error(&format!(
8205 "generator.{name} is not a function"
8206 ))),
8207 }
8208}
8209
8210fn iter_result(value: Value, done: bool) -> Value {
8212 with_host(|h| {
8213 let mut m: IndexMap<String, Value> = IndexMap::new();
8214 m.insert("value".into(), value);
8215 m.insert("done".into(), Value::Bool(done));
8216 h.new_object(m)
8217 })
8218}
8219
8220fn iter_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
8223 match name {
8224 "next" => {
8225 let step = with_host(|h| {
8226 if let Some(JsObj::Iter { items, idx }) = h.get_mut(recv) {
8227 if *idx < items.len() {
8228 let v = items[*idx].clone();
8229 *idx += 1;
8230 return Some(v);
8231 }
8232 }
8233 None
8234 });
8235 Ok(match step {
8236 Some(v) => iter_result(v, false),
8237 None => iter_result(Value::Undef, true),
8238 })
8239 }
8240 "return" => {
8241 with_host(|h| {
8243 if let Some(JsObj::Iter { items, idx }) = h.get_mut(recv) {
8244 *idx = items.len();
8245 }
8246 });
8247 Ok(iter_result(arg0(&args), true))
8248 }
8249 "@@iterator" => Ok(recv.clone()),
8251 _ => Err(host::type_error(&format!(
8252 "iterator.{name} is not a function"
8253 ))),
8254 }
8255}
8256
8257fn symbol_method(recv: &Value, name: &str, _args: Vec<Value>) -> Result<Value, String> {
8258 match name {
8259 "toString" => Ok(with_host(|h| {
8260 let s = h.str_of(recv);
8261 h.new_str(s)
8262 })),
8263 _ => Err(host::type_error(&format!(
8264 "symbol.{name} is not a function"
8265 ))),
8266 }
8267}
8268
8269fn object_create(args: Vec<Value>) -> Result<Value, String> {
8272 let proto = arg0(&args);
8273 reject_bad_prototype(&proto)?;
8279 let obj = with_host(|h| h.new_object(IndexMap::new()));
8280 with_host(|h| h.set_proto(&obj, proto));
8282 if let Some(descs) = args.get(1).filter(|d| !matches!(d, Value::Undef)) {
8284 let entries: Vec<(String, Value)> = with_host(|h| match h.get(descs) {
8285 Some(JsObj::Object(p)) => p.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
8286 _ => Vec::new(),
8287 });
8288 for (k, d) in entries {
8289 apply_descriptor(&obj, &k, &d);
8290 }
8291 }
8292 Ok(obj)
8293}
8294
8295fn builtin_proto_method_names(ns: &str) -> Option<&'static [&'static str]> {
8299 match ns {
8300 "EventEmitter.prototype" => Some(crate::stdlib::events::METHODS),
8301 _ => None,
8302 }
8303}
8304
8305fn proxy_or_own_symbol_keys(v: &Value) -> Result<Vec<Value>, String> {
8309 if let Some(keys) = crate::proxy::own_keys(v)? {
8310 return Ok(keys
8311 .iter()
8312 .filter(|k| host::is_symbol_key(k))
8313 .map(|k| crate::proxy::key_value(k))
8314 .collect());
8315 }
8316 Ok(with_host(|h| h.own_symbol_keys(v)))
8317}
8318
8319pub fn define_property_pub(obj: &Value, key: Value, desc: Value) -> Result<Value, String> {
8321 object_define_property(vec![obj.clone(), key, desc])
8322}
8323
8324pub fn own_descriptor_pub(obj: &Value, key: Value) -> Result<Value, String> {
8326 object_get_own_descriptor(vec![obj.clone(), key])
8327}
8328
8329fn object_define_property(args: Vec<Value>) -> Result<Value, String> {
8330 let obj = arg0(&args);
8331 if with_host(|h| h.kind_of(&obj)) == Some(ObjKind::Proxy) {
8334 let key = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
8335 let desc = args.get(2).cloned().unwrap_or(Value::Undef);
8336 if !with_host(|h| is_object_like(h, &desc)) {
8337 return Err(host::type_error(&format!(
8338 "Property description must be an object: {}",
8339 with_host(|h| h.str_of(&desc))
8340 )));
8341 }
8342 crate::proxy::define_property(&obj, &key, &desc)?;
8343 return Ok(obj);
8344 }
8345 if !with_host(|h| is_object_like(h, &obj)) {
8348 return Err(host::type_error(
8349 "Object.defineProperty called on non-object",
8350 ));
8351 }
8352 let desc = args.get(2).cloned().unwrap_or(Value::Undef);
8353 if !with_host(|h| is_object_like(h, &desc)) {
8354 return Err(host::type_error(&format!(
8355 "Property description must be an object: {}",
8356 with_host(|h| h.str_of(&desc))
8357 )));
8358 }
8359 let key = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
8360 apply_descriptor(&obj, &key, &desc);
8361 Ok(obj)
8362}
8363
8364fn is_object_like(h: &host::JsHost, v: &Value) -> bool {
8368 matches!(v, Value::Obj(_)) && !h.is_null(v) && !host::is_primitive(h, v)
8369}
8370
8371fn require_object_coercible(v: &Value) -> Result<(), String> {
8378 if with_host(|h| matches!(v, Value::Undef) || h.is_null(v)) {
8379 return Err(host::type_error(
8380 "Cannot convert undefined or null to object",
8381 ));
8382 }
8383 Ok(())
8384}
8385
8386fn reject_bad_prototype(proto: &Value) -> Result<(), String> {
8391 if with_host(|h| h.is_null(proto) || is_object_like(h, proto)) {
8392 return Ok(());
8393 }
8394 Err(host::type_error(&format!(
8395 "Object prototype may only be an Object or null: {}",
8396 with_host(|h| h.str_of(proto))
8397 )))
8398}
8399
8400fn apply_descriptor(obj: &Value, key: &str, desc: &Value) {
8408 let (value, get, set, attrs) = with_host(|h| match h.get(desc) {
8409 Some(JsObj::Object(p)) => {
8410 let flag = |n: &str| p.get(n).map(|v| h.truthy(v)).unwrap_or(false);
8411 (
8412 p.get("value").cloned(),
8413 p.get("get").cloned(),
8414 p.get("set").cloned(),
8415 host::PropAttrs {
8416 writable: flag("writable"),
8417 enumerable: flag("enumerable"),
8418 configurable: flag("configurable"),
8419 },
8420 )
8421 }
8422 _ => (None, None, None, host::PropAttrs::default()),
8423 });
8424 with_host(|h| h.set_prop_attrs(obj, key, attrs));
8425 if get.is_some() || set.is_some() {
8426 with_host(|h| h.set_accessor(obj, key, get, set));
8427 } else if let Some(v) = value {
8428 if matches!(
8431 with_host(|h| h.get(obj).cloned()),
8432 Some(JsObj::Func(_)) | Some(JsObj::Class(_))
8433 ) {
8434 with_host(|h| h.set_fn_prop(obj, key, v));
8435 } else if let (Some(ObjKind::Array), Ok(i)) =
8436 (with_host(|h| h.kind_of(obj)), key.parse::<usize>())
8437 {
8438 with_host(|h| {
8444 let old = match h.get(obj) {
8445 Some(JsObj::Array(items)) => items.len(),
8446 _ => 0,
8447 };
8448 if let Some(JsObj::Array(items)) = h.get_mut(obj) {
8449 if i >= old {
8450 items.resize(i + 1, Value::Undef);
8451 }
8452 items[i] = v;
8453 }
8454 if i > old {
8455 h.mark_hole_range(obj, old..i);
8456 }
8457 h.clear_hole(obj, i);
8458 });
8459 } else {
8460 with_host(|h| {
8461 if let Some(JsObj::Object(p)) = h.get_mut(obj) {
8462 p.insert(key.to_string(), v);
8463 host::canonicalize_own_keys(p);
8464 }
8465 });
8466 }
8467 }
8468}
8469
8470fn object_define_properties(args: Vec<Value>) -> Result<Value, String> {
8472 let obj = arg0(&args);
8473 let descs = args.get(1).cloned().unwrap_or(Value::Undef);
8474 let entries: Vec<(String, Value)> = with_host(|h| match h.get(&descs) {
8475 Some(JsObj::Object(p)) => p.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
8476 _ => Vec::new(),
8477 });
8478 for (k, d) in entries {
8479 apply_descriptor(&obj, &k, &d);
8480 }
8481 Ok(obj)
8482}
8483
8484fn object_get_own_descriptor(args: Vec<Value>) -> Result<Value, String> {
8485 let obj = arg0(&args);
8486 require_object_coercible(&obj)?;
8487 let key = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
8488 if with_host(|h| h.kind_of(&obj)) == Some(ObjKind::Proxy) {
8489 return Ok(crate::proxy::get_own_descriptor(&obj, &key)?.unwrap_or(Value::Undef));
8490 }
8491 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(&obj).cloned()) {
8494 if let Some(names) = builtin_proto_method_names(&ns) {
8495 if names.contains(&key.as_str()) {
8496 return Ok(with_host(|h| {
8497 let thunk = h.alloc(JsObj::Builtin(format!(
8498 "@proto:{}:{key}",
8499 ns.trim_end_matches(".prototype")
8500 )));
8501 let mut m: IndexMap<String, Value> = IndexMap::new();
8502 m.insert("value".into(), thunk);
8503 m.insert("writable".into(), Value::Bool(true));
8504 m.insert("enumerable".into(), Value::Bool(true));
8505 m.insert("configurable".into(), Value::Bool(true));
8506 h.new_object(m)
8507 }));
8508 }
8509 }
8510 }
8511 if let Some((get, set)) = with_host(|h| h.own_accessor(&obj, &key)) {
8513 return Ok(with_host(|h| {
8514 let a = h.prop_attrs(&obj, &key);
8515 let mut m: IndexMap<String, Value> = IndexMap::new();
8516 m.insert("get".into(), get.unwrap_or(Value::Undef));
8517 m.insert("set".into(), set.unwrap_or(Value::Undef));
8518 m.insert("enumerable".into(), Value::Bool(a.enumerable));
8519 m.insert("configurable".into(), Value::Bool(a.configurable));
8520 h.new_object(m)
8521 }));
8522 }
8523 let val = with_host(|h| match h.get(&obj) {
8524 Some(JsObj::Object(p))
8528 if p.get("@@native").map(|t| h.str_of(t)).as_deref() == Some("Buffer") =>
8529 {
8530 match (
8531 p.get("@@bytes").and_then(|b| h.get(b)),
8532 key.parse::<usize>(),
8533 ) {
8534 (Some(JsObj::Array(items)), Ok(i)) => items.get(i).cloned(),
8535 _ => None,
8536 }
8537 }
8538 Some(JsObj::Object(p)) => p.get(&key).cloned(),
8539 Some(JsObj::Array(items)) => match key.parse::<usize>() {
8542 Ok(i) if h.is_hole(&obj, i) => None,
8544 Ok(i) => items.get(i).cloned(),
8545 Err(_) if key == "length" => Some(Value::Float(items.len() as f64)),
8546 Err(_) => h.fn_prop(&obj, &key),
8547 },
8548 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.fn_prop(&obj, &key),
8550 _ => None,
8551 });
8552 match val {
8553 Some(v) => Ok(with_host(|h| {
8554 let a = h.prop_attrs(&obj, &key);
8555 let mut m: IndexMap<String, Value> = IndexMap::new();
8556 m.insert("value".into(), v);
8557 m.insert("writable".into(), Value::Bool(a.writable));
8558 m.insert("enumerable".into(), Value::Bool(a.enumerable));
8559 m.insert("configurable".into(), Value::Bool(a.configurable));
8560 h.new_object(m)
8561 })),
8562 None => Ok(Value::Undef),
8563 }
8564}
8565
8566fn object_get_own_descriptors(args: Vec<Value>) -> Result<Value, String> {
8571 let obj = arg0(&args);
8572 let names = object_keys(vec![obj.clone()], 3)?;
8573 let keys: Vec<String> = with_host(|h| match h.get(&names) {
8574 Some(JsObj::Array(items)) => items.iter().map(|k| h.str_of(k)).collect(),
8575 _ => Vec::new(),
8576 });
8577 let mut out: IndexMap<String, Value> = IndexMap::new();
8578 for k in keys {
8579 let ks = with_host(|h| h.new_str(k.clone()));
8580 let d = object_get_own_descriptor(vec![obj.clone(), ks])?;
8581 if !matches!(d, Value::Undef) {
8582 out.insert(k, d);
8583 }
8584 }
8585 Ok(with_host(|h| h.new_object(out)))
8586}
8587
8588pub fn has_property(obj: &Value, key: &str) -> Result<bool, String> {
8591 if let Some(b) = crate::proxy::has(obj, key)? {
8592 return Ok(b);
8593 }
8594 Ok(has_property_ordinary(obj, key))
8595}
8596
8597fn has_property_ordinary(obj: &Value, key: &str) -> bool {
8599 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(obj).cloned()) {
8605 return !matches!(namespace_property(&ns, key), Value::Undef);
8606 }
8607 if crate::stdlib::typedarray::has_index(obj, key) == Some(true) {
8613 return true;
8614 }
8615 if with_host(|h| host::lookup_chain(h, obj, key)).is_some() {
8616 return true;
8617 }
8618 if with_host(|h| host::lookup_accessor(h, obj, key)).is_some() {
8619 return true;
8620 }
8621 with_host(|h| match h.get(obj) {
8622 Some(JsObj::Object(p)) => p.contains_key(key),
8623 Some(JsObj::Array(items)) => {
8624 key == "length"
8625 || key
8626 .parse::<usize>()
8627 .map(|i| i < items.len() && !h.is_hole(obj, i))
8628 .unwrap_or(false)
8629 || h.fn_prop(obj, key).is_some()
8632 }
8633 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.fn_prop(obj, key).is_some(),
8634 _ => false,
8635 })
8636}
8637
8638pub(crate) fn deep_clone(v: &Value) -> Value {
8645 deep_clone_seen(v, &mut std::collections::HashMap::new())
8646}
8647
8648fn deep_clone_seen(v: &Value, seen: &mut std::collections::HashMap<u32, Value>) -> Value {
8649 let idx = match v {
8650 Value::Obj(i) => *i,
8651 _ => return v.clone(),
8652 };
8653 if let Some(done) = seen.get(&idx) {
8654 return done.clone();
8655 }
8656 match with_host(|h| h.get(v).cloned()) {
8657 Some(JsObj::Array(items)) => {
8658 let out = with_host(|h| h.new_array(Vec::new()));
8661 seen.insert(idx, out.clone());
8662 let cloned: Vec<Value> = items.iter().map(|x| deep_clone_seen(x, seen)).collect();
8663 with_host(|h| {
8664 if let Some(JsObj::Array(a)) = h.get_mut(&out) {
8665 *a = cloned;
8666 }
8667 h.copy_holes(v, &out, Some);
8670 });
8671 out
8672 }
8673 Some(JsObj::Object(props)) => {
8674 let out = with_host(|h| h.new_object(IndexMap::new()));
8675 seen.insert(idx, out.clone());
8676 let cloned: IndexMap<String, Value> = props
8677 .iter()
8678 .map(|(k, val)| (k.clone(), deep_clone_seen(val, seen)))
8679 .collect();
8680 with_host(|h| {
8681 if let Some(JsObj::Object(p)) = h.get_mut(&out) {
8682 *p = cloned;
8683 }
8684 if let Some(p) = h.proto_of(v) {
8687 h.set_proto(&out, p);
8688 }
8689 h.copy_prop_attrs(v, &out);
8690 });
8691 out
8692 }
8693 Some(JsObj::Map { entries, weak }) => {
8695 let out = with_host(|h| {
8696 h.alloc(JsObj::Map {
8697 entries: IndexMap::new(),
8698 weak,
8699 })
8700 });
8701 seen.insert(idx, out.clone());
8702 let pairs: Vec<(Value, Value)> = entries.values().cloned().collect();
8703 for (k, val) in pairs {
8704 let ck = deep_clone_seen(&k, seen);
8705 let cv = deep_clone_seen(&val, seen);
8706 let _ = map_method(&out, "set", vec![ck, cv]);
8707 }
8708 out
8709 }
8710 Some(JsObj::Set { entries, weak }) => {
8711 let out = with_host(|h| {
8712 h.alloc(JsObj::Set {
8713 entries: IndexMap::new(),
8714 weak,
8715 })
8716 });
8717 seen.insert(idx, out.clone());
8718 let vals: Vec<Value> = entries.values().cloned().collect();
8719 for x in vals {
8720 let cx = deep_clone_seen(&x, seen);
8721 let _ = set_method(&out, "add", vec![cx]);
8722 }
8723 out
8724 }
8725 _ => v.clone(),
8729 }
8730}
8731
8732pub fn error_string(h: &host::JsHost, v: &Value) -> String {
8737 if let Some(JsObj::Object(props)) = h.get(v) {
8738 let name = props
8739 .get("name")
8740 .map(|x| h.str_of(x))
8741 .or_else(|| host::lookup_chain(h, v, "name").map(|x| h.str_of(&x)))
8742 .unwrap_or_else(|| "Error".into());
8743 if let Some(m) = props.get("message") {
8744 return format!("{name}: {}", h.str_of(m));
8745 }
8746 return name;
8747 }
8748 h.str_of(v)
8749}
8750
8751fn make_builtin(name: String) -> Value {
8752 with_host(|h| h.alloc(JsObj::Builtin(name)))
8753}
8754
8755pub fn prototype_of(v: &Value) -> Value {
8764 if matches!(with_host(|h| h.get(v).cloned()), Some(JsObj::Builtin(ref n)) if n == "Buffer") {
8769 return with_host(|h| h.alloc(JsObj::Builtin("Uint8Array".into())));
8770 }
8771 if let Some(JsObj::Class(c)) = with_host(|h| h.get(v).cloned()) {
8779 if let Some(parent) = c.parent {
8780 return parent;
8781 }
8782 }
8783 if with_host(|h| h.has_null_proto(v)) {
8785 return with_host(|h| h.null());
8786 }
8787 if let Some(p) = with_host(|h| h.proto_of(v)) {
8788 return p;
8789 }
8790 with_host(|h| {
8795 h.ensure_native_protos();
8796 match default_ctor_name(h, v) {
8797 Some("Object") => h.object_proto(),
8798 Some(c) => h.alloc(JsObj::Builtin(format!("{c}.prototype"))),
8799 None => h.null(),
8800 }
8801 })
8802}
8803
8804fn new_promise(executor: Value) -> Result<Value, String> {
8807 let p = with_host(|h| h.new_promise());
8808 let id = with_host(|h| h.promise_id(&p).unwrap());
8809 let res = make_builtin(format!("@@presolve:{id}"));
8810 let rej = make_builtin(format!("@@preject:{id}"));
8811 if let Err(e) = host::invoke(&executor, vec![res, rej], None) {
8812 let ev = host::take_exc_or_error(&e);
8814 host::reject_promise_val(id, ev);
8815 }
8816 Ok(p)
8817}
8818
8819fn promise_resolve(v: Value) -> Result<Value, String> {
8820 Ok(host::promise_of(&v))
8821}
8822fn promise_reject(v: Value) -> Result<Value, String> {
8823 let p = with_host(|h| h.new_promise());
8824 let id = with_host(|h| h.promise_id(&p).unwrap());
8825 host::reject_promise_val(id, v);
8826 Ok(p)
8827}
8828
8829fn promise_with_resolvers() -> Result<Value, String> {
8833 let p = with_host(|h| h.new_promise());
8834 let id = with_host(|h| h.promise_id(&p).unwrap());
8835 let resolve = make_builtin(format!("@@presolve:{id}"));
8836 let reject = make_builtin(format!("@@preject:{id}"));
8837 let mut props: IndexMap<String, Value> = IndexMap::new();
8838 props.insert("promise".into(), p);
8839 props.insert("resolve".into(), resolve);
8840 props.insert("reject".into(), reject);
8841 Ok(with_host(|h| h.new_object(props)))
8842}
8843
8844#[derive(Clone, Copy)]
8845enum AllMode {
8846 All,
8847 AllSettled,
8848}
8849
8850fn promise_all(args: Vec<Value>, mode: AllMode) -> Result<Value, String> {
8852 let items = host::iter_all(&arg0(&args))?;
8853 let result = with_host(|h| h.new_promise());
8854 let rid = with_host(|h| h.promise_id(&result).unwrap());
8855 let n = items.len();
8856 if n == 0 {
8857 let empty = with_host(|h| h.new_array(Vec::new()));
8858 host::resolve_promise_val(rid, empty);
8859 return Ok(result);
8860 }
8861 let slots = std::rc::Rc::new(std::cell::RefCell::new(vec![Value::Undef; n]));
8863 let remaining = std::rc::Rc::new(std::cell::RefCell::new(n));
8864 for (i, it) in items.into_iter().enumerate() {
8865 let ap = host::promise_of(&it);
8866 let aid = with_host(|h| h.promise_id(&ap).unwrap());
8867 let slots = slots.clone();
8868 let remaining = remaining.clone();
8869 host::subscribe_native(
8870 aid,
8871 Box::new(move |state, val| {
8872 let settled = match mode {
8873 AllMode::All => {
8874 if state == host::PromiseState::Rejected {
8875 host::reject_promise_val(rid, val);
8876 return Ok(());
8877 }
8878 val
8879 }
8880 AllMode::AllSettled => with_host(|h| {
8881 let mut m: IndexMap<String, Value> = IndexMap::new();
8882 if state == host::PromiseState::Rejected {
8883 m.insert("status".into(), h.new_str("rejected"));
8884 m.insert("reason".into(), val);
8885 } else {
8886 m.insert("status".into(), h.new_str("fulfilled"));
8887 m.insert("value".into(), val);
8888 }
8889 h.new_object(m)
8890 }),
8891 };
8892 slots.borrow_mut()[i] = settled;
8893 let mut r = remaining.borrow_mut();
8894 *r -= 1;
8895 if *r == 0 {
8896 let arr = with_host(|h| h.new_array(slots.borrow().clone()));
8897 host::resolve_promise_val(rid, arr);
8898 }
8899 Ok(())
8900 }),
8901 );
8902 }
8903 Ok(result)
8904}
8905
8906fn promise_race(args: Vec<Value>, any: bool) -> Result<Value, String> {
8908 let items = host::iter_all(&arg0(&args))?;
8909 let result = with_host(|h| h.new_promise());
8910 let rid = with_host(|h| h.promise_id(&result).unwrap());
8911 let n = items.len();
8912 let errors = std::rc::Rc::new(std::cell::RefCell::new(vec![Value::Undef; n]));
8913 let remaining = std::rc::Rc::new(std::cell::RefCell::new(n));
8914 for (i, it) in items.into_iter().enumerate() {
8915 let ap = host::promise_of(&it);
8916 let aid = with_host(|h| h.promise_id(&ap).unwrap());
8917 let errors = errors.clone();
8918 let remaining = remaining.clone();
8919 host::subscribe_native(
8920 aid,
8921 Box::new(move |state, val| {
8922 if any {
8923 if state == host::PromiseState::Fulfilled {
8924 host::resolve_promise_val(rid, val);
8925 } else {
8926 errors.borrow_mut()[i] = val;
8927 let mut r = remaining.borrow_mut();
8928 *r -= 1;
8929 if *r == 0 {
8930 let reasons = with_host(|h| h.new_array(errors.borrow().clone()));
8932 let msg = with_host(|h| h.new_str("All promises were rejected"));
8933 let agg = make_error("AggregateError", &[reasons, msg]);
8934 host::reject_promise_val(rid, agg);
8935 }
8936 }
8937 } else if state == host::PromiseState::Rejected {
8938 host::reject_promise_val(rid, val);
8939 } else {
8940 host::resolve_promise_val(rid, val);
8941 }
8942 Ok(())
8943 }),
8944 );
8945 }
8946 Ok(result)
8947}
8948
8949fn promise_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
8951 match name {
8952 "then" => Ok(host::promise_then(
8953 recv,
8954 args.first().cloned().unwrap_or(Value::Undef),
8955 args.get(1).cloned().unwrap_or(Value::Undef),
8956 )),
8957 "catch" => Ok(host::promise_then(
8958 recv,
8959 Value::Undef,
8960 args.first().cloned().unwrap_or(Value::Undef),
8961 )),
8962 "finally" => {
8963 let cb = arg0(&args);
8964 let i = match cb {
8965 Value::Obj(i) => i,
8966 _ => 0,
8967 };
8968 let pass = make_builtin(format!("@@finpass:{i}"));
8969 let throw = make_builtin(format!("@@finthrow:{i}"));
8970 Ok(host::promise_then(recv, pass, throw))
8971 }
8972 _ => Err(host::type_error(&format!(
8973 "promise.{name} is not a function"
8974 ))),
8975 }
8976}
8977
8978fn enqueue_microtask(next_tick: bool, cb: Value, args: Vec<Value>) {
8979 with_host(|h| {
8980 if next_tick {
8981 h.queue_nexttick(cb, args);
8982 } else {
8983 h.queue_micro(cb, args);
8984 }
8985 });
8986}
8987
8988fn schedule_timer(name: &str, args: Vec<Value>) -> Value {
8996 let cb = arg0(&args);
8997 let delay = if name == "setImmediate" {
8998 -1.0 } else {
9000 args.get(1)
9001 .map(|d| with_host(|h| h.to_number(d)))
9002 .unwrap_or(0.0)
9003 .max(0.0)
9004 };
9005 let extra = if name == "setImmediate" {
9006 args.get(1..).map(|s| s.to_vec()).unwrap_or_default()
9007 } else {
9008 args.get(2..).map(|s| s.to_vec()).unwrap_or_default()
9009 };
9010 let interval = (name == "setInterval").then(|| delay.max(1.0));
9013 let id = with_host(|h| h.add_timer(delay, cb, extra, interval));
9014 let tag = if name == "setImmediate" {
9015 "Immediate"
9016 } else {
9017 "Timeout"
9018 };
9019 crate::stdlib::timers::new_handle(id, tag)
9020}
9021
9022fn clear_timer(v: &Value) {
9025 let id =
9026 crate::stdlib::timers::handle_id(v).unwrap_or_else(|| with_host(|h| h.to_number(v)) as u64);
9027 with_host(|h| h.cancel_timer(id));
9028}