1use crate::host::{self, ops, with_host, FuncVal, JsObj};
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::DECLARE, b_declare);
15 vm.register_builtin(ops::DELNAME, b_delname);
16 vm.register_builtin(ops::GETATTR, b_getattr);
17 vm.register_builtin(ops::SETATTR, b_setattr);
18 vm.register_builtin(ops::GETITEM, b_getitem);
19 vm.register_builtin(ops::SETITEM, b_setitem);
20 vm.register_builtin(ops::DELITEM, b_delitem);
21 vm.register_builtin(ops::MKSTR, b_mkstr);
22 vm.register_builtin(ops::MKARR, b_mkarr);
23 vm.register_builtin(ops::MKOBJ, b_mkobj);
24 vm.register_builtin(ops::CALL, b_call);
25 vm.register_builtin(ops::CALL_METHOD, b_call_method);
26 vm.register_builtin(ops::CALL_VALUE, b_call_value);
27 vm.register_builtin(ops::NEW, b_new);
28 vm.register_builtin(ops::TRUTHY, b_truthy);
29 vm.register_builtin(ops::TOSTR, b_tostr);
30 vm.register_builtin(ops::MKFUNC, b_mkfunc);
31 vm.register_builtin(ops::GETITER, b_getiter);
32 vm.register_builtin(ops::FORITER, b_foriter);
33 vm.register_builtin(ops::FORIN_KEYS, b_forin_keys);
34 vm.register_builtin(ops::CONTAINS, b_contains);
35 vm.register_builtin(ops::SIG_RETURN, b_sig_return);
36 vm.register_builtin(ops::BINOP, b_binop);
37 vm.register_builtin(ops::UNARY, b_unary);
38 vm.register_builtin(ops::STRICT_EQ, b_strict_eq);
39 vm.register_builtin(ops::LOOSE_EQ, b_loose_eq);
40 vm.register_builtin(ops::TYPEOF, b_typeof);
41 vm.register_builtin(ops::LOAD_NULL, b_load_null);
42 vm.register_builtin(ops::THROW, b_throw);
43 vm.register_builtin(ops::TRY, b_try);
44 vm.register_builtin(ops::NULLISH, b_nullish);
45 vm.register_builtin(ops::UNPACK, b_unpack);
46 vm.register_builtin(ops::BUILD_ARGS, b_build_args);
47 vm.register_builtin(ops::THIS, b_this);
48 vm.register_builtin(ops::INSTANCEOF, b_instanceof);
49 vm.register_builtin(ops::DELPROP_NAME, b_delprop_name);
50 vm.register_builtin(ops::APPLY, b_apply);
51 vm.register_builtin(ops::APPLY_METHOD, b_apply_method);
52 vm.register_builtin(ops::OBJ_REST, b_obj_rest);
53 vm.register_builtin(ops::DIV, b_div);
54 vm.register_builtin(ops::MKCLASS, b_mkclass);
55 vm.register_builtin(ops::DEF_MEMBER, b_def_member);
56 vm.register_builtin(ops::DEF_FIELD, b_def_field);
57 vm.register_builtin(ops::SUPER_CALL, b_super_call);
58 vm.register_builtin(ops::SUPER_GET, b_super_get);
59 vm.register_builtin(ops::YIELD, b_yield);
60 vm.register_builtin(ops::PROPKEY, b_propkey);
61 vm.register_builtin(ops::NEW_TARGET, b_new_target);
62 vm.register_builtin(ops::AWAIT, b_await);
63 vm.register_builtin(ops::DEF_ACCESSOR, b_def_accessor);
64 vm.register_builtin(ops::DBG_LINE, b_dbg_line);
65 vm.register_builtin(ops::MKBIGINT, b_mkbigint);
66 vm.register_builtin(ops::MKREGEX, b_mkregex);
67 vm.register_builtin(ops::TAG_TMPL, b_tag_tmpl);
68 vm.register_builtin(ops::GET_ASYNC_ITER, b_get_async_iter);
69 vm.register_builtin(ops::ASYNC_STEP, b_async_step);
70 vm.register_builtin(ops::NUM_STEP, b_num_step);
71 vm.register_builtin(ops::ITER_CLOSE, b_iter_close);
72 vm.register_builtin(ops::TYPEOF_NAME, b_typeof_name);
73}
74
75fn b_iter_close(vm: &mut VM, _: u8) -> Value {
79 let it = vm.pop();
80 if with_host(|h| h.is_generator_val(&it)) {
81 if let Err(e) = host::gen_return(&it, Value::Undef) {
84 return abort(vm, e);
85 }
86 return Value::Undef;
87 }
88 if matches!(with_host(|h| h.get(&it).cloned()), Some(JsObj::Object(_))) {
90 if let Some(f) = with_host(|h| host::lookup_chain(h, &it, "return")) {
91 if with_host(|h| host::is_callable(h, &f)) {
92 if let Err(e) = host::invoke(&f, Vec::new(), Some(it.clone())) {
93 return abort(vm, e);
94 }
95 }
96 }
97 }
98 Value::Undef
99}
100
101fn b_num_step(vm: &mut VM, _: u8) -> Value {
106 let old = vm.pop();
107 let tag = match vm.pop() {
108 Value::Int(n) => n,
109 Value::Float(f) => f as i64,
110 _ => 1,
111 };
112 if with_host(|h| h.is_bigint_val(&old)) {
113 let b = with_host(|h| h.as_bigint(&old)).unwrap();
114 let old_n = with_host(|h| h.new_bigint(b.clone()));
115 let new = with_host(|h| h.new_bigint(b + num_bigint::BigInt::from(tag)));
116 vm.push(old_n);
117 new
118 } else {
119 let n = with_host(|h| h.to_number(&old));
120 vm.push(Value::Float(n));
121 Value::Float(n + tag as f64)
122 }
123}
124
125fn b_async_step(vm: &mut VM, _: u8) -> Value {
128 let iter = vm.pop();
129 let r = host::async_step(&iter);
130 finish(vm, r)
131}
132
133fn b_mkbigint(vm: &mut VM, _: u8) -> Value {
136 let digits = sval(&vm.pop());
137 match digits.parse::<num_bigint::BigInt>() {
138 Ok(b) => with_host(|h| h.new_bigint(b)),
139 Err(_) => abort(vm, host::type_error("invalid BigInt literal")),
140 }
141}
142
143fn b_tag_tmpl(vm: &mut VM, argc: u8) -> Value {
148 let mut all = pop_n(vm, argc as usize);
149 let int_of = |v: &Value| match v {
150 Value::Int(n) => *n as usize,
151 Value::Float(f) => *f as usize,
152 _ => 0,
153 };
154 let tag = all.remove(0);
155 let n = int_of(&all.remove(0));
156 let mcount = int_of(&all.remove(0));
157 let cooked: Vec<Value> = all.drain(0..n.min(all.len())).collect();
158 let raw: Vec<Value> = all.drain(0..n.min(all.len())).collect();
159 let values: Vec<Value> = all.drain(0..mcount.min(all.len())).collect();
160 let strings = with_host(|h| h.new_array(cooked));
163 let raw_arr = with_host(|h| h.new_array(raw));
164 with_host(|h| h.set_fn_prop(&strings, "raw", raw_arr));
165 let mut call_args = vec![strings];
166 call_args.extend(values);
167 let r = host::invoke(&tag, call_args, None);
168 finish(vm, r)
169}
170
171fn b_get_async_iter(vm: &mut VM, _: u8) -> Value {
175 let src = vm.pop();
176 let r = host::get_async_iterator(&src);
177 finish(vm, r)
178}
179
180fn b_mkregex(vm: &mut VM, _: u8) -> Value {
184 let flags = sval(&vm.pop());
185 let pattern = sval(&vm.pop());
186 match crate::regexp::build_regexp(&pattern, &flags) {
187 Ok(v) => v,
188 Err(e) => abort(vm, e),
189 }
190}
191
192fn b_dbg_line(vm: &mut VM, _: u8) -> Value {
198 let line = match vm.pop() {
199 Value::Int(n) => n as u32,
200 _ => 0,
201 };
202 crate::dap::on_debug_line(line);
203 Value::Undef
204}
205
206fn b_def_accessor(vm: &mut VM, _: u8) -> Value {
209 let func = vm.pop();
210 let kind = match vm.pop() {
211 Value::Int(n) => n,
212 _ => 0,
213 };
214 let name = sval(&vm.pop());
215 let obj = vm.pop();
216 with_host(|h| {
217 if kind == host::member::SET {
218 h.set_accessor(&obj, &name, None, Some(func));
219 } else {
220 h.set_accessor(&obj, &name, Some(func), None);
221 }
222 });
223 obj
224}
225
226fn b_await(vm: &mut VM, _: u8) -> Value {
227 let v = vm.pop();
228 match host::await_value(v) {
229 Ok(r) => r,
230 Err(e) => abort(vm, e),
231 }
232}
233
234fn b_mkclass(vm: &mut VM, _: u8) -> Value {
237 let ctor = vm.pop();
238 let parent = vm.pop();
239 let name = sval(&vm.pop());
240 host::build_class(&name, parent, ctor)
241}
242
243fn b_def_member(vm: &mut VM, _: u8) -> Value {
244 let func = vm.pop();
245 let is_static = matches!(vm.pop(), Value::Bool(true));
246 let kind = match vm.pop() {
247 Value::Int(n) => n,
248 _ => 0,
249 };
250 let name = sval(&vm.pop());
251 let class_val = vm.pop();
252 host::define_member(&class_val, &name, kind, is_static, func);
253 class_val
254}
255
256fn b_def_field(vm: &mut VM, _: u8) -> Value {
257 let thunk = vm.pop();
258 let name = sval(&vm.pop());
259 let class_val = vm.pop();
260 host::define_field(&class_val, &name, thunk);
261 class_val
262}
263
264fn b_super_call(vm: &mut VM, argc: u8) -> Value {
267 let args = pop_n(vm, argc as usize);
268 let this = with_host(|h| h.current_this());
269 let this = match this {
270 Some(t) => t,
271 None => return abort(vm, host::type_error("'super' keyword unexpected here")),
272 };
273 let (parent, fields) = with_host(|h| h.super_context());
275 let (parent, fields) = match parent {
276 Some(p) => (p, fields),
277 None => return abort(vm, host::type_error("'super' keyword unexpected here")),
278 };
279 let nt = with_host(|h| h.current_new_target()).unwrap_or_else(|| this.clone());
280 let r = host::super_construct(&parent, args, &this, &nt);
281 if let Err(e) = r {
282 return abort(vm, e);
283 }
284 for (name, thunk) in fields {
286 match host::invoke(&thunk, Vec::new(), Some(this.clone())) {
287 Ok(val) => with_host(|h| {
288 if let Some(JsObj::Object(props)) = h.get_mut(&this) {
289 props.insert(name, val);
290 }
291 }),
292 Err(e) => return abort(vm, e),
293 }
294 }
295 Value::Undef
296}
297
298fn b_super_get(vm: &mut VM, _: u8) -> Value {
300 let name = sval(&vm.pop());
301 match with_host(|h| h.super_resolve(&name)) {
302 host::SuperRef::Data(v) => v,
303 host::SuperRef::Getter(getter) => {
304 let this = with_host(|h| h.current_this());
305 match host::invoke(&getter, Vec::new(), this) {
306 Ok(v) => v,
307 Err(e) => abort(vm, e),
308 }
309 }
310 }
311}
312
313fn b_yield(vm: &mut VM, _: u8) -> Value {
314 let v = vm.pop();
315 match host::gen_yield(v) {
316 Ok(sent) => {
317 if with_host(|h| h.error.is_some() || h.signal.is_some()) {
321 vm.ip = vm.chunk.ops.len();
322 }
323 sent
324 }
325 Err(e) => abort(vm, e),
326 }
327}
328
329fn b_propkey(vm: &mut VM, _: u8) -> Value {
330 let v = vm.pop();
331 let k = with_host(|h| h.property_key(&v));
332 with_host(|h| h.new_str(k))
333}
334
335fn b_new_target(_vm: &mut VM, _: u8) -> Value {
336 with_host(|h| h.current_new_target().unwrap_or(Value::Undef))
337}
338
339fn b_div(vm: &mut VM, _: u8) -> Value {
345 let b = vm.pop();
346 let a = vm.pop();
347 let r = with_host(|h| h.arith(NumOp::Div, &a, &b));
348 finish(vm, r)
349}
350
351fn b_obj_rest(vm: &mut VM, _: u8) -> Value {
353 let excluded = vm.pop();
354 let obj = vm.pop();
355 let excl: Vec<String> = with_host(|h| h.iter_vec(&excluded))
356 .unwrap_or_default()
357 .iter()
358 .map(|v| with_host(|h| h.str_of(v)))
359 .collect();
360 with_host(|h| {
361 let props: IndexMap<String, Value> = match h.get(&obj) {
362 Some(JsObj::Object(m)) => m
363 .iter()
364 .filter(|(k, _)| !excl.contains(k))
365 .map(|(k, v)| (k.clone(), v.clone()))
366 .collect(),
367 _ => IndexMap::new(),
368 };
369 h.new_object(props)
370 })
371}
372
373fn pop_n(vm: &mut VM, n: usize) -> Vec<Value> {
376 let mut v = Vec::with_capacity(n);
377 for _ in 0..n {
378 v.push(vm.pop());
379 }
380 v.reverse();
381 v
382}
383
384fn sval(v: &Value) -> String {
386 if let Value::Str(s) = v {
387 return (**s).clone();
388 }
389 with_host(|h| h.as_str(v)).unwrap_or_default()
390}
391
392fn abort(vm: &mut VM, e: String) -> Value {
393 with_host(|h| h.error = Some(e));
394 vm.ip = vm.chunk.ops.len();
395 Value::Undef
396}
397
398fn finish(vm: &mut VM, r: Result<Value, String>) -> Value {
400 match r {
401 Ok(v) => {
402 if with_host(|h| h.error.is_some() || h.signal.is_some()) {
403 vm.ip = vm.chunk.ops.len();
404 }
405 v
406 }
407 Err(e) => abort(vm, e),
408 }
409}
410
411fn b_getlocal(vm: &mut VM, _: u8) -> Value {
414 let name = sval(&vm.pop());
415 if let Some(v) = with_host(|h| h.read_name(&name)) {
416 return v;
417 }
418 match name.as_str() {
420 "undefined" => return Value::Undef,
421 "NaN" => return Value::Float(f64::NAN),
422 "Infinity" => return Value::Float(f64::INFINITY),
423 "globalThis" => return with_host(|h| h.new_object(IndexMap::new())),
424 _ => {}
425 }
426 if is_namespace(&name) || is_known_builtin(&name) {
427 return with_host(|h| h.alloc(JsObj::Builtin(name.clone())));
428 }
429 abort(vm, host::ref_error(&name))
430}
431
432fn b_setlocal(vm: &mut VM, _: u8) -> Value {
433 let val = vm.pop();
434 let name = sval(&vm.pop());
435 with_host(|h| h.set_name(&name, val.clone()));
436 val
437}
438
439fn b_declare(vm: &mut VM, _: u8) -> Value {
440 let val = vm.pop();
441 let name = sval(&vm.pop());
442 with_host(|h| h.declare_name(&name, val.clone()));
443 val
444}
445
446fn b_delname(vm: &mut VM, _: u8) -> Value {
447 let name = sval(&vm.pop());
448 with_host(|h| h.del_name(&name));
449 Value::Bool(true)
450}
451
452fn b_this(_vm: &mut VM, _: u8) -> Value {
453 with_host(|h| h.current_this().unwrap_or(Value::Undef))
454}
455
456fn b_load_null(_vm: &mut VM, _: u8) -> Value {
457 with_host(|h| h.null())
458}
459
460fn b_getattr(vm: &mut VM, _: u8) -> Value {
463 let name = sval(&vm.pop());
464 let recv = vm.pop();
465 match get_property(&recv, &name) {
466 Ok(v) => v,
467 Err(e) => abort(vm, e),
468 }
469}
470
471pub fn get_property(recv: &Value, name: &str) -> Result<Value, String> {
474 if with_host(|h| h.is_nullish(recv)) {
475 return Err(host::type_error(&format!(
476 "Cannot read properties of {} (reading '{name}')",
477 with_host(|h| h.str_of(recv))
478 )));
479 }
480 if let Some((getter, _)) = with_host(|h| host::lookup_accessor(h, recv, name)) {
482 return match getter {
483 Some(g) => host::invoke(&g, Vec::new(), Some(recv.clone())),
484 None => Ok(Value::Undef), };
486 }
487 if name == "constructor" {
492 if let Some(v) = with_host(|h| {
493 match h.get(recv) {
494 Some(JsObj::Object(p)) => p.get("constructor").cloned(),
495 _ => None,
496 }
497 .or_else(|| host::lookup_chain(h, recv, "constructor"))
498 }) {
499 return Ok(v);
500 }
501 if let Some(cn) = with_host(|h| default_ctor_name(h, recv)) {
502 return Ok(with_host(|h| h.alloc(JsObj::Builtin(cn.to_string()))));
503 }
504 }
505 let obj = with_host(|h| h.get(recv).cloned());
506 Ok(match obj {
507 Some(JsObj::Object(props)) => {
508 if !name.is_empty()
511 && name.bytes().all(|b| b.is_ascii_digit())
512 && matches!(props.get("@@native"), Some(v) if with_host(|h| h.str_of(v)) == "TypedArray")
513 {
514 if let Some(v) = crate::stdlib::typedarray::elem_get(recv, name) {
515 return Ok(v);
516 }
517 }
518 if let Some(v) = props.get(name) {
519 v.clone()
520 } else if let Some(v) = with_host(|h| host::lookup_chain(h, recv, name)) {
521 v
523 } else if name == "__proto__" {
524 with_host(|h| h.proto_of(recv)).unwrap_or_else(|| with_host(|h| h.null()))
525 } else if crate::stdlib::native_tag(recv)
526 .map(|tag| crate::stdlib::instance_has_method(&tag, name))
527 .unwrap_or(false)
528 {
529 bound_method(recv, name)
532 } else if is_object_method(name) {
533 bound_method(recv, name)
534 } else {
535 Value::Undef
536 }
537 }
538 Some(JsObj::Class(_)) | Some(JsObj::Func(_)) | Some(JsObj::BoundFunc { .. }) => {
539 function_property(recv, name)
540 }
541 Some(JsObj::Symbol { desc, .. }) => match name {
542 "description" => match desc {
543 Some(d) => with_host(|h| h.new_str(d)),
544 None => Value::Undef,
545 },
546 "toString" => bound_method(recv, name),
547 _ => Value::Undef,
548 },
549 Some(JsObj::BigInt(_)) => {
550 if matches!(
551 name,
552 "toString" | "valueOf" | "toLocaleString" | "constructor"
553 ) {
554 bound_method(recv, name)
555 } else {
556 Value::Undef
557 }
558 }
559 Some(JsObj::RegExp(r)) => crate::regexp::regexp_property(&r, name).unwrap_or_else(|| {
560 if crate::regexp::is_regexp_method(name) {
561 bound_method(recv, name)
562 } else {
563 Value::Undef
564 }
565 }),
566 Some(JsObj::Map { entries, .. }) => match name {
567 "size" => Value::Float(entries.len() as f64),
568 "@@iterator" => bound_method(recv, name),
569 _ if is_map_method(name) => bound_method(recv, name),
570 _ => Value::Undef,
571 },
572 Some(JsObj::Set { entries, .. }) => match name {
573 "size" => Value::Float(entries.len() as f64),
574 "@@iterator" => bound_method(recv, name),
575 _ if is_set_method(name) => bound_method(recv, name),
576 _ => Value::Undef,
577 },
578 Some(JsObj::Generator { .. }) => {
579 if is_generator_method(name) {
580 bound_method(recv, name)
581 } else {
582 Value::Undef
583 }
584 }
585 Some(JsObj::Promise { .. }) => {
586 if matches!(name, "then" | "catch" | "finally") {
587 bound_method(recv, name)
588 } else {
589 Value::Undef
590 }
591 }
592 Some(JsObj::Iter { .. }) => {
593 if matches!(name, "next" | "return" | "@@iterator") {
594 bound_method(recv, name)
595 } else {
596 Value::Undef
597 }
598 }
599 Some(JsObj::Array(items)) => {
600 if name == "length" {
601 Value::Float(items.len() as f64)
602 } else if let Ok(i) = name.parse::<usize>() {
603 items.get(i).cloned().unwrap_or(Value::Undef)
604 } else if name == "@@iterator" || is_array_method(name) || is_object_method(name) {
605 bound_method(recv, name)
606 } else if let Some(v) = with_host(|h| h.fn_prop(recv, name)) {
607 v
610 } else {
611 Value::Undef
612 }
613 }
614 Some(JsObj::Str(s)) => {
615 if name == "length" {
616 Value::Float(s.chars().count() as f64)
617 } else if let Ok(i) = name.parse::<usize>() {
618 match s.chars().nth(i) {
619 Some(c) => with_host(|h| h.new_str(c.to_string())),
620 None => Value::Undef,
621 }
622 } else if name == "@@iterator" || is_string_method(name) {
623 bound_method(recv, name)
624 } else {
625 Value::Undef
626 }
627 }
628 Some(JsObj::Builtin(ns)) => namespace_property(&ns, name),
629 _ => {
630 if matches!(recv, Value::Float(_) | Value::Int(_)) && is_number_method(name) {
632 bound_method(recv, name)
633 } else {
634 Value::Undef
635 }
636 }
637 })
638}
639
640fn default_ctor_name(h: &host::JsHost, recv: &Value) -> Option<&'static str> {
646 match h.get(recv) {
647 Some(JsObj::Array(_)) => Some("Array"),
648 Some(JsObj::Object(props)) => {
649 match props.get("@@native").map(|t| h.str_of(t)).as_deref() {
655 Some("Buffer") => Some("Buffer"),
656 Some("URL") => Some("URL"),
657 Some("Date") => Some("Date"),
658 Some("WeakRef") => Some("WeakRef"),
659 Some("FinalizationRegistry") => Some("FinalizationRegistry"),
660 Some("TextEncoder") => Some("TextEncoder"),
661 Some("TextDecoder") => Some("TextDecoder"),
662 Some("EventEmitter") => Some("EventEmitter"),
663 _ => Some("Object"),
664 }
665 }
666 Some(JsObj::Map { weak, .. }) => Some(if *weak { "WeakMap" } else { "Map" }),
667 Some(JsObj::Set { weak, .. }) => Some(if *weak { "WeakSet" } else { "Set" }),
668 Some(JsObj::Promise { .. }) => Some("Promise"),
669 Some(JsObj::Str(_)) => Some("String"),
670 Some(JsObj::Symbol { .. }) => Some("Symbol"),
671 Some(JsObj::BigInt(_)) => Some("BigInt"),
672 Some(JsObj::RegExp(_)) => Some("RegExp"),
673 Some(JsObj::Iter { .. }) => Some("Iterator"),
674 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) | Some(JsObj::BoundFunc { .. }) => {
675 Some("Function")
676 }
677 _ => match recv {
678 Value::Float(_) | Value::Int(_) => Some("Number"),
679 Value::Bool(_) => Some("Boolean"),
680 _ => None,
681 },
682 }
683}
684
685fn is_builtin_ctor(name: &str) -> bool {
690 matches!(
691 name,
692 "Array"
693 | "Object"
694 | "Number"
695 | "String"
696 | "Boolean"
697 | "Symbol"
698 | "Function"
699 | "Map"
700 | "Set"
701 | "WeakMap"
702 | "WeakSet"
703 | "Promise"
704 | "BigInt"
705 | "Iterator"
706 | "RegExp"
707 | "Date"
708 | "ArrayBuffer"
709 | "Uint8Array"
710 | "Int8Array"
711 | "Uint8ClampedArray"
712 | "Int16Array"
713 | "Uint16Array"
714 | "Int32Array"
715 | "Uint32Array"
716 | "Float32Array"
717 | "Float64Array"
718 | "WeakRef"
719 | "FinalizationRegistry"
720 | "TextEncoder"
721 | "TextDecoder"
722 | "IncomingMessage"
723 | "ServerResponse"
724 | "EventEmitter"
725 | "Buffer"
726 | "URL"
727 | "URLSearchParams"
728 ) || host::ERROR_NAMES.contains(&name)
729}
730
731fn bound_method(recv: &Value, name: &str) -> Value {
732 with_host(|h| {
733 h.alloc(JsObj::BoundMethod {
734 recv: recv.clone(),
735 name: name.to_string(),
736 })
737 })
738}
739
740fn is_object_method(name: &str) -> bool {
742 matches!(
743 name,
744 "hasOwnProperty"
745 | "isPrototypeOf"
746 | "propertyIsEnumerable"
747 | "toString"
748 | "valueOf"
749 | "constructor"
750 )
751}
752
753pub fn is_object_builtin_method(name: &str) -> bool {
754 matches!(
755 name,
756 "hasOwnProperty" | "isPrototypeOf" | "propertyIsEnumerable" | "toString" | "valueOf"
757 )
758}
759
760pub fn object_builtin_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
762 match name {
763 "hasOwnProperty" => {
764 let k = with_host(|h| h.property_key(&arg0(&args)));
765 if matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Builtin(_))) {
768 return Ok(Value::Bool(has_property(recv, &k)));
769 }
770 let has = with_host(|h| match h.get(recv) {
771 Some(JsObj::Object(p)) => p.contains_key(&k),
772 Some(JsObj::Array(items)) => {
773 k == "length" || k.parse::<usize>().map(|i| i < items.len()).unwrap_or(false)
774 }
775 _ => false,
776 });
777 Ok(Value::Bool(has))
778 }
779 "isPrototypeOf" => {
780 let target = arg0(&args);
781 let mut cur = with_host(|h| h.proto_of(&target));
782 while let Some(p) = cur {
783 if with_host(|h| h.strict_eq(&p, recv)) {
784 return Ok(Value::Bool(true));
785 }
786 cur = with_host(|h| h.proto_of(&p));
787 }
788 Ok(Value::Bool(false))
789 }
790 "propertyIsEnumerable" => {
791 let k = with_host(|h| h.str_of(&arg0(&args)));
792 let has =
793 with_host(|h| matches!(h.get(recv), Some(JsObj::Object(p)) if p.contains_key(&k)));
794 Ok(Value::Bool(has))
795 }
796 "toString" => Ok(with_host(|h| {
797 let s = h.str_of(recv);
800 h.new_str(s)
801 })),
802 "valueOf" => Ok(recv.clone()),
803 _ => Err(host::type_error(&format!("{name} is not a function"))),
804 }
805}
806
807pub fn function_builtin_method(
811 recv: &Value,
812 name: &str,
813 args: &[Value],
814) -> Result<Option<Value>, String> {
815 match name {
816 "call" => {
817 let this = args.first().cloned();
818 let rest = args.get(1..).map(|s| s.to_vec()).unwrap_or_default();
819 Ok(Some(host::invoke(recv, rest, this)?))
820 }
821 "apply" => {
822 let this = args.first().cloned();
823 let arr = args.get(1).cloned().unwrap_or(Value::Undef);
824 let call_args = if matches!(arr, Value::Undef) || with_host(|h| h.is_null(&arr)) {
825 Vec::new()
826 } else {
827 with_host(|h| h.iter_vec(&arr)).unwrap_or_default()
828 };
829 Ok(Some(host::invoke(recv, call_args, this)?))
830 }
831 "bind" => {
832 let this = args.first().cloned().unwrap_or(Value::Undef);
833 let pre = args.get(1..).map(|s| s.to_vec()).unwrap_or_default();
834 Ok(Some(with_host(|h| {
835 h.alloc(JsObj::BoundFunc {
836 target: recv.clone(),
837 this,
838 args: pre,
839 })
840 })))
841 }
842 "toString" => Ok(Some(with_host(|h| {
843 let s = h.str_of(recv);
844 h.new_str(s)
845 }))),
846 _ => Ok(None),
847 }
848}
849
850fn is_function_method(name: &str) -> bool {
851 matches!(name, "call" | "apply" | "bind" | "toString")
852}
853fn is_map_method(name: &str) -> bool {
854 matches!(
855 name,
856 "get" | "set" | "has" | "delete" | "clear" | "forEach" | "keys" | "values" | "entries"
857 )
858}
859fn is_set_method(name: &str) -> bool {
860 matches!(
861 name,
862 "add" | "has" | "delete" | "clear" | "forEach" | "keys" | "values" | "entries"
863 )
864}
865fn is_generator_method(name: &str) -> bool {
866 matches!(name, "next" | "return" | "throw")
867}
868
869fn function_property(recv: &Value, name: &str) -> Value {
872 if matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Class(_))) {
874 if let Some(v) = with_host(|h| h.class_static(recv, name)) {
875 return v;
876 }
877 } else if let Some(v) = with_host(|h| h.fn_prop(recv, name)) {
878 return v;
879 }
880 if let Some(v) = with_host(|h| host::lookup_chain(h, recv, name)) {
884 return v;
885 }
886 match name {
887 "name" => with_host(|h| {
888 let n = h.callable_name(recv);
889 h.new_str(n)
890 }),
891 "length" => Value::Float(with_host(|h| h.func_arity(recv)) as f64),
892 "prototype" => ensure_fn_prototype(recv),
893 _ if is_function_method(name) => bound_method(recv, name),
894 _ => Value::Undef,
895 }
896}
897
898fn ensure_fn_prototype(recv: &Value) -> Value {
902 if let Some(p) = with_host(|h| h.fn_prop(recv, "prototype")) {
903 return p;
904 }
905 let is_arrow =
907 matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Func(f)) if f.is_arrow);
908 if is_arrow {
909 return Value::Undef;
910 }
911 if !matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Func(_))) {
912 return Value::Undef;
913 }
914 with_host(|h| {
915 let proto = h.new_object(IndexMap::new());
916 if let Some(JsObj::Object(p)) = h.get_mut(&proto) {
917 p.insert("constructor".to_string(), recv.clone());
918 }
919 h.set_fn_prop(recv, "prototype", proto.clone());
920 proto
921 })
922}
923
924fn namespace_property(ns: &str, name: &str) -> Value {
927 let konst = match (ns, name) {
929 ("Math", "PI") => Some(std::f64::consts::PI),
930 ("Math", "E") => Some(std::f64::consts::E),
931 ("Math", "LN2") => Some(std::f64::consts::LN_2),
932 ("Math", "LN10") => Some(std::f64::consts::LN_10),
933 ("Math", "LOG2E") => Some(std::f64::consts::LOG2_E),
934 ("Math", "LOG10E") => Some(std::f64::consts::LOG10_E),
935 ("Math", "SQRT2") => Some(std::f64::consts::SQRT_2),
936 ("Math", "SQRT1_2") => Some(std::f64::consts::FRAC_1_SQRT_2),
937 ("Number", "MAX_SAFE_INTEGER") => Some(9007199254740991.0),
938 ("Number", "MIN_SAFE_INTEGER") => Some(-9007199254740991.0),
939 ("Number", "MAX_VALUE") => Some(f64::MAX),
940 ("Number", "MIN_VALUE") => Some(f64::MIN_POSITIVE),
941 ("Number", "EPSILON") => Some(f64::EPSILON),
942 ("Number", "POSITIVE_INFINITY") => Some(f64::INFINITY),
943 ("Number", "NEGATIVE_INFINITY") => Some(f64::NEG_INFINITY),
944 ("Number", "NaN") => Some(f64::NAN),
945 _ => None,
946 };
947 if let Some(k) = konst {
948 return Value::Float(k);
949 }
950 if name == "name" && is_builtin_ctor(ns) {
954 return with_host(|h| h.new_str(ns.to_string()));
955 }
956 if ns == "Symbol" && name == "iterator" {
958 return with_host(|h| h.well_known_iterator());
959 }
960 if ns == "Symbol" && name == "asyncIterator" {
961 return with_host(|h| h.well_known_async_iterator());
962 }
963 if let Some(v) = crate::stdlib::constant(ns, name) {
966 return v;
967 }
968 if name == "prototype" && is_builtin_ctor(ns) {
973 return with_host(|h| h.alloc(JsObj::Builtin(format!("{ns}.prototype"))));
974 }
975 if let Some(ctor) = ns.strip_suffix(".prototype") {
979 return with_host(|h| h.alloc(JsObj::Builtin(format!("@proto:{ctor}:{name}"))));
980 }
981 let qualified = format!("{ns}.{name}");
982 if is_known_builtin(&qualified) {
983 return with_host(|h| h.alloc(JsObj::Builtin(qualified)));
984 }
985 if let Some(v) = with_host(|h| h.builtin_static(ns, name)) {
987 return v;
988 }
989 Value::Undef
990}
991
992pub fn proto_method(recv: &Value, ctor_method: &str, args: Vec<Value>) -> Result<Value, String> {
998 let (ctor, method) = ctor_method.split_once(':').unwrap_or(("", ctor_method));
999 if ctor == "Object" && method == "toString" {
1000 return Ok(with_host(|h| h.new_str(object_tag(h, recv))));
1001 }
1002 if ctor == "EventEmitter" {
1006 return crate::stdlib::events::instance_call(recv, method, args);
1007 }
1008 host::call_method(recv, method, args)
1009}
1010
1011fn object_tag(h: &host::JsHost, v: &Value) -> String {
1013 let tag = match v {
1014 Value::Undef => "Undefined",
1015 Value::Bool(_) => "Boolean",
1016 Value::Int(_) | Value::Float(_) => "Number",
1017 Value::Str(_) => "String",
1018 Value::Obj(_) => match h.get(v) {
1019 Some(JsObj::Null) => "Null",
1020 Some(JsObj::Str(_)) => "String",
1021 Some(JsObj::Array(_)) => "Array",
1022 Some(JsObj::Func(_))
1023 | Some(JsObj::Class(_))
1024 | Some(JsObj::Builtin(_))
1025 | Some(JsObj::BoundFunc { .. })
1026 | Some(JsObj::BoundMethod { .. }) => "Function",
1027 Some(JsObj::RegExp(_)) => "RegExp",
1028 _ => "Object",
1029 },
1030 _ => "Object",
1033 };
1034 format!("[object {tag}]")
1035}
1036
1037fn b_setattr(vm: &mut VM, _: u8) -> Value {
1038 let val = vm.pop();
1039 let name = sval(&vm.pop());
1040 let recv = vm.pop();
1041 set_property(&recv, &name, val.clone());
1042 val
1043}
1044
1045fn set_property(recv: &Value, name: &str, val: Value) {
1046 if name == "__proto__" && matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Object(_)))
1048 {
1049 with_host(|h| h.set_proto(recv, val));
1050 return;
1051 }
1052 if let Some((_, Some(setter))) = with_host(|h| host::lookup_accessor(h, recv, name)) {
1054 let _ = host::invoke(&setter, vec![val], Some(recv.clone()));
1055 return;
1056 }
1057 if let Some((Some(_), None)) = with_host(|h| host::lookup_accessor(h, recv, name)) {
1059 return;
1060 }
1061 if matches!(
1063 with_host(|h| h.get(recv).cloned()),
1064 Some(JsObj::Func(_)) | Some(JsObj::Class(_))
1065 ) {
1066 with_host(|h| h.set_fn_prop(recv, name, val));
1067 return;
1068 }
1069 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(recv).cloned()) {
1073 with_host(|h| h.set_builtin_static(&ns, name, val));
1074 return;
1075 }
1076 if name == "lastIndex" {
1078 if let Some(n) = with_host(|h| match h.get(recv) {
1079 Some(JsObj::RegExp(_)) => Some(h.to_number(&val)),
1080 _ => None,
1081 }) {
1082 with_host(|h| {
1083 if let Some(JsObj::RegExp(r)) = h.get_mut(recv) {
1084 r.last_index = if n.is_finite() && n >= 0.0 {
1085 n as usize
1086 } else {
1087 0
1088 };
1089 }
1090 });
1091 return;
1092 }
1093 }
1094 if !name.is_empty() && name.bytes().all(|b| b.is_ascii_digit()) {
1096 let is_ta = matches!(
1097 with_host(|h| h.get(recv).cloned()),
1098 Some(JsObj::Object(ref p)) if p.get("@@native").map(|v| with_host(|h| h.str_of(v))).as_deref() == Some("TypedArray")
1099 );
1100 if is_ta && crate::stdlib::typedarray::elem_set(recv, name, &val) {
1101 return;
1102 }
1103 }
1104 if matches!(with_host(|h| h.get(recv).cloned()), Some(JsObj::Array(_)))
1106 && name != "length"
1107 && name.parse::<usize>().is_err()
1108 {
1109 with_host(|h| h.set_fn_prop(recv, name, val));
1110 return;
1111 }
1112 with_host(|h| match h.get_mut(recv) {
1113 Some(JsObj::Object(props)) => {
1114 let is_new = !props.contains_key(name);
1117 props.insert(name.to_string(), val);
1118 if is_new && host::array_index(name).is_some() {
1119 host::canonicalize_own_keys(props);
1120 }
1121 }
1122 Some(JsObj::Array(items)) => {
1123 if name == "length" {
1124 let n = h_val_to_len(&val);
1125 items.resize(n, Value::Undef);
1126 } else if let Ok(i) = name.parse::<usize>() {
1127 if i >= items.len() {
1128 items.resize(i + 1, Value::Undef);
1129 }
1130 items[i] = val;
1131 }
1132 }
1133 _ => {}
1134 });
1135}
1136
1137fn h_val_to_len(v: &Value) -> usize {
1138 match v {
1139 Value::Float(f) if f.is_finite() && *f >= 0.0 => *f as usize,
1140 Value::Int(n) if *n >= 0 => *n as usize,
1141 _ => 0,
1142 }
1143}
1144
1145fn b_getitem(vm: &mut VM, _: u8) -> Value {
1146 let idx = vm.pop();
1147 let recv = vm.pop();
1148 let key = with_host(|h| h.property_key(&idx));
1149 match get_property(&recv, &key) {
1150 Ok(v) => v,
1151 Err(e) => abort(vm, e),
1152 }
1153}
1154
1155fn b_setitem(vm: &mut VM, _: u8) -> Value {
1156 let val = vm.pop();
1157 let idx = vm.pop();
1158 let recv = vm.pop();
1159 let key = with_host(|h| h.property_key(&idx));
1160 set_property(&recv, &key, val.clone());
1161 val
1162}
1163
1164fn b_delitem(vm: &mut VM, _: u8) -> Value {
1165 let idx = vm.pop();
1166 let recv = vm.pop();
1167 let key = with_host(|h| h.str_of(&idx));
1168 with_host(|h| match h.get_mut(&recv) {
1169 Some(JsObj::Object(props)) => {
1170 props.shift_remove(&key);
1171 }
1172 Some(JsObj::Array(items)) => {
1173 if let Ok(i) = key.parse::<usize>() {
1174 if i < items.len() {
1175 items[i] = Value::Undef;
1176 }
1177 }
1178 }
1179 _ => {}
1180 });
1181 Value::Bool(true)
1182}
1183
1184fn b_delprop_name(vm: &mut VM, _: u8) -> Value {
1185 let name = sval(&vm.pop());
1186 let recv = vm.pop();
1187 with_host(|h| {
1188 if let Some(JsObj::Object(props)) = h.get_mut(&recv) {
1189 props.shift_remove(&name);
1190 }
1191 });
1192 Value::Bool(true)
1193}
1194
1195fn b_mkstr(vm: &mut VM, argc: u8) -> Value {
1198 let parts = pop_n(vm, argc as usize);
1199 let s: String = with_host(|h| parts.iter().map(|p| h.str_of(p)).collect());
1200 with_host(|h| h.new_str(s))
1201}
1202
1203fn b_mkarr(vm: &mut VM, argc: u8) -> Value {
1204 let items = pop_n(vm, argc as usize);
1205 with_host(|h| h.new_array(items))
1206}
1207
1208fn b_mkobj(vm: &mut VM, argc: u8) -> Value {
1209 let flat = pop_n(vm, argc as usize);
1210 let mut props: IndexMap<String, Value> = IndexMap::new();
1211 let mut proto_override: Option<Value> = None;
1213 let mut i = 0;
1214 while i + 2 < flat.len() || (i + 2 == flat.len() && flat.len() % 3 == 0 && i < flat.len()) {
1215 if i + 2 >= flat.len() {
1216 break;
1217 }
1218 let spread = matches!(flat[i], Value::Int(1));
1219 if spread {
1220 let src = flat[i + 1].clone();
1221 let entries = with_host(|h| match h.get(&src) {
1222 Some(JsObj::Object(m)) => m
1223 .iter()
1224 .map(|(k, v)| (k.clone(), v.clone()))
1225 .collect::<Vec<_>>(),
1226 Some(JsObj::Array(items)) => items
1227 .iter()
1228 .enumerate()
1229 .map(|(idx, v)| (idx.to_string(), v.clone()))
1230 .collect::<Vec<_>>(),
1231 _ => Vec::new(),
1232 });
1233 for (k, v) in entries {
1234 props.insert(k, v);
1235 }
1236 } else {
1237 let key = with_host(|h| h.str_of(&flat[i + 1]));
1238 if key == "__proto__" {
1239 proto_override = Some(flat[i + 2].clone());
1240 } else {
1241 props.insert(key, flat[i + 2].clone());
1242 }
1243 }
1244 i += 3;
1245 }
1246 with_host(|h| {
1247 let o = h.new_object(props);
1248 if let Some(p) = proto_override {
1249 if matches!(p, Value::Obj(_)) {
1250 h.set_proto(&o, p);
1251 }
1252 }
1253 o
1254 })
1255}
1256
1257fn b_mkfunc(vm: &mut VM, _: u8) -> Value {
1258 let def_id = match vm.pop() {
1259 Value::Int(n) => n as usize,
1260 Value::Float(f) => f as usize,
1261 _ => return abort(vm, "internal: MKFUNC id".into()),
1262 };
1263 let is_arrow = with_host(|h| h.funcs.get(def_id).map(|d| d.is_arrow).unwrap_or(false));
1264 with_host(|h| {
1265 let env = h.current_env_capture();
1266 let this = h.current_this();
1267 h.alloc(JsObj::Func(FuncVal {
1268 def_id,
1269 env: Some(env),
1270 this,
1271 is_arrow,
1272 home_class: None,
1273 }))
1274 })
1275}
1276
1277fn b_truthy(vm: &mut VM, _: u8) -> Value {
1280 let v = vm.pop();
1281 Value::Bool(with_host(|h| h.truthy(&v)))
1282}
1283
1284fn b_nullish(vm: &mut VM, _: u8) -> Value {
1285 let v = vm.pop();
1286 Value::Bool(with_host(|h| h.is_nullish(&v)))
1287}
1288
1289fn b_tostr(vm: &mut VM, _: u8) -> Value {
1290 let v = vm.pop();
1291 match host::to_string_value(&v) {
1294 Ok(s) => s,
1295 Err(e) => abort(vm, e),
1296 }
1297}
1298
1299fn b_typeof(vm: &mut VM, _: u8) -> Value {
1300 let v = vm.pop();
1301 with_host(|h| {
1302 let t = h.type_of(&v);
1303 h.new_str(t)
1304 })
1305}
1306
1307fn b_typeof_name(vm: &mut VM, _: u8) -> Value {
1310 let name = sval(&vm.pop());
1311 if let Some(v) = with_host(|h| h.read_name(&name)) {
1313 return with_host(|h| {
1314 let t = h.type_of(&v);
1315 h.new_str(t)
1316 });
1317 }
1318 let t = match name.as_str() {
1322 "undefined" => "undefined".to_string(),
1323 "NaN" | "Infinity" => "number".to_string(),
1324 "globalThis" => "object".to_string(),
1325 n if is_namespace(n) || is_known_builtin(n) => {
1326 let v = with_host(|h| h.alloc(JsObj::Builtin(name.clone())));
1327 with_host(|h| h.type_of(&v)).to_string()
1328 }
1329 _ => "undefined".to_string(), };
1331 with_host(|h| h.new_str(t))
1332}
1333
1334fn b_strict_eq(vm: &mut VM, _: u8) -> Value {
1335 let b = vm.pop();
1336 let a = vm.pop();
1337 Value::Bool(with_host(|h| h.strict_eq(&a, &b)))
1338}
1339
1340fn b_loose_eq(vm: &mut VM, _: u8) -> Value {
1341 let b = vm.pop();
1342 let a = vm.pop();
1343 Value::Bool(with_host(|h| h.loose_eq(&a, &b)))
1344}
1345
1346fn b_instanceof(vm: &mut VM, _: u8) -> Value {
1347 let ctor = vm.pop();
1348 let obj = vm.pop();
1349 match host::instance_of(&obj, &ctor) {
1350 Ok(b) => Value::Bool(b),
1351 Err(e) => abort(vm, e),
1352 }
1353}
1354
1355fn b_binop(vm: &mut VM, _: u8) -> Value {
1358 let b = vm.pop();
1359 let a = vm.pop();
1360 let tag = match vm.pop() {
1361 Value::Int(n) => n,
1362 _ => 0,
1363 };
1364 let r = with_host(|h| h.bitwise(tag, &a, &b));
1365 finish(vm, r)
1366}
1367
1368fn b_unary(vm: &mut VM, _: u8) -> Value {
1369 let v = vm.pop();
1370 let tag = match vm.pop() {
1371 Value::Int(n) => n,
1372 _ => 0,
1373 };
1374 if with_host(|h| h.is_bigint_val(&v)) {
1377 return match tag {
1378 host::unop::POS => abort(
1379 vm,
1380 host::type_error("Cannot convert a BigInt value to a number"),
1381 ),
1382 host::unop::BITNOT => {
1383 let b = with_host(|h| h.as_bigint(&v)).unwrap();
1384 let r = -(b + num_bigint::BigInt::from(1));
1385 with_host(|h| h.new_bigint(r))
1386 }
1387 _ => Value::Undef,
1388 };
1389 }
1390 with_host(|h| match tag {
1391 host::unop::POS => Value::Float(h.to_number(&v)),
1392 host::unop::BITNOT => {
1393 let n = h.to_number(&v);
1394 let i = if n.is_finite() {
1395 n.trunc() as i64 as i32
1396 } else {
1397 0
1398 };
1399 Value::Float(!i as f64)
1400 }
1401 _ => Value::Undef,
1402 })
1403}
1404
1405fn b_contains(vm: &mut VM, _: u8) -> Value {
1408 let container = vm.pop();
1409 let key = vm.pop();
1410 if !matches!(container, Value::Obj(_)) {
1412 return abort(vm, host::type_error("Cannot use 'in' operator to search"));
1413 }
1414 let k = with_host(|h| h.property_key(&key));
1415 Value::Bool(has_property(&container, &k))
1416}
1417
1418fn b_sig_return(vm: &mut VM, _: u8) -> Value {
1421 let v = vm.pop();
1422 with_host(|h| h.signal = Some(host::Signal::Return(v.clone())));
1423 vm.ip = vm.chunk.ops.len();
1424 v
1425}
1426
1427fn b_throw(vm: &mut VM, _: u8) -> Value {
1428 let v = vm.pop();
1429 let msg = with_host(|h| {
1430 h.exc = Some(v.clone());
1431 error_display(h, &v)
1433 });
1434 abort(vm, msg)
1435}
1436
1437fn error_display(h: &host::JsHost, v: &Value) -> String {
1438 if let Some(JsObj::Object(props)) = h.get(v) {
1439 let name = props
1440 .get("name")
1441 .map(|x| h.str_of(x))
1442 .unwrap_or_else(|| "Error".into());
1443 if let Some(m) = props.get("message") {
1444 return format!("Uncaught {name}: {}", h.str_of(m));
1445 }
1446 }
1447 format!("Uncaught {}", h.str_of(v))
1448}
1449
1450fn b_try(vm: &mut VM, _: u8) -> Value {
1451 let id = match vm.pop() {
1452 Value::Int(n) => n as usize,
1453 _ => return abort(vm, "internal: TRY id".into()),
1454 };
1455 let td = match with_host(|h| h.try_def(id)) {
1456 Some(t) => t,
1457 None => return abort(vm, "internal: unknown try id".into()),
1458 };
1459 let mut pending: Option<String> = None;
1460
1461 let body_res = host::run_chunk_on(td.block.clone());
1462 let signal_after = with_host(|h| h.signal.is_some());
1463 if let Err(e) = body_res {
1464 if signal_after {
1465 pending = Some(e);
1466 } else if let Some((bind, hbody)) = &td.handler {
1467 let thrown =
1469 with_host(|h| h.exc.clone()).unwrap_or_else(|| with_host(|h| synth_error(h, &e)));
1470 with_host(|h| {
1471 h.error = None;
1472 h.exc = None;
1473 });
1474 if let Some(name) = bind {
1475 with_host(|h| h.declare_name(name, thrown));
1476 }
1477 if let Err(e2) = host::run_chunk_on(hbody.clone()) {
1478 pending = Some(e2);
1479 }
1480 } else {
1481 pending = Some(e);
1482 }
1483 }
1484
1485 if let Some(fin) = &td.finalizer {
1487 let sig_before = with_host(|h| h.signal.take());
1488 match host::run_chunk_on(fin.clone()) {
1489 Ok(_) => {
1490 if with_host(|h| h.signal.is_none()) {
1491 with_host(|h| h.signal = sig_before);
1492 }
1493 }
1494 Err(e) => pending = Some(e),
1495 }
1496 }
1497
1498 if let Some(e) = pending {
1499 return abort(vm, e);
1500 }
1501 Value::Undef
1502}
1503
1504pub(crate) fn synth_error(h: &mut host::JsHost, e: &str) -> Value {
1507 h.ensure_error_protos();
1508 let (name, message) = match e.split_once(": ") {
1509 Some((n, m)) if host::ERROR_NAMES.contains(&n) => (n.to_string(), m.to_string()),
1510 _ => ("Error".to_string(), e.to_string()),
1511 };
1512 let mut props: IndexMap<String, Value> = IndexMap::new();
1513 let mv = h.new_str(message.clone());
1514 props.insert("message".into(), mv);
1515 let stack = if message.is_empty() {
1516 format!("{name}\n at <anonymous>")
1517 } else {
1518 format!("{name}: {message}\n at <anonymous>")
1519 };
1520 let sv = h.new_str(stack);
1521 props.insert("stack".into(), sv);
1522 let obj = h.new_object(props);
1523 if let Some(p) = host::error_proto_of(h, &name) {
1524 h.set_proto(&obj, p);
1525 }
1526 obj
1527}
1528
1529fn b_getiter(vm: &mut VM, _: u8) -> Value {
1532 let v = vm.pop();
1533 if with_host(|h| h.is_generator_val(&v)) {
1535 return v;
1536 }
1537 if let Some(iter_fn) = with_host(|h| host::lookup_chain(h, &v, "@@iterator")) {
1539 if with_host(|h| host::is_callable(h, &iter_fn)) {
1540 return match host::invoke(&iter_fn, Vec::new(), Some(v.clone())) {
1541 Ok(it) => it,
1542 Err(e) => abort(vm, e),
1543 };
1544 }
1545 }
1546 match with_host(|h| h.iter_vec(&v)) {
1547 Ok(items) => with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })),
1548 Err(e) => abort(vm, e),
1549 }
1550}
1551
1552fn b_forin_keys(vm: &mut VM, _: u8) -> Value {
1553 let v = vm.pop();
1554 let keys = with_host(|h| h.enum_keys(&v));
1555 with_host(|h| h.new_array(keys))
1556}
1557
1558fn b_foriter(vm: &mut VM, _: u8) -> Value {
1559 let it = match vm.stack.last() {
1560 Some(v) => v.clone(),
1561 None => return abort(vm, "internal: FORITER with empty stack".into()),
1562 };
1563 let eager = with_host(|h| {
1565 if let Some(JsObj::Iter { items, idx }) = h.get_mut(&it) {
1566 if *idx < items.len() {
1567 let v = items[*idx].clone();
1568 *idx += 1;
1569 return Some(Some(v));
1570 }
1571 return Some(None);
1572 }
1573 None
1574 });
1575 if let Some(step) = eager {
1576 return match step {
1577 Some(v) => {
1578 vm.push(v);
1579 Value::Bool(true)
1580 }
1581 None => Value::Bool(false),
1582 };
1583 }
1584 if with_host(|h| h.is_generator_val(&it)) {
1586 return match host::gen_resume(&it, Value::Undef) {
1587 Ok(host::GenStep::Yield(v)) => {
1588 vm.push(v);
1589 Value::Bool(true)
1590 }
1591 Ok(host::GenStep::Done(_)) => Value::Bool(false),
1592 Err(e) => abort(vm, e),
1593 };
1594 }
1595 match host::call_method(&it, "next", Vec::new()) {
1597 Ok(step) => {
1598 let done = get_property(&step, "done")
1599 .map(|d| with_host(|h| h.truthy(&d)))
1600 .unwrap_or(true);
1601 if done {
1602 Value::Bool(false)
1603 } else {
1604 match get_property(&step, "value") {
1605 Ok(v) => {
1606 vm.push(v);
1607 Value::Bool(true)
1608 }
1609 Err(e) => abort(vm, e),
1610 }
1611 }
1612 }
1613 Err(e) => abort(vm, e),
1614 }
1615}
1616
1617fn b_unpack(vm: &mut VM, _: u8) -> Value {
1618 let star = match vm.pop() {
1619 Value::Int(n) => n,
1620 _ => -1,
1621 };
1622 let count = match vm.pop() {
1623 Value::Int(n) => n as usize,
1624 _ => 0,
1625 };
1626 let iterable = vm.pop();
1627 let items = match host::iter_all(&iterable) {
1628 Ok(v) => v,
1629 Err(e) => return abort(vm, e),
1630 };
1631 let ordered: Vec<Value> = if star < 0 {
1632 (0..count)
1633 .map(|i| items.get(i).cloned().unwrap_or(Value::Undef))
1634 .collect()
1635 } else {
1636 let si = star as usize;
1637 let after = count.saturating_sub(si + 1);
1638 let rest_end = items.len().saturating_sub(after).max(si);
1639 let mut out: Vec<Value> = Vec::with_capacity(count);
1640 for i in 0..si {
1641 out.push(items.get(i).cloned().unwrap_or(Value::Undef));
1642 }
1643 let rest: Vec<Value> = items
1644 .get(si..rest_end)
1645 .map(|s| s.to_vec())
1646 .unwrap_or_default();
1647 out.push(with_host(|h| h.new_array(rest)));
1648 for j in 0..after {
1649 out.push(items.get(rest_end + j).cloned().unwrap_or(Value::Undef));
1650 }
1651 out
1652 };
1653 if ordered.is_empty() {
1654 return Value::Undef;
1655 }
1656 for it in ordered[1..].iter().rev().cloned() {
1657 vm.push(it);
1658 }
1659 ordered[0].clone()
1660}
1661
1662fn b_build_args(vm: &mut VM, argc: u8) -> Value {
1663 let flat = pop_n(vm, argc as usize);
1664 let mut out = Vec::new();
1665 let mut i = 0;
1666 while i + 1 < flat.len() {
1667 let spread = matches!(flat[i], Value::Int(1));
1668 let val = flat[i + 1].clone();
1669 if spread {
1670 match host::iter_all(&val) {
1671 Ok(items) => out.extend(items),
1672 Err(e) => return abort(vm, e),
1673 }
1674 } else {
1675 out.push(val);
1676 }
1677 i += 2;
1678 }
1679 with_host(|h| h.new_array(out))
1680}
1681
1682fn b_call(vm: &mut VM, argc: u8) -> Value {
1685 let mut args = pop_n(vm, argc as usize);
1686 let name = sval(&args.remove(0));
1687 let r = host::call_named(&name, args);
1688 finish(vm, r)
1689}
1690
1691fn b_call_method(vm: &mut VM, argc: u8) -> Value {
1692 let mut args = pop_n(vm, argc as usize);
1693 let recv = args.remove(0);
1694 let name = sval(&args.remove(0));
1695 let r = host::call_method(&recv, &name, args);
1696 finish(vm, r)
1697}
1698
1699fn b_call_value(vm: &mut VM, argc: u8) -> Value {
1700 let mut args = pop_n(vm, argc as usize);
1701 let callable = args.remove(0);
1702 let r = host::invoke(&callable, args, None);
1703 finish(vm, r)
1704}
1705
1706fn b_new(vm: &mut VM, argc: u8) -> Value {
1707 let mut args = pop_n(vm, argc as usize);
1708 let ctor = args.remove(0);
1709 let r = host::construct(&ctor, args);
1710 finish(vm, r)
1711}
1712
1713fn b_apply(vm: &mut VM, _: u8) -> Value {
1714 let args_arr = vm.pop();
1715 let callable = vm.pop();
1716 let args = host::iter_all(&args_arr).unwrap_or_default();
1717 let r = host::invoke(&callable, args, None);
1718 finish(vm, r)
1719}
1720
1721fn b_apply_method(vm: &mut VM, _: u8) -> Value {
1722 let args_arr = vm.pop();
1723 let name = sval(&vm.pop());
1724 let recv = vm.pop();
1725 let args = host::iter_all(&args_arr).unwrap_or_default();
1726 let r = host::call_method(&recv, &name, args);
1727 finish(vm, r)
1728}
1729
1730pub fn numeric_hook(op: NumOp, a: &Value, b: &Value) -> Result<Value, String> {
1735 with_host(|h| h.arith(op, a, b))
1736}
1737
1738fn is_namespace(name: &str) -> bool {
1742 matches!(
1743 name,
1744 "console"
1745 | "Math"
1746 | "JSON"
1747 | "Object"
1748 | "Array"
1749 | "Number"
1750 | "String"
1751 | "Boolean"
1752 | "Symbol"
1753 | "Reflect"
1754 | "Promise"
1755 | "process"
1756 | "Buffer"
1757 | "URL"
1758 | "URLSearchParams"
1759 )
1760}
1761
1762const GLOBAL_FUNCS: &[&str] = &[
1763 "parseInt",
1764 "parseFloat",
1765 "isNaN",
1766 "isFinite",
1767 "encodeURIComponent",
1768 "decodeURIComponent",
1769 "encodeURI",
1770 "decodeURI",
1771 "eval",
1772 "String",
1773 "Number",
1774 "Boolean",
1775 "Array",
1776 "Object",
1777 "Function",
1778 "Symbol",
1779 "Map",
1780 "Set",
1781 "WeakMap",
1782 "WeakSet",
1783 "Promise",
1784 "Error",
1785 "TypeError",
1786 "RangeError",
1787 "SyntaxError",
1788 "ReferenceError",
1789 "EvalError",
1790 "URIError",
1791 "BigInt",
1792 "RegExp",
1793 "Date",
1794 "ArrayBuffer",
1795 "Uint8Array",
1796 "Int8Array",
1797 "Uint8ClampedArray",
1798 "Int16Array",
1799 "Uint16Array",
1800 "Int32Array",
1801 "Uint32Array",
1802 "Float32Array",
1803 "Float64Array",
1804 "WeakRef",
1805 "FinalizationRegistry",
1806 "TextEncoder",
1807 "TextDecoder",
1808 "queueMicrotask",
1809 "setTimeout",
1810 "setInterval",
1811 "setImmediate",
1812 "clearTimeout",
1813 "clearInterval",
1814 "structuredClone",
1815 "require",
1816 "__cjs_require",
1819 "__cjs_resolve",
1820];
1821
1822const NS_METHODS: &[&str] = &[
1823 "console.log",
1824 "console.error",
1825 "console.warn",
1826 "console.info",
1827 "console.debug",
1828 "Math.floor",
1829 "Math.ceil",
1830 "Math.round",
1831 "Math.trunc",
1832 "Math.abs",
1833 "Math.sign",
1834 "Math.max",
1835 "Math.min",
1836 "Math.pow",
1837 "Math.sqrt",
1838 "Math.cbrt",
1839 "Math.random",
1840 "Math.hypot",
1841 "Math.clz32",
1842 "Math.fround",
1843 "Math.log",
1844 "Math.log2",
1845 "Math.log10",
1846 "Math.exp",
1847 "Math.sin",
1848 "Math.cos",
1849 "Math.tan",
1850 "Math.atan",
1851 "Math.atan2",
1852 "Math.asin",
1853 "Math.acos",
1854 "JSON.stringify",
1855 "JSON.parse",
1856 "Object.keys",
1857 "Object.values",
1858 "Object.entries",
1859 "Object.assign",
1860 "Object.freeze",
1861 "Object.is",
1862 "Object.fromEntries",
1863 "Object.getPrototypeOf",
1864 "Object.setPrototypeOf",
1865 "Object.create",
1866 "Object.getOwnPropertyNames",
1867 "Object.defineProperty",
1868 "Object.getOwnPropertyDescriptor",
1869 "Object.hasOwn",
1870 "Object.groupBy",
1871 "Array.isArray",
1872 "Array.from",
1873 "Array.of",
1874 "Number.isInteger",
1875 "Number.isNaN",
1876 "Number.isFinite",
1877 "Number.isSafeInteger",
1878 "Number.parseInt",
1879 "Number.parseFloat",
1880 "String.fromCharCode",
1881 "String.fromCodePoint",
1882 "String.raw",
1883 "Symbol.for",
1884 "Symbol.keyFor",
1885 "BigInt.asIntN",
1886 "BigInt.asUintN",
1887 "Reflect.ownKeys",
1888 "Reflect.has",
1889 "Reflect.get",
1890 "Reflect.set",
1891 "Reflect.getPrototypeOf",
1892 "Promise.resolve",
1893 "Promise.reject",
1894 "Promise.all",
1895 "Promise.allSettled",
1896 "Promise.race",
1897 "Promise.any",
1898 "Promise.withResolvers",
1899 "Map.groupBy",
1900 "process.nextTick",
1901 "Error.captureStackTrace",
1902 "require.resolve",
1903];
1904
1905pub fn is_known_builtin(name: &str) -> bool {
1906 GLOBAL_FUNCS.contains(&name)
1907 || NS_METHODS.contains(&name)
1908 || is_namespace(name)
1909 || crate::stdlib::is_method(name)
1910}
1911
1912pub fn call_builtin_function(name: &str, args: Vec<Value>) -> Result<Value, String> {
1914 if name == "require" {
1917 let spec = with_host(|h| h.str_of(&arg0(&args)));
1918 return crate::module::require(&spec, &crate::module::entry_dir());
1919 }
1920 if name == "__cjs_require" {
1923 let spec = with_host(|h| h.str_of(&arg0(&args)));
1924 let from = with_host(|h| h.str_of(args.get(1).unwrap_or(&Value::Undef)));
1925 return crate::module::require(&spec, std::path::Path::new(&from));
1926 }
1927 if name == "require.resolve" {
1929 let spec = with_host(|h| h.str_of(&arg0(&args)));
1930 if crate::stdlib::resolve(&spec).is_some() {
1931 return Ok(with_host(|h| h.new_str(spec)));
1932 }
1933 return match crate::module::resolve(&spec, &crate::module::entry_dir()) {
1934 Some(p) => Ok(with_host(|h| h.new_str(p.to_string_lossy().to_string()))),
1935 None => Err(format!("Error: Cannot find module '{spec}'")),
1936 };
1937 }
1938 if name == "__cjs_resolve" {
1941 let spec = with_host(|h| h.str_of(&arg0(&args)));
1942 let from = with_host(|h| h.str_of(args.get(1).unwrap_or(&Value::Undef)));
1943 if crate::stdlib::resolve(&spec).is_some() {
1944 return Ok(with_host(|h| h.new_str(spec)));
1945 }
1946 return match crate::module::resolve(&spec, std::path::Path::new(&from)) {
1947 Some(p) => Ok(with_host(|h| h.new_str(p.to_string_lossy().to_string()))),
1948 None => Err(format!("Error: Cannot find module '{spec}'")),
1949 };
1950 }
1951 if name == "Error.captureStackTrace" {
1956 let target = arg0(&args);
1957 let prep = with_host(|h| h.builtin_static("Error", "prepareStackTrace"));
1958 let stack = match prep {
1959 Some(f)
1960 if matches!(
1961 with_host(|h| h.get(&f).cloned()),
1962 Some(JsObj::Func(_)) | Some(JsObj::Builtin(_)) | Some(JsObj::BoundFunc { .. })
1963 ) =>
1964 {
1965 let sites = crate::module::callsite_stack(10)?;
1966 host::invoke(&f, vec![target.clone(), sites], None)?
1967 }
1968 _ => with_host(|h| h.new_str("")),
1969 };
1970 set_property(&target, "stack", stack);
1971 return Ok(Value::Undef);
1972 }
1973 if let Some(r) = crate::stdlib::call(name, &args) {
1975 return r;
1976 }
1977 match name {
1978 "console.log" | "console.info" | "console.debug" => {
1979 print_line(&args, false);
1980 Ok(Value::Undef)
1981 }
1982 "console.error" | "console.warn" => {
1983 print_line(&args, true);
1984 Ok(Value::Undef)
1985 }
1986 "parseInt" | "Number.parseInt" => Ok(Value::Float(parse_int(&args))),
1987 "parseFloat" | "Number.parseFloat" => Ok(Value::Float(parse_float(&args))),
1988 "isNaN" => Ok(Value::Bool(arg_num(&args, 0).is_nan())),
1989 "isFinite" => Ok(Value::Bool(arg_num(&args, 0).is_finite())),
1990 "encodeURIComponent" => uri_encode(&with_host(|h| h.str_of(&arg0(&args))), false),
1991 "encodeURI" => uri_encode(&with_host(|h| h.str_of(&arg0(&args))), true),
1992 "decodeURIComponent" => uri_decode(&with_host(|h| h.str_of(&arg0(&args))), false),
1993 "decodeURI" => uri_decode(&with_host(|h| h.str_of(&arg0(&args))), true),
1994 "eval" => match arg0(&args) {
1998 v @ (Value::Undef | Value::Bool(_) | Value::Int(_) | Value::Float(_)) => Ok(v),
1999 _ => Err(host::type_error("eval is not supported in node-js")),
2000 },
2001 "Number.isInteger" => Ok(Value::Bool(is_integer(arg0(&args)))),
2002 "Number.isSafeInteger" => Ok(Value::Bool(is_safe_integer(arg0(&args)))),
2003 "Number.isNaN" => Ok(Value::Bool(
2004 matches!(arg0(&args), Value::Float(f) if f.is_nan()),
2005 )),
2006 "Number.isFinite" => Ok(Value::Bool(
2007 matches!(arg0(&args), Value::Float(f) if f.is_finite())
2008 || matches!(arg0(&args), Value::Int(_)),
2009 )),
2010 "String" => {
2011 if args.is_empty() {
2012 Ok(with_host(|h| h.new_str("")))
2013 } else {
2014 host::to_string_value(&args[0])
2017 }
2018 }
2019 "Number" => Ok(Value::Float(if args.is_empty() {
2020 0.0
2021 } else {
2022 with_host(|h| h.to_number(&args[0]))
2023 })),
2024 "BigInt" => bigint_ctor(&arg0(&args)),
2025 "RegExp" => regexp_ctor(&args),
2026 "BigInt.asIntN" | "BigInt.asUintN" => bigint_as_n(name.ends_with("asUintN"), &args),
2027 "Boolean" => Ok(Value::Bool(with_host(|h| h.truthy(&arg0(&args))))),
2028 "String.fromCharCode" => Ok(with_host(|h| {
2029 let s: String = args
2030 .iter()
2031 .filter_map(|a| char::from_u32(h.to_number(a) as u32))
2032 .collect();
2033 h.new_str(s)
2034 })),
2035 "String.raw" => string_raw(&args),
2036 "Array" => construct_builtin("Array", args),
2038 "Array.of" => Ok(with_host(|h| h.new_array(args))),
2039 "Array.isArray" => Ok(Value::Bool(matches!(
2040 with_host(|h| h.get(&arg0(&args)).cloned()),
2041 Some(JsObj::Array(_))
2042 ))),
2043 "Array.from" => array_from(args),
2044 "Object" => Ok(object_call(args)),
2045 "Object.keys" => object_keys(args, 0),
2046 "Object.values" => object_keys(args, 1),
2047 "Object.entries" => object_keys(args, 2),
2048 "Object.assign" => object_assign(args),
2049 "Object.freeze" => Ok(arg0(&args)),
2050 "Object.is" => {
2053 let a = arg0(&args);
2054 let b = args.get(1).cloned().unwrap_or(Value::Undef);
2055 let num = |v: &Value| match v {
2056 Value::Int(n) => Some(*n as f64),
2057 Value::Float(f) => Some(*f),
2058 _ => None,
2059 };
2060 let r = match (num(&a), num(&b)) {
2061 (Some(x), Some(y)) => {
2062 if x.is_nan() && y.is_nan() {
2063 true
2064 } else if x == 0.0 && y == 0.0 {
2065 x.is_sign_negative() == y.is_sign_negative()
2066 } else {
2067 x == y
2068 }
2069 }
2070 _ => with_host(|h| h.strict_eq(&a, &b)),
2071 };
2072 Ok(Value::Bool(r))
2073 }
2074 "Object.fromEntries" => object_from_entries(args),
2075 "Object.getPrototypeOf" | "Reflect.getPrototypeOf" => Ok(with_host(|h| {
2076 h.proto_of(&arg0(&args)).unwrap_or_else(|| h.null())
2077 })),
2078 "Object.setPrototypeOf" => {
2079 let obj = arg0(&args);
2080 let proto = args.get(1).cloned().unwrap_or(Value::Undef);
2081 with_host(|h| h.set_proto(&obj, proto));
2082 Ok(obj)
2083 }
2084 "Object.create" => object_create(args),
2085 "Object.getOwnPropertyNames" => object_keys(args, 0),
2086 "Object.hasOwn" => {
2088 let obj = arg0(&args);
2089 let key = args.get(1).cloned().unwrap_or(Value::Undef);
2090 object_builtin_method(&obj, "hasOwnProperty", vec![key])
2091 }
2092 "Object.defineProperty" => object_define_property(args),
2093 "Object.getOwnPropertyDescriptor" => object_get_own_descriptor(args),
2094 "Object.groupBy" => object_group_by(args),
2097 "Symbol" => Ok(with_host(|h| {
2098 let desc = args
2099 .first()
2100 .filter(|a| !matches!(a, Value::Undef))
2101 .map(|a| h.str_of(a));
2102 h.new_symbol(desc)
2103 })),
2104 "Symbol.for" => Ok(with_host(|h| {
2105 let key = h.str_of(&arg0(&args));
2106 h.symbol_for(&key)
2107 })),
2108 "Symbol.keyFor" => Ok(with_host(|h| match h.get(&arg0(&args)) {
2109 Some(JsObj::Symbol { desc, .. }) => {
2110 desc.clone().map(|d| h.new_str(d)).unwrap_or(Value::Undef)
2111 }
2112 _ => Value::Undef,
2113 })),
2114 "Map" | "WeakMap" | "Set" | "WeakSet" | "Promise" => construct_builtin(name, args),
2115 "Reflect.ownKeys" => object_keys(args, 0),
2116 "Reflect.has" => {
2117 let obj = arg0(&args);
2118 let k = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
2119 Ok(Value::Bool(has_property(&obj, &k)))
2120 }
2121 "Reflect.get" => {
2122 let obj = arg0(&args);
2123 let k = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
2124 get_property(&obj, &k)
2125 }
2126 "Reflect.set" => {
2127 let obj = arg0(&args);
2128 let k = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
2129 let v = args.get(2).cloned().unwrap_or(Value::Undef);
2130 set_property(&obj, &k, v);
2131 Ok(Value::Bool(true))
2132 }
2133 "JSON.stringify" => json_stringify(args),
2134 "JSON.parse" => json_parse(args),
2135 "structuredClone" => Ok(deep_clone(&arg0(&args))),
2136 "queueMicrotask" | "process.nextTick" => {
2137 let cb = arg0(&args);
2138 let rest = args.get(1..).map(|s| s.to_vec()).unwrap_or_default();
2139 enqueue_microtask(name == "process.nextTick", cb, rest);
2140 Ok(Value::Undef)
2141 }
2142 "setTimeout" | "setInterval" | "setImmediate" => Ok(schedule_timer(name, args)),
2143 "clearTimeout" | "clearInterval" => {
2144 clear_timer(&arg0(&args));
2145 Ok(Value::Undef)
2146 }
2147 "Promise.resolve" => promise_resolve(arg0(&args)),
2148 "Promise.reject" => promise_reject(arg0(&args)),
2149 "Promise.all" => promise_all(args, AllMode::All),
2150 "Promise.allSettled" => promise_all(args, AllMode::AllSettled),
2151 "Promise.race" => promise_race(args, false),
2152 "Promise.any" => promise_race(args, true),
2153 "Promise.withResolvers" => promise_with_resolvers(),
2156 "Map.groupBy" => map_group_by(args),
2159 n if host::ERROR_NAMES.contains(&n) => Ok(make_error(name, &args)),
2160 _ if name.starts_with("Math.") => math_fn(&name[5..], &args),
2161 _ if name.starts_with("@@presolve:") => {
2163 let id: u32 = name[11..].parse().unwrap_or(0);
2164 host::resolve_promise_val(id, arg0(&args));
2165 Ok(Value::Undef)
2166 }
2167 _ if name.starts_with("@@preject:") => {
2168 let id: u32 = name[10..].parse().unwrap_or(0);
2169 host::reject_promise_val(id, arg0(&args));
2170 Ok(Value::Undef)
2171 }
2172 _ if name.starts_with("@@finpass:") => {
2173 let i: u32 = name[10..].parse().unwrap_or(0);
2175 let cb = Value::Obj(i);
2176 host::invoke(&cb, Vec::new(), None)?;
2177 Ok(arg0(&args))
2178 }
2179 _ if name.starts_with("@@finthrow:") => {
2180 let i: u32 = name[11..].parse().unwrap_or(0);
2182 let cb = Value::Obj(i);
2183 host::invoke(&cb, Vec::new(), None)?;
2184 let reason = arg0(&args);
2185 with_host(|h| h.exc = Some(reason.clone()));
2186 Err(with_host(|h| error_string(h, &reason)))
2187 }
2188 _ => Err(host::type_error(&format!("{name} is not a function"))),
2189 }
2190}
2191
2192fn bigint_ctor(v: &Value) -> Result<Value, String> {
2196 use num_bigint::BigInt;
2197 let big = match v {
2198 Value::Bool(b) => BigInt::from(*b as i64),
2199 Value::Int(n) => BigInt::from(*n),
2200 Value::Float(f) => {
2201 if !f.is_finite() || f.fract() != 0.0 {
2202 let disp = with_host(|h| h.str_of(v));
2203 return Err(format!(
2204 "RangeError: The number {disp} cannot be converted to a BigInt because it is not an integer"
2205 ));
2206 }
2207 match BigInt::parse_bytes(host::fmt_number(*f).as_bytes(), 10) {
2210 Some(b) => b,
2211 None => return Err(host::type_error("Cannot convert value to a BigInt")),
2212 }
2213 }
2214 Value::Str(s) => match host::parse_bigint_str(s) {
2215 Some(b) => b,
2216 None => return Err(format!("SyntaxError: Cannot convert {s} to a BigInt")),
2217 },
2218 Value::Obj(_) => match with_host(|h| h.get(v).cloned()) {
2219 Some(JsObj::BigInt(b)) => b,
2220 Some(JsObj::Str(s)) => match host::parse_bigint_str(&s) {
2221 Some(b) => b,
2222 None => return Err(format!("SyntaxError: Cannot convert {s} to a BigInt")),
2223 },
2224 _ => return Err(host::type_error("Cannot convert value to a BigInt")),
2225 },
2226 _ => return Err(host::type_error("Cannot convert value to a BigInt")),
2227 };
2228 Ok(with_host(|h| h.new_bigint(big)))
2229}
2230
2231fn regexp_ctor(args: &[Value]) -> Result<Value, String> {
2234 let (source, existing_flags) = match with_host(|h| h.get(&arg0(args)).cloned()) {
2235 Some(JsObj::RegExp(r)) => (r.source.clone(), Some(r.flags.clone())),
2236 _ => {
2237 let a0 = arg0(args);
2238 let src = if matches!(a0, Value::Undef) {
2239 String::new()
2240 } else {
2241 with_host(|h| h.str_of(&a0))
2242 };
2243 (src, None)
2244 }
2245 };
2246 let flags = match args.get(1) {
2247 Some(v) if !matches!(v, Value::Undef) => with_host(|h| h.str_of(v)),
2248 _ => existing_flags.unwrap_or_default(),
2249 };
2250 let src = if source.is_empty() {
2252 "(?:)".to_string()
2253 } else {
2254 source
2255 };
2256 crate::regexp::build_regexp(&src, &flags)
2257}
2258
2259fn bigint_as_n(unsigned: bool, args: &[Value]) -> Result<Value, String> {
2262 use num_bigint::BigInt;
2263 use num_traits::Signed;
2264 let bits = with_host(|h| h.to_number(&arg0(args))) as i64;
2265 if bits < 0 {
2266 return Err("RangeError: Invalid value: not (convertible to) a safe integer".into());
2267 }
2268 let x = match with_host(|h| h.as_bigint(&args.get(1).cloned().unwrap_or(Value::Undef))) {
2269 Some(b) => b,
2270 None => return Err(host::type_error("Cannot convert to a BigInt")),
2271 };
2272 let bits = bits as u32;
2273 if bits == 0 {
2274 return Ok(with_host(|h| h.new_bigint(BigInt::from(0))));
2275 }
2276 let modulus = BigInt::from(1) << bits; let mut r = &x % &modulus;
2279 if r.is_negative() {
2280 r += &modulus;
2281 }
2282 if !unsigned {
2283 let half = BigInt::from(1) << (bits - 1);
2284 if r >= half {
2285 r -= &modulus;
2286 }
2287 }
2288 Ok(with_host(|h| h.new_bigint(r)))
2289}
2290
2291fn string_raw(args: &[Value]) -> Result<Value, String> {
2294 let call_site = arg0(args);
2295 let raw = get_property(&call_site, "raw")?;
2296 let raws = with_host(|h| h.iter_vec(&raw)).unwrap_or_default();
2297 let mut out = String::new();
2298 for (i, r) in raws.iter().enumerate() {
2299 out.push_str(&with_host(|h| h.str_of(r)));
2300 if i + 1 < raws.len() {
2301 if let Some(sub) = args.get(i + 1) {
2302 out.push_str(&with_host(|h| h.str_of(sub)));
2303 }
2304 }
2305 }
2306 Ok(with_host(|h| h.new_str(out)))
2307}
2308
2309fn object_call(args: Vec<Value>) -> Value {
2312 let a = arg0(&args);
2313 if matches!(
2314 with_host(|h| h.get(&a).cloned()),
2315 Some(JsObj::Object(_)) | Some(JsObj::Array(_))
2316 ) {
2317 a
2318 } else {
2319 with_host(|h| h.new_object(IndexMap::new()))
2320 }
2321}
2322
2323pub fn construct_builtin(name: &str, args: Vec<Value>) -> Result<Value, String> {
2325 if let Some(r) = crate::stdlib::construct(name, &args) {
2327 return r;
2328 }
2329 match name {
2330 "Array" => {
2331 if args.len() == 1 {
2333 if let Value::Float(f) = args[0] {
2334 if f.fract() == 0.0 && f >= 0.0 {
2335 return Ok(with_host(|h| h.new_array(vec![Value::Undef; f as usize])));
2336 }
2337 }
2338 }
2339 Ok(with_host(|h| h.new_array(args)))
2340 }
2341 "Object" => Ok(object_call(args)),
2342 "Map" | "WeakMap" => {
2343 let weak = name == "WeakMap";
2344 let m = with_host(|h| {
2345 h.alloc(JsObj::Map {
2346 entries: indexmap::IndexMap::new(),
2347 weak,
2348 })
2349 });
2350 if let Some(init) = args
2351 .first()
2352 .filter(|a| !matches!(a, Value::Undef) && !with_host(|h| h.is_null(a)))
2353 {
2354 let pairs = host::iter_all(init)?;
2355 for p in pairs {
2356 let kv = host::iter_all(&p)?;
2357 let k = kv.first().cloned().unwrap_or(Value::Undef);
2358 let v = kv.get(1).cloned().unwrap_or(Value::Undef);
2359 map_method(&m, "set", vec![k, v])?;
2360 }
2361 }
2362 Ok(m)
2363 }
2364 "Set" | "WeakSet" => {
2365 let weak = name == "WeakSet";
2366 let s = with_host(|h| {
2367 h.alloc(JsObj::Set {
2368 entries: indexmap::IndexMap::new(),
2369 weak,
2370 })
2371 });
2372 if let Some(init) = args
2373 .first()
2374 .filter(|a| !matches!(a, Value::Undef) && !with_host(|h| h.is_null(a)))
2375 {
2376 let vals = host::iter_all(init)?;
2377 for v in vals {
2378 set_method(&s, "add", vec![v])?;
2379 }
2380 }
2381 Ok(s)
2382 }
2383 "Promise" => new_promise(arg0(&args)),
2384 "RegExp" => regexp_ctor(&args),
2385 "BigInt" => Err(host::type_error("BigInt is not a constructor")),
2386 "Error" => Ok(make_error(name, &args)),
2387 n if host::ERROR_NAMES.contains(&n) => Ok(make_error(name, &args)),
2388 _ => Err(host::type_error(&format!("{name} is not a constructor"))),
2389 }
2390}
2391
2392fn make_error(name: &str, args: &[Value]) -> Value {
2393 with_host(|h| {
2394 h.ensure_error_protos();
2395 let mut props: IndexMap<String, Value> = IndexMap::new();
2396 let msg = args
2397 .first()
2398 .filter(|a| !matches!(a, Value::Undef))
2399 .map(|a| h.str_of(a));
2400 if let Some(m) = &msg {
2401 let mv = h.new_str(m.clone());
2402 props.insert("message".into(), mv);
2403 }
2404 let stack = match &msg {
2407 Some(m) if !m.is_empty() => format!("{name}: {m}\n at <anonymous>"),
2408 _ => format!("{name}\n at <anonymous>"),
2409 };
2410 let sv = h.new_str(stack);
2411 props.insert("stack".into(), sv);
2412 let e = h.new_object(props);
2413 if let Some(p) = host::error_proto_of(h, name) {
2414 h.set_proto(&e, p);
2415 }
2416 e
2417 })
2418}
2419
2420fn print_line(args: &[Value], stderr: bool) {
2421 let line: String = crate::stdlib::util::format(args);
2424 if stderr {
2425 eprintln!("{line}");
2426 } else {
2427 println!("{line}");
2428 }
2429}
2430
2431fn arg0(args: &[Value]) -> Value {
2432 args.first().cloned().unwrap_or(Value::Undef)
2433}
2434fn arg_num(args: &[Value], i: usize) -> f64 {
2435 with_host(|h| h.to_number(&args.get(i).cloned().unwrap_or(Value::Undef)))
2436}
2437
2438fn is_integer(v: Value) -> bool {
2439 match v {
2440 Value::Int(_) => true,
2441 Value::Float(f) => f.is_finite() && f.fract() == 0.0,
2442 _ => false,
2443 }
2444}
2445fn is_safe_integer(v: Value) -> bool {
2446 match v {
2447 Value::Float(f) => f.is_finite() && f.fract() == 0.0 && f.abs() <= 9007199254740991.0,
2448 Value::Int(_) => true,
2449 _ => false,
2450 }
2451}
2452
2453fn uri_encode(s: &str, uri: bool) -> Result<Value, String> {
2457 const UNRESERVED: &[u8] =
2459 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()";
2460 const RESERVED: &[u8] = b";,/?:@&=+$#";
2462 let mut out = String::with_capacity(s.len());
2463 for &b in s.as_bytes() {
2464 if UNRESERVED.contains(&b) || (uri && RESERVED.contains(&b)) {
2465 out.push(b as char);
2466 } else {
2467 out.push('%');
2468 out.push(
2469 char::from_digit((b >> 4) as u32, 16)
2470 .unwrap()
2471 .to_ascii_uppercase(),
2472 );
2473 out.push(
2474 char::from_digit((b & 0xf) as u32, 16)
2475 .unwrap()
2476 .to_ascii_uppercase(),
2477 );
2478 }
2479 }
2480 Ok(with_host(|h| h.new_str(out)))
2481}
2482
2483fn uri_decode(s: &str, uri: bool) -> Result<Value, String> {
2487 const RESERVED: &[u8] = b";,/?:@&=+$#";
2488 let bytes = s.as_bytes();
2489 let mut out: Vec<u8> = Vec::with_capacity(bytes.len());
2490 let mut i = 0;
2491 while i < bytes.len() {
2492 if bytes[i] == b'%' {
2493 if i + 2 >= bytes.len() {
2494 return Err("URIError: URI malformed".into());
2495 }
2496 let hi = (bytes[i + 1] as char).to_digit(16);
2497 let lo = (bytes[i + 2] as char).to_digit(16);
2498 match (hi, lo) {
2499 (Some(h), Some(l)) => {
2500 let byte = (h * 16 + l) as u8;
2501 if uri && RESERVED.contains(&byte) {
2503 out.extend_from_slice(&bytes[i..i + 3]);
2504 } else {
2505 out.push(byte);
2506 }
2507 i += 3;
2508 }
2509 _ => return Err("URIError: URI malformed".into()),
2510 }
2511 } else {
2512 out.push(bytes[i]);
2513 i += 1;
2514 }
2515 }
2516 match String::from_utf8(out) {
2517 Ok(decoded) => Ok(with_host(|h| h.new_str(decoded))),
2518 Err(_) => Err("URIError: URI malformed".into()),
2519 }
2520}
2521
2522fn parse_int(args: &[Value]) -> f64 {
2523 let s = with_host(|h| h.str_of(&arg0(args)));
2524 let radix = args
2525 .get(1)
2526 .map(|r| with_host(|h| h.to_number(r)) as u32)
2527 .filter(|r| (2..=36).contains(r));
2528 let t = s.trim();
2529 let (neg, digits) = match t.strip_prefix('-') {
2530 Some(rest) => (true, rest),
2531 None => (false, t.strip_prefix('+').unwrap_or(t)),
2532 };
2533 let (radix, digits) = match radix {
2534 Some(16) => (
2535 16u32,
2536 digits
2537 .strip_prefix("0x")
2538 .or_else(|| digits.strip_prefix("0X"))
2539 .unwrap_or(digits),
2540 ),
2541 Some(r) => (r, digits),
2542 None => {
2543 if let Some(hex) = digits
2544 .strip_prefix("0x")
2545 .or_else(|| digits.strip_prefix("0X"))
2546 {
2547 (16, hex)
2548 } else {
2549 (10, digits)
2550 }
2551 }
2552 };
2553 let valid: String = digits.chars().take_while(|c| c.is_digit(radix)).collect();
2554 if valid.is_empty() {
2555 return f64::NAN;
2556 }
2557 let n = i64::from_str_radix(&valid, radix)
2558 .map(|n| n as f64)
2559 .unwrap_or(f64::NAN);
2560 if neg {
2561 -n
2562 } else {
2563 n
2564 }
2565}
2566
2567fn parse_float(args: &[Value]) -> f64 {
2568 let s = with_host(|h| h.str_of(&arg0(args)));
2569 let t = s.trim_start();
2570 let inf_body = t
2572 .strip_prefix('+')
2573 .or_else(|| t.strip_prefix('-'))
2574 .unwrap_or(t);
2575 if inf_body.starts_with("Infinity") {
2576 return if t.starts_with('-') {
2577 f64::NEG_INFINITY
2578 } else {
2579 f64::INFINITY
2580 };
2581 }
2582 let mut end = 0;
2584 let bytes = t.as_bytes();
2585 let mut seen_dot = false;
2586 let mut seen_e = false;
2587 for (i, &c) in bytes.iter().enumerate() {
2588 match c {
2589 b'0'..=b'9' => end = i + 1,
2590 b'+' | b'-' if i == 0 || bytes[i - 1] == b'e' || bytes[i - 1] == b'E' => end = i + 1,
2591 b'.' if !seen_dot && !seen_e => {
2592 seen_dot = true;
2593 end = i + 1;
2594 }
2595 b'e' | b'E' if !seen_e && i > 0 => {
2596 seen_e = true;
2597 end = i + 1;
2598 }
2599 _ => break,
2600 }
2601 }
2602 t[..end].parse::<f64>().unwrap_or(f64::NAN)
2603}
2604
2605fn math_fn(fname: &str, args: &[Value]) -> Result<Value, String> {
2606 let x = arg_num(args, 0);
2607 let r = match fname {
2608 "floor" => x.floor(),
2609 "ceil" => x.ceil(),
2610 "round" => {
2611 let r = (x + 0.5).floor();
2614 if r == 0.0 && x.is_sign_negative() {
2615 -0.0
2616 } else {
2617 r
2618 }
2619 }
2620 "trunc" => x.trunc(),
2621 "abs" => x.abs(),
2622 "sign" => {
2623 if x.is_nan() {
2624 f64::NAN
2625 } else if x > 0.0 {
2626 1.0
2627 } else if x < 0.0 {
2628 -1.0
2629 } else {
2630 x
2631 }
2632 }
2633 "sqrt" => x.sqrt(),
2634 "cbrt" => x.cbrt(),
2635 "exp" => x.exp(),
2636 "log" => x.ln(),
2637 "log2" => x.log2(),
2638 "log10" => x.log10(),
2639 "sin" => x.sin(),
2640 "cos" => x.cos(),
2641 "tan" => x.tan(),
2642 "asin" => x.asin(),
2643 "acos" => x.acos(),
2644 "atan" => x.atan(),
2645 "atan2" => x.atan2(arg_num(args, 1)),
2646 "pow" => x.powf(arg_num(args, 1)),
2647 "hypot" => {
2648 let xs: Vec<f64> = args.iter().map(|a| with_host(|h| h.to_number(a))).collect();
2651 let mut max = 0.0f64;
2652 for x in &xs {
2653 if x.abs() > max {
2654 max = x.abs();
2655 }
2656 }
2657 if xs.iter().any(|x| x.is_infinite()) {
2658 f64::INFINITY
2659 } else if max == 0.0 || !max.is_finite() {
2660 max
2661 } else {
2662 let s: f64 = xs.iter().map(|x| (x / max) * (x / max)).sum();
2663 max * s.sqrt()
2664 }
2665 }
2666 "random" => pseudo_random(),
2667 "max" => {
2668 if args.is_empty() {
2669 f64::NEG_INFINITY
2670 } else {
2671 let mut m = f64::NEG_INFINITY;
2672 for a in args {
2673 let n = with_host(|h| h.to_number(a));
2674 if n.is_nan() {
2675 return Ok(Value::Float(f64::NAN));
2676 }
2677 if n > m {
2678 m = n;
2679 }
2680 }
2681 m
2682 }
2683 }
2684 "min" => {
2685 if args.is_empty() {
2686 f64::INFINITY
2687 } else {
2688 let mut m = f64::INFINITY;
2689 for a in args {
2690 let n = with_host(|h| h.to_number(a));
2691 if n.is_nan() {
2692 return Ok(Value::Float(f64::NAN));
2693 }
2694 if n < m {
2695 m = n;
2696 }
2697 }
2698 m
2699 }
2700 }
2701 "clz32" => {
2703 let u = if x.is_finite() {
2704 x.trunc().rem_euclid(4294967296.0) as u32
2705 } else {
2706 0
2707 };
2708 u.leading_zeros() as f64
2709 }
2710 "fround" => (x as f32) as f64,
2712 _ => return Err(host::type_error(&format!("Math.{fname} is not a function"))),
2713 };
2714 Ok(Value::Float(r))
2715}
2716
2717fn pseudo_random() -> f64 {
2720 use std::cell::Cell;
2721 thread_local!(static SEED: Cell<u64> = const { Cell::new(0x2545F4914F6CDD1D) });
2722 SEED.with(|s| {
2723 let mut x = s.get();
2724 x ^= x << 13;
2725 x ^= x >> 7;
2726 x ^= x << 17;
2727 s.set(x);
2728 (x >> 11) as f64 / (1u64 << 53) as f64
2729 })
2730}
2731
2732fn object_keys(args: Vec<Value>, mode: u8) -> Result<Value, String> {
2735 let v = arg0(&args);
2736 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(&v).cloned()) {
2739 if let Some(names) = builtin_proto_method_names(&ns) {
2740 return Ok(with_host(|h| {
2741 let out: Vec<Value> = names
2742 .iter()
2743 .map(|name| match mode {
2744 1 => h.alloc(JsObj::Builtin(format!(
2745 "@proto:{}:{name}",
2746 ns.trim_end_matches(".prototype")
2747 ))),
2748 2 => {
2749 let ks = h.new_str(*name);
2750 let val = h.alloc(JsObj::Builtin(format!(
2751 "@proto:{}:{name}",
2752 ns.trim_end_matches(".prototype")
2753 )));
2754 h.new_array(vec![ks, val])
2755 }
2756 _ => h.new_str(*name),
2757 })
2758 .collect();
2759 h.new_array(out)
2760 }));
2761 }
2762 }
2763 let entries: Vec<(String, Value)> = with_host(|h| match h.get(&v) {
2764 Some(JsObj::Object(props)) => props
2765 .iter()
2766 .filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
2767 .map(|(k, val)| (k.clone(), val.clone()))
2768 .collect(),
2769 Some(JsObj::Array(items)) => items
2770 .iter()
2771 .enumerate()
2772 .map(|(i, val)| (i.to_string(), val.clone()))
2773 .collect(),
2774 _ => Vec::new(),
2775 });
2776 Ok(with_host(|h| {
2777 let out: Vec<Value> = entries
2778 .into_iter()
2779 .map(|(k, val)| match mode {
2780 0 => h.new_str(k),
2781 1 => val,
2782 _ => {
2783 let ks = h.new_str(k);
2784 h.new_array(vec![ks, val])
2785 }
2786 })
2787 .collect();
2788 h.new_array(out)
2789 }))
2790}
2791
2792fn object_assign(args: Vec<Value>) -> Result<Value, String> {
2793 let target = arg0(&args);
2794 for src in args.iter().skip(1) {
2795 let entries: Vec<(String, Value)> = with_host(|h| match h.get(src) {
2796 Some(JsObj::Object(p)) => p.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
2797 _ => Vec::new(),
2798 });
2799 with_host(|h| {
2800 if let Some(JsObj::Object(p)) = h.get_mut(&target) {
2801 for (k, v) in entries {
2802 p.insert(k, v);
2803 }
2804 host::canonicalize_own_keys(p);
2805 }
2806 });
2807 }
2808 Ok(target)
2809}
2810
2811fn object_from_entries(args: Vec<Value>) -> Result<Value, String> {
2812 let pairs = with_host(|h| h.iter_vec(&arg0(&args))).unwrap_or_default();
2813 let mut props: IndexMap<String, Value> = IndexMap::new();
2814 for p in pairs {
2815 let kv = with_host(|h| h.iter_vec(&p)).unwrap_or_default();
2816 let key = with_host(|h| h.str_of(&kv.first().cloned().unwrap_or(Value::Undef)));
2817 let val = kv.get(1).cloned().unwrap_or(Value::Undef);
2818 props.insert(key, val);
2819 }
2820 Ok(with_host(|h| h.new_object(props)))
2821}
2822
2823fn object_group_by(args: Vec<Value>) -> Result<Value, String> {
2827 let items = host::iter_all(&arg0(&args))?;
2828 let cb = args.get(1).cloned().unwrap_or(Value::Undef);
2829 let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
2830 for (i, item) in items.into_iter().enumerate() {
2831 let key_v = host::invoke(&cb, vec![item.clone(), Value::Float(i as f64)], None)?;
2832 let key = with_host(|h| h.property_key(&key_v));
2833 groups.entry(key).or_default().push(item);
2834 }
2835 let props: IndexMap<String, Value> = with_host(|h| {
2836 groups
2837 .into_iter()
2838 .map(|(k, v)| (k, h.new_array(v)))
2839 .collect()
2840 });
2841 let obj = with_host(|h| h.new_object(props));
2842 with_host(|h| {
2844 let nv = h.null();
2845 h.set_proto(&obj, nv);
2846 });
2847 Ok(obj)
2848}
2849
2850fn map_group_by(args: Vec<Value>) -> Result<Value, String> {
2853 let items = host::iter_all(&arg0(&args))?;
2854 let cb = args.get(1).cloned().unwrap_or(Value::Undef);
2855 let m = with_host(|h| {
2856 h.alloc(JsObj::Map {
2857 entries: IndexMap::new(),
2858 weak: false,
2859 })
2860 });
2861 for (i, item) in items.into_iter().enumerate() {
2862 let key_v = host::invoke(&cb, vec![item.clone(), Value::Float(i as f64)], None)?;
2863 let existing = map_method(&m, "get", vec![key_v.clone()])?;
2864 if matches!(existing, Value::Undef) {
2865 let arr = with_host(|h| h.new_array(vec![item]));
2866 map_method(&m, "set", vec![key_v, arr])?;
2867 } else {
2868 with_host(|h| {
2869 if let Some(JsObj::Array(a)) = h.get_mut(&existing) {
2870 a.push(item);
2871 }
2872 });
2873 }
2874 }
2875 Ok(m)
2876}
2877
2878fn array_from(args: Vec<Value>) -> Result<Value, String> {
2879 let src = arg0(&args);
2882 let items = match host::iter_all(&src) {
2883 Ok(v) => v,
2884 Err(_) => array_like_items(&src),
2885 };
2886 if let Some(cb) = args.get(1).cloned() {
2887 let mut out = Vec::with_capacity(items.len());
2888 for (i, it) in items.into_iter().enumerate() {
2889 out.push(host::invoke(&cb, vec![it, Value::Float(i as f64)], None)?);
2890 }
2891 return Ok(with_host(|h| h.new_array(out)));
2892 }
2893 Ok(with_host(|h| h.new_array(items)))
2894}
2895
2896fn array_like_items(src: &Value) -> Vec<Value> {
2898 let len = get_property(src, "length")
2899 .ok()
2900 .map(|l| with_host(|h| h.to_number(&l)))
2901 .unwrap_or(0.0);
2902 if !len.is_finite() || len <= 0.0 {
2903 return Vec::new();
2904 }
2905 (0..len as usize)
2906 .map(|i| get_property(src, &i.to_string()).unwrap_or(Value::Undef))
2907 .collect()
2908}
2909
2910fn json_stringify(args: Vec<Value>) -> Result<Value, String> {
2913 let v = arg0(&args);
2914 if with_host(|h| json_has_bigint(h, &v)) {
2917 return Err(host::type_error("Do not know how to serialize a BigInt"));
2918 }
2919 let indent = match args.get(2) {
2920 Some(Value::Float(f)) => " ".repeat((*f as usize).min(10)),
2921 Some(other) => with_host(|h| h.as_str(other)).unwrap_or_default(),
2922 None => String::new(),
2923 };
2924 let keys: Option<Vec<String>> = args.get(1).and_then(|r| {
2926 with_host(|h| match h.get(r) {
2927 Some(JsObj::Array(items)) => {
2928 Some(items.iter().map(|k| h.str_of(k)).collect::<Vec<_>>())
2929 }
2930 _ => None,
2931 })
2932 });
2933 let s = with_host(|h| json_str(h, &v, &indent, 0, keys.as_deref()));
2934 match s {
2935 Some(s) => Ok(with_host(|h| h.new_str(s))),
2936 None => Ok(Value::Undef),
2937 }
2938}
2939
2940fn json_has_bigint(h: &host::JsHost, v: &Value) -> bool {
2943 match h.get(v) {
2944 Some(JsObj::BigInt(_)) => true,
2945 Some(JsObj::Array(items)) => items.iter().any(|x| json_has_bigint(h, x)),
2946 Some(JsObj::Object(props)) => props
2947 .iter()
2948 .filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
2949 .any(|(_, val)| json_has_bigint(h, val)),
2950 _ => false,
2951 }
2952}
2953
2954fn json_str(
2955 h: &host::JsHost,
2956 v: &Value,
2957 indent: &str,
2958 depth: usize,
2959 keys: Option<&[String]>,
2960) -> Option<String> {
2961 let sep = if indent.is_empty() { ":" } else { ": " };
2962 match v {
2963 Value::Undef => None,
2964 Value::Bool(b) => Some(if *b { "true".into() } else { "false".into() }),
2965 Value::Int(n) => Some(n.to_string()),
2966 Value::Float(f) => Some(if f.is_finite() {
2967 host::fmt_number(*f)
2968 } else {
2969 "null".into()
2970 }),
2971 Value::Str(s) => Some(json_quote(s)),
2972 Value::Obj(_) => match h.get(v) {
2973 Some(JsObj::Str(s)) => Some(json_quote(s)),
2974 Some(JsObj::Null) => Some("null".into()),
2975 Some(JsObj::Map { .. }) | Some(JsObj::Set { .. }) => Some("{}".into()),
2977 Some(JsObj::Func(_))
2979 | Some(JsObj::Builtin(_))
2980 | Some(JsObj::BoundMethod { .. })
2981 | Some(JsObj::BoundFunc { .. })
2982 | Some(JsObj::Class(_))
2983 | Some(JsObj::Symbol { .. })
2984 | Some(JsObj::Generator { .. }) => None,
2985 Some(JsObj::Array(items)) => {
2986 if items.is_empty() {
2987 return Some("[]".into());
2988 }
2989 let parts: Vec<String> = items
2990 .iter()
2991 .map(|x| {
2992 json_str(h, x, indent, depth + 1, keys).unwrap_or_else(|| "null".into())
2993 })
2994 .collect();
2995 Some(wrap(&parts, "[", "]", indent, depth))
2996 }
2997 Some(JsObj::Object(props)) => {
2998 let parts: Vec<String> = match keys {
3000 Some(allow) => allow
3001 .iter()
3002 .filter_map(|k| {
3003 props.get(k).and_then(|val| {
3004 json_str(h, val, indent, depth + 1, keys)
3005 .map(|vs| format!("{}{sep}{vs}", json_quote(k)))
3006 })
3007 })
3008 .collect(),
3009 None => props
3010 .iter()
3011 .filter(|(k, _)| !k.starts_with("@@") && !k.starts_with('#'))
3012 .filter_map(|(k, val)| {
3013 json_str(h, val, indent, depth + 1, keys)
3014 .map(|vs| format!("{}{sep}{vs}", json_quote(k)))
3015 })
3016 .collect(),
3017 };
3018 if parts.is_empty() {
3019 return Some("{}".into());
3020 }
3021 Some(wrap(&parts, "{", "}", indent, depth))
3022 }
3023 _ => Some("null".into()),
3024 },
3025 _ => Some("null".into()),
3026 }
3027}
3028
3029fn wrap(parts: &[String], open: &str, close: &str, indent: &str, depth: usize) -> String {
3030 if indent.is_empty() {
3031 format!("{open}{}{close}", parts.join(","))
3032 } else {
3033 let pad = indent.repeat(depth + 1);
3034 let pad_close = indent.repeat(depth);
3035 format!(
3036 "{open}\n{pad}{}\n{pad_close}{close}",
3037 parts.join(&format!(",\n{pad}"))
3038 )
3039 }
3040}
3041
3042fn json_quote(s: &str) -> String {
3043 let mut out = String::from("\"");
3044 for c in s.chars() {
3045 match c {
3046 '"' => out.push_str("\\\""),
3047 '\\' => out.push_str("\\\\"),
3048 '\n' => out.push_str("\\n"),
3049 '\t' => out.push_str("\\t"),
3050 '\r' => out.push_str("\\r"),
3051 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
3052 _ => out.push(c),
3053 }
3054 }
3055 out.push('"');
3056 out
3057}
3058
3059fn json_parse(args: Vec<Value>) -> Result<Value, String> {
3060 let s = with_host(|h| h.str_of(&arg0(&args)));
3061 let mut p = JsonParser {
3062 chars: s.chars().collect(),
3063 pos: 0,
3064 };
3065 p.skip_ws();
3066 let v = p.parse_value()?;
3067 p.skip_ws();
3068 if let Some(reviver) = args
3070 .get(1)
3071 .filter(|r| with_host(|h| host::is_callable(h, r)))
3072 .cloned()
3073 {
3074 return json_revive("", v, &reviver);
3075 }
3076 Ok(v)
3077}
3078
3079fn json_revive(key: &str, val: Value, reviver: &Value) -> Result<Value, String> {
3082 match with_host(|h| h.get(&val).cloned()) {
3083 Some(JsObj::Array(items)) => {
3084 for i in 0..items.len() {
3085 let elem = with_host(|h| match h.get(&val) {
3086 Some(JsObj::Array(it)) => it[i].clone(),
3087 _ => Value::Undef,
3088 });
3089 let nv = json_revive(&i.to_string(), elem, reviver)?;
3090 with_host(|h| {
3091 if let Some(JsObj::Array(it)) = h.get_mut(&val) {
3092 it[i] = nv;
3093 }
3094 });
3095 }
3096 }
3097 Some(JsObj::Object(props)) => {
3098 let keys: Vec<String> = props
3099 .keys()
3100 .filter(|k| !k.starts_with("@@"))
3101 .cloned()
3102 .collect();
3103 for k in keys {
3104 let elem = with_host(|h| match h.get(&val) {
3105 Some(JsObj::Object(p)) => p.get(&k).cloned().unwrap_or(Value::Undef),
3106 _ => Value::Undef,
3107 });
3108 let nv = json_revive(&k, elem, reviver)?;
3109 with_host(|h| {
3110 if let Some(JsObj::Object(p)) = h.get_mut(&val) {
3111 if matches!(nv, Value::Undef) {
3112 p.shift_remove(&k);
3113 } else {
3114 p.insert(k.clone(), nv);
3115 }
3116 }
3117 });
3118 }
3119 }
3120 _ => {}
3121 }
3122 let kv = with_host(|h| h.new_str(key.to_string()));
3123 host::invoke(reviver, vec![kv, val], None)
3124}
3125
3126struct JsonParser {
3127 chars: Vec<char>,
3128 pos: usize,
3129}
3130impl JsonParser {
3131 fn peek(&self) -> Option<char> {
3132 self.chars.get(self.pos).copied()
3133 }
3134 fn skip_ws(&mut self) {
3135 while matches!(
3136 self.peek(),
3137 Some(' ') | Some('\n') | Some('\t') | Some('\r')
3138 ) {
3139 self.pos += 1;
3140 }
3141 }
3142 fn parse_value(&mut self) -> Result<Value, String> {
3143 self.skip_ws();
3144 match self.peek() {
3145 Some('{') => self.parse_object(),
3146 Some('[') => self.parse_array(),
3147 Some('"') => {
3148 let s = self.parse_string()?;
3149 Ok(with_host(|h| h.new_str(s)))
3150 }
3151 Some('t') | Some('f') => self.parse_bool(),
3152 Some('n') => {
3153 self.expect_lit("null")?;
3154 Ok(with_host(|h| h.null()))
3155 }
3156 Some(c) if c == '-' || c.is_ascii_digit() => self.parse_number(),
3157 _ => Err("SyntaxError: Unexpected token in JSON".into()),
3158 }
3159 }
3160 fn expect_lit(&mut self, lit: &str) -> Result<(), String> {
3161 for ch in lit.chars() {
3162 if self.peek() != Some(ch) {
3163 return Err("SyntaxError: Unexpected token in JSON".into());
3164 }
3165 self.pos += 1;
3166 }
3167 Ok(())
3168 }
3169 fn parse_bool(&mut self) -> Result<Value, String> {
3170 if self.peek() == Some('t') {
3171 self.expect_lit("true")?;
3172 Ok(Value::Bool(true))
3173 } else {
3174 self.expect_lit("false")?;
3175 Ok(Value::Bool(false))
3176 }
3177 }
3178 fn parse_number(&mut self) -> Result<Value, String> {
3179 let start = self.pos;
3180 while matches!(self.peek(), Some(c) if c.is_ascii_digit() || c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E')
3181 {
3182 self.pos += 1;
3183 }
3184 let s: String = self.chars[start..self.pos].iter().collect();
3185 s.parse::<f64>()
3186 .map(Value::Float)
3187 .map_err(|_| "SyntaxError: bad number in JSON".into())
3188 }
3189 fn parse_string(&mut self) -> Result<String, String> {
3190 self.pos += 1; let mut out = String::new();
3192 loop {
3193 match self.peek() {
3194 None => return Err("SyntaxError: unterminated string in JSON".into()),
3195 Some('"') => {
3196 self.pos += 1;
3197 break;
3198 }
3199 Some('\\') => {
3200 self.pos += 1;
3201 match self.peek() {
3202 Some('n') => out.push('\n'),
3203 Some('t') => out.push('\t'),
3204 Some('r') => out.push('\r'),
3205 Some('"') => out.push('"'),
3206 Some('\\') => out.push('\\'),
3207 Some('/') => out.push('/'),
3208 Some('b') => out.push('\u{08}'),
3209 Some('f') => out.push('\u{0C}'),
3210 Some('u') => {
3211 let h: String = self.chars
3212 [self.pos + 1..(self.pos + 5).min(self.chars.len())]
3213 .iter()
3214 .collect();
3215 if let Ok(n) = u32::from_str_radix(&h, 16) {
3216 if let Some(ch) = char::from_u32(n) {
3217 out.push(ch);
3218 }
3219 }
3220 self.pos += 4;
3221 }
3222 _ => {}
3223 }
3224 self.pos += 1;
3225 }
3226 Some(c) => {
3227 out.push(c);
3228 self.pos += 1;
3229 }
3230 }
3231 }
3232 Ok(out)
3233 }
3234 fn parse_array(&mut self) -> Result<Value, String> {
3235 self.pos += 1; let mut items = Vec::new();
3237 self.skip_ws();
3238 if self.peek() == Some(']') {
3239 self.pos += 1;
3240 return Ok(with_host(|h| h.new_array(items)));
3241 }
3242 loop {
3243 items.push(self.parse_value()?);
3244 self.skip_ws();
3245 match self.peek() {
3246 Some(',') => {
3247 self.pos += 1;
3248 }
3249 Some(']') => {
3250 self.pos += 1;
3251 break;
3252 }
3253 _ => return Err("SyntaxError: bad array in JSON".into()),
3254 }
3255 }
3256 Ok(with_host(|h| h.new_array(items)))
3257 }
3258 fn parse_object(&mut self) -> Result<Value, String> {
3259 self.pos += 1; let mut props: IndexMap<String, Value> = IndexMap::new();
3261 self.skip_ws();
3262 if self.peek() == Some('}') {
3263 self.pos += 1;
3264 return Ok(with_host(|h| h.new_object(props)));
3265 }
3266 loop {
3267 self.skip_ws();
3268 let key = self.parse_string()?;
3269 self.skip_ws();
3270 if self.peek() != Some(':') {
3271 return Err("SyntaxError: expected ':' in JSON".into());
3272 }
3273 self.pos += 1;
3274 let val = self.parse_value()?;
3275 props.insert(key, val);
3276 self.skip_ws();
3277 match self.peek() {
3278 Some(',') => {
3279 self.pos += 1;
3280 }
3281 Some('}') => {
3282 self.pos += 1;
3283 break;
3284 }
3285 _ => return Err("SyntaxError: bad object in JSON".into()),
3286 }
3287 }
3288 Ok(with_host(|h| h.new_object(props)))
3289 }
3290}
3291
3292fn is_array_method(name: &str) -> bool {
3295 matches!(
3296 name,
3297 "push"
3298 | "pop"
3299 | "shift"
3300 | "unshift"
3301 | "map"
3302 | "filter"
3303 | "forEach"
3304 | "join"
3305 | "slice"
3306 | "indexOf"
3307 | "lastIndexOf"
3308 | "includes"
3309 | "reduce"
3310 | "concat"
3311 | "reverse"
3312 | "sort"
3313 | "find"
3314 | "findIndex"
3315 | "some"
3316 | "every"
3317 | "flat"
3318 | "fill"
3319 | "splice"
3320 | "keys"
3321 | "values"
3322 | "entries"
3323 | "flatMap"
3324 | "at"
3325 | "toString"
3326 | "reduceRight"
3327 | "findLast"
3328 | "findLastIndex"
3329 | "copyWithin"
3330 )
3331}
3332fn is_string_method(name: &str) -> bool {
3333 matches!(
3334 name,
3335 "toUpperCase"
3336 | "toLowerCase"
3337 | "charAt"
3338 | "charCodeAt"
3339 | "codePointAt"
3340 | "indexOf"
3341 | "lastIndexOf"
3342 | "includes"
3343 | "slice"
3344 | "substring"
3345 | "substr"
3346 | "split"
3347 | "trim"
3348 | "trimStart"
3349 | "trimEnd"
3350 | "replace"
3351 | "replaceAll"
3352 | "repeat"
3353 | "startsWith"
3354 | "endsWith"
3355 | "padStart"
3356 | "padEnd"
3357 | "concat"
3358 | "at"
3359 | "toString"
3360 | "valueOf"
3361 | "match"
3362 | "matchAll"
3363 | "search"
3364 | "normalize"
3365 | "localeCompare"
3366 )
3367}
3368
3369fn is_regexp_arg(v: &Value) -> bool {
3371 matches!(with_host(|h| h.get(v).cloned()), Some(JsObj::RegExp(_)))
3372}
3373
3374fn replace_str_fn(s: &str, pat: &str, repl: &Value, all: bool) -> Result<String, String> {
3377 if pat.is_empty() {
3378 return Ok(s.to_string());
3379 }
3380 let mut out = String::new();
3381 let mut rest = s;
3382 let mut base = 0usize;
3383 while let Some(pos) = rest.find(pat) {
3384 out.push_str(&rest[..pos]);
3385 let offset = base + pos;
3386 let m = with_host(|h| h.new_str(pat.to_string()));
3387 let str_arg = with_host(|h| h.new_str(s.to_string()));
3388 let r = host::invoke(repl, vec![m, Value::Float(offset as f64), str_arg], None)?;
3389 out.push_str(&with_host(|h| h.str_of(&r)));
3390 let consumed = pos + pat.len();
3391 base += consumed;
3392 rest = &rest[consumed..];
3393 if !all {
3394 break;
3395 }
3396 }
3397 out.push_str(rest);
3398 Ok(out)
3399}
3400fn is_number_method(name: &str) -> bool {
3401 matches!(
3402 name,
3403 "toFixed" | "toString" | "toPrecision" | "toLocaleString" | "valueOf"
3404 )
3405}
3406
3407pub fn call_type_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
3409 let obj = with_host(|h| h.get(recv).cloned());
3410 match obj {
3411 Some(JsObj::Array(_)) => array_method(recv, name, args),
3412 Some(JsObj::Str(s)) => string_method(&s, name, args),
3413 Some(JsObj::Map { .. }) => map_method(recv, name, args),
3414 Some(JsObj::Set { .. }) => set_method(recv, name, args),
3415 Some(JsObj::Generator { .. }) => generator_method(recv, name, args),
3416 Some(JsObj::Promise { .. }) => promise_method(recv, name, args),
3417 Some(JsObj::Iter { .. }) => iter_method(recv, name, args),
3418 Some(JsObj::Symbol { .. }) => symbol_method(recv, name, args),
3419 Some(JsObj::BigInt(b)) => bigint_method(&b, name, args),
3420 Some(JsObj::RegExp(_)) => crate::regexp::regexp_method(recv, name, args),
3421 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) | Some(JsObj::BoundFunc { .. }) => {
3422 match function_builtin_method(recv, name, &args)? {
3423 Some(v) => Ok(v),
3424 None => Err(host::type_error(&format!("{name} is not a function"))),
3425 }
3426 }
3427 Some(JsObj::Object(props)) => {
3428 if let Some(f) = props.get(name).cloned() {
3429 host::invoke(&f, args, Some(recv.clone()))
3430 } else if name == "hasOwnProperty" {
3431 let k = with_host(|h| h.str_of(&arg0(&args)));
3432 Ok(Value::Bool(props.contains_key(&k)))
3433 } else if name == "toString" {
3434 Ok(with_host(|h| h.new_str("[object Object]")))
3435 } else {
3436 Err(host::type_error(&format!("{} is not a function", name)))
3437 }
3438 }
3439 _ => {
3440 if let Value::Float(_) | Value::Int(_) = recv {
3442 return number_method(with_host(|h| h.to_number(recv)), name, args);
3443 }
3444 if let Some(s) = with_host(|h| h.as_str(recv)) {
3445 return string_method(&s, name, args);
3446 }
3447 Err(host::type_error(&format!("{} is not a function", name)))
3448 }
3449 }
3450}
3451
3452fn array_items(recv: &Value) -> Vec<Value> {
3453 with_host(|h| match h.get(recv) {
3454 Some(JsObj::Array(items)) => items.clone(),
3455 _ => Vec::new(),
3456 })
3457}
3458
3459fn array_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
3460 match name {
3461 "push" => {
3462 with_host(|h| {
3463 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
3464 items.extend(args.iter().cloned());
3465 }
3466 });
3467 Ok(Value::Float(array_items(recv).len() as f64))
3468 }
3469 "pop" => Ok(with_host(|h| {
3470 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
3471 items.pop().unwrap_or(Value::Undef)
3472 } else {
3473 Value::Undef
3474 }
3475 })),
3476 "shift" => Ok(with_host(|h| {
3477 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
3478 if items.is_empty() {
3479 Value::Undef
3480 } else {
3481 items.remove(0)
3482 }
3483 } else {
3484 Value::Undef
3485 }
3486 })),
3487 "unshift" => {
3488 with_host(|h| {
3489 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
3490 for (i, a) in args.iter().enumerate() {
3491 items.insert(i, a.clone());
3492 }
3493 }
3494 });
3495 Ok(Value::Float(array_items(recv).len() as f64))
3496 }
3497 "join" => {
3498 let sep = if args.is_empty() {
3499 ",".to_string()
3500 } else {
3501 with_host(|h| h.str_of(&args[0]))
3502 };
3503 let items = array_items(recv);
3504 let s = with_host(|h| {
3505 items
3506 .iter()
3507 .map(|x| match x {
3508 Value::Undef => String::new(),
3509 _ if h.is_null(x) => String::new(),
3510 _ => h.str_of(x),
3511 })
3512 .collect::<Vec<_>>()
3513 .join(&sep)
3514 });
3515 Ok(with_host(|h| h.new_str(s)))
3516 }
3517 "indexOf" => {
3518 let items = array_items(recv);
3519 let target = arg0(&args);
3520 let idx = with_host(|h| items.iter().position(|x| h.strict_eq(x, &target)));
3521 Ok(Value::Float(idx.map(|i| i as f64).unwrap_or(-1.0)))
3522 }
3523 "lastIndexOf" => {
3524 let items = array_items(recv);
3525 let target = arg0(&args);
3526 let idx = with_host(|h| items.iter().rposition(|x| h.strict_eq(x, &target)));
3527 Ok(Value::Float(idx.map(|i| i as f64).unwrap_or(-1.0)))
3528 }
3529 "includes" => {
3530 let items = array_items(recv);
3532 let target = arg0(&args);
3533 let tnan = matches!(target, Value::Float(f) if f.is_nan());
3534 Ok(Value::Bool(with_host(|h| {
3535 items.iter().any(|x| {
3536 (tnan && matches!(x, Value::Float(f) if f.is_nan())) || h.strict_eq(x, &target)
3537 })
3538 })))
3539 }
3540 "slice" => {
3541 let items = array_items(recv);
3542 let (lo, hi) = slice_bounds(&args, items.len());
3543 Ok(with_host(|h| h.new_array(items[lo..hi].to_vec())))
3544 }
3545 "concat" => {
3546 let mut out = array_items(recv);
3547 for a in &args {
3548 match with_host(|h| h.get(a).cloned()) {
3549 Some(JsObj::Array(items)) => out.extend(items),
3550 _ => out.push(a.clone()),
3551 }
3552 }
3553 Ok(with_host(|h| h.new_array(out)))
3554 }
3555 "reverse" => {
3556 with_host(|h| {
3557 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
3558 items.reverse();
3559 }
3560 });
3561 Ok(recv.clone())
3562 }
3563 "fill" => {
3564 let val = arg0(&args);
3566 let len = array_items(recv).len() as i64;
3567 let norm =
3568 |v: i64| -> usize { (if v < 0 { (len + v).max(0) } else { v.min(len) }) as usize };
3569 let start = if args.len() >= 2 {
3570 norm(arg_num(&args, 1) as i64)
3571 } else {
3572 0
3573 };
3574 let end = if args.len() >= 3 {
3575 norm(arg_num(&args, 2) as i64)
3576 } else {
3577 len as usize
3578 };
3579 with_host(|h| {
3580 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
3581 for it in items.iter_mut().take(end).skip(start) {
3582 *it = val.clone();
3583 }
3584 }
3585 });
3586 Ok(recv.clone())
3587 }
3588 "copyWithin" => {
3589 let items = array_items(recv);
3591 let len = items.len() as i64;
3592 let norm =
3593 |v: i64| -> usize { (if v < 0 { (len + v).max(0) } else { v.min(len) }) as usize };
3594 let target = norm(arg_num(&args, 0) as i64);
3595 let start = if args.len() >= 2 {
3596 norm(arg_num(&args, 1) as i64)
3597 } else {
3598 0
3599 };
3600 let end = if args.len() >= 3 {
3601 norm(arg_num(&args, 2) as i64)
3602 } else {
3603 len as usize
3604 };
3605 let slice: Vec<Value> = items[start..end.max(start)].to_vec();
3606 with_host(|h| {
3607 if let Some(JsObj::Array(a)) = h.get_mut(recv) {
3608 for (k, v) in slice.into_iter().enumerate() {
3609 if target + k < a.len() {
3610 a[target + k] = v;
3611 }
3612 }
3613 }
3614 });
3615 Ok(recv.clone())
3616 }
3617 "at" => {
3618 let items = array_items(recv);
3619 let mut i = arg_num(&args, 0) as i64;
3620 if i < 0 {
3621 i += items.len() as i64;
3622 }
3623 Ok(if i >= 0 && (i as usize) < items.len() {
3624 items[i as usize].clone()
3625 } else {
3626 Value::Undef
3627 })
3628 }
3629 "map" => {
3630 let items = array_items(recv);
3631 let cb = arg0(&args);
3632 let mut out = Vec::with_capacity(items.len());
3633 for (i, it) in items.iter().enumerate() {
3634 out.push(host::invoke(
3635 &cb,
3636 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3637 None,
3638 )?);
3639 }
3640 Ok(with_host(|h| h.new_array(out)))
3641 }
3642 "flatMap" => {
3643 let items = array_items(recv);
3644 let cb = arg0(&args);
3645 let mut out = Vec::new();
3646 for (i, it) in items.iter().enumerate() {
3647 let r = host::invoke(
3648 &cb,
3649 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3650 None,
3651 )?;
3652 match with_host(|h| h.get(&r).cloned()) {
3653 Some(JsObj::Array(inner)) => out.extend(inner),
3654 _ => out.push(r),
3655 }
3656 }
3657 Ok(with_host(|h| h.new_array(out)))
3658 }
3659 "filter" => {
3660 let items = array_items(recv);
3661 let cb = arg0(&args);
3662 let mut out = Vec::new();
3663 for (i, it) in items.iter().enumerate() {
3664 let keep = host::invoke(
3665 &cb,
3666 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3667 None,
3668 )?;
3669 if with_host(|h| h.truthy(&keep)) {
3670 out.push(it.clone());
3671 }
3672 }
3673 Ok(with_host(|h| h.new_array(out)))
3674 }
3675 "forEach" => {
3676 let items = array_items(recv);
3677 let cb = arg0(&args);
3678 for (i, it) in items.iter().enumerate() {
3679 host::invoke(
3680 &cb,
3681 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3682 None,
3683 )?;
3684 }
3685 Ok(Value::Undef)
3686 }
3687 "find" => {
3688 let items = array_items(recv);
3689 let cb = arg0(&args);
3690 for (i, it) in items.iter().enumerate() {
3691 let m = host::invoke(
3692 &cb,
3693 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3694 None,
3695 )?;
3696 if with_host(|h| h.truthy(&m)) {
3697 return Ok(it.clone());
3698 }
3699 }
3700 Ok(Value::Undef)
3701 }
3702 "findIndex" => {
3703 let items = array_items(recv);
3704 let cb = arg0(&args);
3705 for (i, it) in items.iter().enumerate() {
3706 let m = host::invoke(
3707 &cb,
3708 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3709 None,
3710 )?;
3711 if with_host(|h| h.truthy(&m)) {
3712 return Ok(Value::Float(i as f64));
3713 }
3714 }
3715 Ok(Value::Float(-1.0))
3716 }
3717 "some" => {
3718 let items = array_items(recv);
3719 let cb = arg0(&args);
3720 for (i, it) in items.iter().enumerate() {
3721 let m = host::invoke(
3722 &cb,
3723 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3724 None,
3725 )?;
3726 if with_host(|h| h.truthy(&m)) {
3727 return Ok(Value::Bool(true));
3728 }
3729 }
3730 Ok(Value::Bool(false))
3731 }
3732 "every" => {
3733 let items = array_items(recv);
3734 let cb = arg0(&args);
3735 for (i, it) in items.iter().enumerate() {
3736 let m = host::invoke(
3737 &cb,
3738 vec![it.clone(), Value::Float(i as f64), recv.clone()],
3739 None,
3740 )?;
3741 if !with_host(|h| h.truthy(&m)) {
3742 return Ok(Value::Bool(false));
3743 }
3744 }
3745 Ok(Value::Bool(true))
3746 }
3747 "reduce" => {
3748 let items = array_items(recv);
3749 let cb = arg0(&args);
3750 let mut acc;
3751 let mut start = 0;
3752 if args.len() >= 2 {
3753 acc = args[1].clone();
3754 } else if !items.is_empty() {
3755 acc = items[0].clone();
3756 start = 1;
3757 } else {
3758 return Err(host::type_error(
3759 "Reduce of empty array with no initial value",
3760 ));
3761 }
3762 for (i, it) in items.iter().enumerate().skip(start) {
3763 acc = host::invoke(
3764 &cb,
3765 vec![acc, it.clone(), Value::Float(i as f64), recv.clone()],
3766 None,
3767 )?;
3768 }
3769 Ok(acc)
3770 }
3771 "reduceRight" => {
3772 let items = array_items(recv);
3773 let cb = arg0(&args);
3774 let n = items.len();
3775 let mut acc;
3776 let mut i = n; if args.len() >= 2 {
3778 acc = args[1].clone();
3779 } else if n > 0 {
3780 acc = items[n - 1].clone();
3781 i = n - 1;
3782 } else {
3783 return Err(host::type_error(
3784 "Reduce of empty array with no initial value",
3785 ));
3786 }
3787 while i > 0 {
3788 i -= 1;
3789 acc = host::invoke(
3790 &cb,
3791 vec![acc, items[i].clone(), Value::Float(i as f64), recv.clone()],
3792 None,
3793 )?;
3794 }
3795 Ok(acc)
3796 }
3797 "findLast" => {
3798 let items = array_items(recv);
3799 let cb = arg0(&args);
3800 for i in (0..items.len()).rev() {
3801 let m = host::invoke(
3802 &cb,
3803 vec![items[i].clone(), Value::Float(i as f64), recv.clone()],
3804 None,
3805 )?;
3806 if with_host(|h| h.truthy(&m)) {
3807 return Ok(items[i].clone());
3808 }
3809 }
3810 Ok(Value::Undef)
3811 }
3812 "findLastIndex" => {
3813 let items = array_items(recv);
3814 let cb = arg0(&args);
3815 for i in (0..items.len()).rev() {
3816 let m = host::invoke(
3817 &cb,
3818 vec![items[i].clone(), Value::Float(i as f64), recv.clone()],
3819 None,
3820 )?;
3821 if with_host(|h| h.truthy(&m)) {
3822 return Ok(Value::Float(i as f64));
3823 }
3824 }
3825 Ok(Value::Float(-1.0))
3826 }
3827 "sort" => {
3828 let mut items = array_items(recv);
3829 sort_values(&mut items, args.first())?;
3830 with_host(|h| {
3831 if let Some(JsObj::Array(a)) = h.get_mut(recv) {
3832 *a = items;
3833 }
3834 });
3835 Ok(recv.clone())
3836 }
3837 "toSorted" => {
3839 let mut items = array_items(recv);
3840 sort_values(&mut items, args.first())?;
3841 Ok(with_host(|h| h.new_array(items)))
3842 }
3843 "toReversed" => {
3844 let mut items = array_items(recv);
3845 items.reverse();
3846 Ok(with_host(|h| h.new_array(items)))
3847 }
3848 "toSpliced" => {
3849 let mut items = array_items(recv);
3850 let len = items.len();
3851 let start = {
3852 let s = arg_num(&args, 0);
3853 if s < 0.0 {
3854 ((len as f64 + s).max(0.0)) as usize
3855 } else {
3856 (s as usize).min(len)
3857 }
3858 };
3859 let delete = if args.len() >= 2 {
3860 (arg_num(&args, 1).max(0.0) as usize).min(len - start)
3861 } else if args.is_empty() {
3862 0
3863 } else {
3864 len - start
3865 };
3866 let inserts: Vec<Value> = args.iter().skip(2).cloned().collect();
3867 items.splice(start..start + delete, inserts);
3868 Ok(with_host(|h| h.new_array(items)))
3869 }
3870 "with" => {
3871 let mut items = array_items(recv);
3872 let len = items.len() as i64;
3873 let rel = arg_num(&args, 0) as i64;
3874 let idx = if rel < 0 { len + rel } else { rel };
3875 if idx < 0 || idx >= len {
3876 return Err(host::range_error(&format!("Invalid index : {rel}")));
3877 }
3878 items[idx as usize] = args.get(1).cloned().unwrap_or(Value::Undef);
3879 Ok(with_host(|h| h.new_array(items)))
3880 }
3881 "flat" => {
3882 let raw = if args.is_empty() {
3885 1.0
3886 } else {
3887 arg_num(&args, 0)
3888 };
3889 let depth = if raw.is_nan() {
3890 0.0
3891 } else if raw.is_infinite() {
3892 raw
3893 } else {
3894 raw.trunc()
3895 };
3896 let mut out = Vec::new();
3897 flatten_into(array_items(recv), depth, &mut out);
3898 Ok(with_host(|h| h.new_array(out)))
3899 }
3900 "keys" => {
3901 let n = array_items(recv).len();
3902 let items: Vec<Value> = (0..n).map(|i| Value::Float(i as f64)).collect();
3903 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
3904 }
3905 "values" | "@@iterator" => {
3906 let items = array_items(recv);
3907 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
3908 }
3909 "entries" => {
3910 let items = array_items(recv);
3911 let pairs: Vec<Value> = items
3912 .into_iter()
3913 .enumerate()
3914 .map(|(i, v)| with_host(|h| h.new_array(vec![Value::Float(i as f64), v])))
3915 .collect();
3916 Ok(with_host(|h| {
3917 h.alloc(JsObj::Iter {
3918 items: pairs,
3919 idx: 0,
3920 })
3921 }))
3922 }
3923 "splice" => array_splice(recv, args),
3924 "toString" => {
3925 let s = with_host(|h| h.str_of(recv));
3926 Ok(with_host(|h| h.new_str(s)))
3927 }
3928 _ => Err(host::type_error(&format!("{name} is not a function"))),
3929 }
3930}
3931
3932fn sort_values(items: &mut [Value], cmp: Option<&Value>) -> Result<(), String> {
3936 for i in 1..items.len() {
3937 let mut j = i;
3938 while j > 0 {
3939 let order = match cmp {
3940 Some(cb) => {
3941 let v = host::invoke(cb, vec![items[j - 1].clone(), items[j].clone()], None)?;
3942 with_host(|h| h.to_number(&v))
3943 }
3944 None => {
3945 let a = with_host(|h| h.str_of(&items[j - 1]));
3946 let b = with_host(|h| h.str_of(&items[j]));
3947 if a > b {
3948 1.0
3949 } else {
3950 -1.0
3951 }
3952 }
3953 };
3954 if order > 0.0 {
3955 items.swap(j - 1, j);
3956 j -= 1;
3957 } else {
3958 break;
3959 }
3960 }
3961 }
3962 Ok(())
3963}
3964
3965fn flatten_into(items: Vec<Value>, depth: f64, out: &mut Vec<Value>) {
3968 for it in items {
3969 let inner = if depth > 0.0 {
3970 match with_host(|h| h.get(&it).cloned()) {
3971 Some(JsObj::Array(inner)) => Some(inner),
3972 _ => None,
3973 }
3974 } else {
3975 None
3976 };
3977 match inner {
3978 Some(inner) => flatten_into(inner, depth - 1.0, out),
3979 None => out.push(it),
3980 }
3981 }
3982}
3983
3984fn array_splice(recv: &Value, args: Vec<Value>) -> Result<Value, String> {
3985 let len = array_items(recv).len();
3986 let start = {
3987 let s = arg_num(&args, 0);
3988 if s < 0.0 {
3989 ((len as f64 + s).max(0.0)) as usize
3990 } else {
3991 (s as usize).min(len)
3992 }
3993 };
3994 let delete = if args.len() >= 2 {
3995 (arg_num(&args, 1).max(0.0) as usize).min(len - start)
3996 } else {
3997 len - start
3998 };
3999 let inserts: Vec<Value> = args.iter().skip(2).cloned().collect();
4000 let removed = with_host(|h| {
4001 if let Some(JsObj::Array(items)) = h.get_mut(recv) {
4002 let removed: Vec<Value> = items.splice(start..start + delete, inserts).collect();
4003 removed
4004 } else {
4005 Vec::new()
4006 }
4007 });
4008 Ok(with_host(|h| h.new_array(removed)))
4009}
4010
4011fn slice_bounds(args: &[Value], len: usize) -> (usize, usize) {
4012 let norm = |v: f64| -> usize {
4013 if v < 0.0 {
4014 ((len as f64 + v).max(0.0)) as usize
4015 } else {
4016 (v as usize).min(len)
4017 }
4018 };
4019 let lo = if args.is_empty() || matches!(args[0], Value::Undef) {
4020 0
4021 } else {
4022 norm(arg_num(args, 0))
4023 };
4024 let hi = if args.len() < 2 || matches!(args[1], Value::Undef) {
4025 len
4026 } else {
4027 norm(arg_num(args, 1))
4028 };
4029 (lo, hi.max(lo))
4032}
4033
4034fn string_method(s: &str, name: &str, args: Vec<Value>) -> Result<Value, String> {
4035 let chars: Vec<char> = s.chars().collect();
4036 match name {
4037 "@@iterator" => {
4038 let items: Vec<Value> = chars.iter().map(|c| new_s(c.to_string())).collect();
4039 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
4040 }
4041 "toUpperCase" => Ok(new_s(s.to_uppercase())),
4042 "toLowerCase" => Ok(new_s(s.to_lowercase())),
4043 "localeCompare" => {
4046 let other = with_host(|h| h.str_of(&arg0(&args)));
4047 let (la, lb) = (s.to_lowercase(), other.to_lowercase());
4048 let r = match la.cmp(&lb) {
4049 std::cmp::Ordering::Less => -1.0,
4050 std::cmp::Ordering::Greater => 1.0,
4051 std::cmp::Ordering::Equal => {
4052 let mut t = 0.0;
4053 for (ca, cb) in s.chars().zip(other.chars()) {
4054 if ca != cb {
4055 t = if ca.is_lowercase() { -1.0 } else { 1.0 };
4056 break;
4057 }
4058 }
4059 t
4060 }
4061 };
4062 Ok(Value::Float(r))
4063 }
4064 "normalize" => Ok(new_s(s.to_string())),
4065 "trim" => Ok(new_s(s.trim().to_string())),
4066 "trimStart" => Ok(new_s(s.trim_start().to_string())),
4067 "trimEnd" => Ok(new_s(s.trim_end().to_string())),
4068 "toString" | "valueOf" => Ok(new_s(s.to_string())),
4069 "charAt" => {
4070 let i = arg_num(&args, 0) as usize;
4071 Ok(new_s(
4072 chars.get(i).map(|c| c.to_string()).unwrap_or_default(),
4073 ))
4074 }
4075 "at" => {
4076 let mut i = arg_num(&args, 0) as i64;
4077 if i < 0 {
4078 i += chars.len() as i64;
4079 }
4080 if i >= 0 && (i as usize) < chars.len() {
4081 Ok(new_s(chars[i as usize].to_string()))
4082 } else {
4083 Ok(Value::Undef)
4084 }
4085 }
4086 "charCodeAt" | "codePointAt" => {
4087 let i = arg_num(&args, 0) as usize;
4088 match chars.get(i) {
4089 Some(c) => Ok(Value::Float(*c as u32 as f64)),
4090 None => Ok(Value::Float(f64::NAN)),
4091 }
4092 }
4093 "indexOf" => {
4094 let needle = with_host(|h| h.str_of(&arg0(&args)));
4095 Ok(Value::Float(byte_to_char_index(s, s.find(&needle))))
4096 }
4097 "lastIndexOf" => {
4098 let needle = with_host(|h| h.str_of(&arg0(&args)));
4099 Ok(Value::Float(byte_to_char_index(s, s.rfind(&needle))))
4100 }
4101 "includes" => {
4102 let needle = with_host(|h| h.str_of(&arg0(&args)));
4103 Ok(Value::Bool(s.contains(&needle)))
4104 }
4105 "startsWith" => {
4106 let needle = with_host(|h| h.str_of(&arg0(&args)));
4107 Ok(Value::Bool(s.starts_with(&needle)))
4108 }
4109 "endsWith" => {
4110 let needle = with_host(|h| h.str_of(&arg0(&args)));
4111 Ok(Value::Bool(s.ends_with(&needle)))
4112 }
4113 "slice" => {
4114 let (lo, hi) = slice_bounds(&args, chars.len());
4115 Ok(new_s(chars[lo..hi].iter().collect()))
4116 }
4117 "substring" => {
4118 let mut a = arg_num(&args, 0).max(0.0) as usize;
4119 let mut b = if args.len() < 2 || matches!(args[1], Value::Undef) {
4120 chars.len()
4121 } else {
4122 (arg_num(&args, 1).max(0.0) as usize).min(chars.len())
4123 };
4124 a = a.min(chars.len());
4125 if a > b {
4126 std::mem::swap(&mut a, &mut b);
4127 }
4128 Ok(new_s(chars[a..b].iter().collect()))
4129 }
4130 "substr" => {
4131 let len = chars.len() as i64;
4133 let mut start = arg_num(&args, 0) as i64;
4134 if start < 0 {
4135 start = (len + start).max(0);
4136 }
4137 let start = (start as usize).min(chars.len());
4138 let count = if args.len() >= 2 {
4139 arg_num(&args, 1).max(0.0) as usize
4140 } else {
4141 chars.len()
4142 };
4143 let end = (start + count).min(chars.len());
4144 Ok(new_s(chars[start..end].iter().collect()))
4145 }
4146 "repeat" => {
4147 let n = arg_num(&args, 0);
4148 if n < 0.0 || !n.is_finite() {
4149 return Err(host::type_error("Invalid count value"));
4150 }
4151 Ok(new_s(s.repeat(n as usize)))
4152 }
4153 "concat" => {
4154 let mut out = s.to_string();
4155 for a in &args {
4156 out.push_str(&with_host(|h| h.str_of(a)));
4157 }
4158 Ok(new_s(out))
4159 }
4160 "padStart" => Ok(new_s(pad(s, &args, true))),
4161 "padEnd" => Ok(new_s(pad(s, &args, false))),
4162 "match" => crate::regexp::str_match(s, &arg0(&args)),
4165 "matchAll" => crate::regexp::str_match_all(s, &arg0(&args)),
4166 "search" => {
4167 if is_regexp_arg(&arg0(&args)) {
4168 crate::regexp::str_search(s, &arg0(&args))
4169 } else {
4170 let needle = with_host(|h| h.str_of(&arg0(&args)));
4174 Ok(Value::Float(byte_to_char_index(s, s.find(&needle))))
4175 }
4176 }
4177 "replace" => {
4178 let pat = arg0(&args);
4179 let repl = args.get(1).cloned().unwrap_or(Value::Undef);
4180 if is_regexp_arg(&pat) {
4181 crate::regexp::str_replace_regex(s, &pat, &repl, false)
4182 } else if with_host(|h| host::is_callable(h, &repl)) {
4183 Ok(new_s(replace_str_fn(
4184 s,
4185 &with_host(|h| h.str_of(&pat)),
4186 &repl,
4187 false,
4188 )?))
4189 } else {
4190 let from = with_host(|h| h.str_of(&pat));
4191 let to = with_host(|h| h.str_of(&repl));
4192 Ok(new_s(s.replacen(&from, &to, 1)))
4193 }
4194 }
4195 "replaceAll" => {
4196 let pat = arg0(&args);
4197 let repl = args.get(1).cloned().unwrap_or(Value::Undef);
4198 if is_regexp_arg(&pat) {
4199 crate::regexp::str_replace_regex(s, &pat, &repl, true)
4200 } else if with_host(|h| host::is_callable(h, &repl)) {
4201 Ok(new_s(replace_str_fn(
4202 s,
4203 &with_host(|h| h.str_of(&pat)),
4204 &repl,
4205 true,
4206 )?))
4207 } else {
4208 let from = with_host(|h| h.str_of(&pat));
4209 let to = with_host(|h| h.str_of(&repl));
4210 Ok(new_s(s.replace(&from, &to)))
4211 }
4212 }
4213 "split" => {
4214 if is_regexp_arg(&arg0(&args)) {
4215 let limit = args
4216 .get(1)
4217 .filter(|v| !matches!(v, Value::Undef))
4218 .map(|v| with_host(|h| h.to_number(v)) as usize);
4219 return crate::regexp::str_split_regex(s, &arg0(&args), limit);
4220 }
4221 let mut parts: Vec<Value> = if args.is_empty() || matches!(args[0], Value::Undef) {
4222 vec![new_s(s.to_string())]
4223 } else {
4224 let sep = with_host(|h| h.str_of(&args[0]));
4225 if sep.is_empty() {
4226 chars.iter().map(|c| new_s(c.to_string())).collect()
4227 } else {
4228 s.split(&sep as &str)
4229 .map(|p| new_s(p.to_string()))
4230 .collect()
4231 }
4232 };
4233 if let Some(lim) = args.get(1).filter(|v| !matches!(v, Value::Undef)) {
4235 let n = with_host(|h| h.to_number(lim));
4236 if n.is_finite() && n >= 0.0 {
4237 parts.truncate(n as usize);
4238 }
4239 }
4240 Ok(with_host(|h| h.new_array(parts)))
4241 }
4242 _ => Err(host::type_error(&format!("{name} is not a function"))),
4243 }
4244}
4245
4246fn new_s(s: String) -> Value {
4247 with_host(|h| h.new_str(s))
4248}
4249
4250fn byte_to_char_index(s: &str, byte: Option<usize>) -> f64 {
4251 match byte {
4252 Some(b) => s[..b].chars().count() as f64,
4253 None => -1.0,
4254 }
4255}
4256
4257fn pad(s: &str, args: &[Value], start: bool) -> String {
4258 let target = arg_num(args, 0) as usize;
4259 let cur = s.chars().count();
4260 if cur >= target {
4261 return s.to_string();
4262 }
4263 let filler = if args.len() >= 2 {
4264 with_host(|h| h.str_of(&args[1]))
4265 } else {
4266 " ".to_string()
4267 };
4268 if filler.is_empty() {
4269 return s.to_string();
4270 }
4271 let need = target - cur;
4272 let fill_chars: Vec<char> = filler.chars().collect();
4273 let padding: String = (0..need)
4274 .map(|i| fill_chars[i % fill_chars.len()])
4275 .collect();
4276 if start {
4277 format!("{padding}{s}")
4278 } else {
4279 format!("{s}{padding}")
4280 }
4281}
4282
4283fn bigint_method(b: &num_bigint::BigInt, name: &str, args: Vec<Value>) -> Result<Value, String> {
4285 match name {
4286 "toString" => {
4287 let radix = args.first().map(|_| arg_num(&args, 0) as u32).unwrap_or(10);
4288 if !(2..=36).contains(&radix) {
4289 return Err("RangeError: toString() radix must be between 2 and 36".into());
4290 }
4291 Ok(new_s(b.to_str_radix(radix)))
4292 }
4293 "toLocaleString" => Ok(new_s(b.to_string())),
4294 "valueOf" => Ok(with_host(|h| h.new_bigint(b.clone()))),
4295 _ => Err(host::type_error(&format!("{name} is not a function"))),
4296 }
4297}
4298
4299fn number_method(n: f64, name: &str, args: Vec<Value>) -> Result<Value, String> {
4300 match name {
4301 "toFixed" => {
4302 let digits = arg_num(&args, 0).max(0.0) as usize;
4303 Ok(new_s(to_fixed(n, digits)))
4304 }
4305 "toString" => {
4306 let radix = args.first().map(|_| arg_num(&args, 0) as u32).unwrap_or(10);
4307 if radix == 10 || !(2..=36).contains(&radix) {
4308 Ok(new_s(host::fmt_number(n)))
4309 } else {
4310 Ok(new_s(to_radix(n, radix)))
4311 }
4312 }
4313 "toPrecision" => {
4314 if args.is_empty() {
4315 Ok(new_s(host::fmt_number(n)))
4316 } else {
4317 let p = arg_num(&args, 0) as usize;
4318 Ok(new_s(to_precision(n, p.max(1))))
4319 }
4320 }
4321 "toLocaleString" => Ok(new_s(to_locale_string(n))),
4322 "valueOf" => Ok(Value::Float(n)),
4323 _ => Err(host::type_error(&format!("{name} is not a function"))),
4324 }
4325}
4326
4327fn to_locale_string(n: f64) -> String {
4334 if n.is_nan() {
4335 return "NaN".to_string();
4336 }
4337 if n.is_infinite() {
4338 return if n < 0.0 { "-∞" } else { "∞" }.to_string();
4339 }
4340 let neg = n.is_sign_negative();
4341 let fixed = to_fixed(n.abs(), 3);
4344 let trimmed = match fixed.split_once('.') {
4345 Some(_) => fixed.trim_end_matches('0').trim_end_matches('.'),
4346 None => fixed.as_str(),
4347 };
4348 let (int_part, frac_part) = match trimmed.split_once('.') {
4349 Some((i, f)) => (i, Some(f)),
4350 None => (trimmed, None),
4351 };
4352 let mut out = String::new();
4353 if neg {
4354 out.push('-'); }
4356 out.push_str(&group_thousands(int_part));
4357 if let Some(f) = frac_part {
4358 out.push('.');
4359 out.push_str(f);
4360 }
4361 out
4362}
4363
4364fn group_thousands(int_part: &str) -> String {
4366 let bytes = int_part.as_bytes();
4367 let n = bytes.len();
4368 let mut out = String::with_capacity(n + n / 3);
4369 for (i, &b) in bytes.iter().enumerate() {
4370 if i > 0 && (n - i) % 3 == 0 {
4371 out.push(',');
4372 }
4373 out.push(b as char);
4374 }
4375 out
4376}
4377
4378fn to_fixed(n: f64, f: usize) -> String {
4387 if !n.is_finite() {
4388 return host::fmt_number(n);
4389 }
4390 if n.abs() >= 1e21 {
4392 return host::fmt_number(n);
4393 }
4394 let neg = n < 0.0;
4395 let full = format!("{:.*}", f + 25, n.abs());
4398 let mut body = round_decimal_string(&full, f);
4399 if neg {
4400 body.insert(0, '-'); }
4402 body
4403}
4404
4405fn round_decimal_string(s: &str, f: usize) -> String {
4408 let (int_part, frac_part) = s.split_once('.').unwrap_or((s, ""));
4409 let mut digits: Vec<u8> = int_part
4410 .bytes()
4411 .chain(frac_part.bytes())
4412 .map(|b| b - b'0')
4413 .collect();
4414 let point = int_part.len(); let keep = point + f; if digits.get(keep).map(|&d| d >= 5).unwrap_or(false) {
4419 let mut i = keep;
4420 loop {
4421 if i == 0 {
4422 digits.insert(0, 1);
4423 return assemble_decimal(&digits, point + 1, f);
4425 }
4426 i -= 1;
4427 if digits[i] == 9 {
4428 digits[i] = 0;
4429 } else {
4430 digits[i] += 1;
4431 break;
4432 }
4433 }
4434 }
4435 assemble_decimal(&digits, point, f)
4436}
4437
4438fn assemble_decimal(digits: &[u8], point: usize, f: usize) -> String {
4441 let int_str: String = digits[..point].iter().map(|d| (d + b'0') as char).collect();
4442 let int_str = int_str.trim_start_matches('0');
4443 let int_str = if int_str.is_empty() { "0" } else { int_str };
4444 if f == 0 {
4445 return int_str.to_string();
4446 }
4447 let frac: String = digits[point..point + f]
4448 .iter()
4449 .map(|d| (d + b'0') as char)
4450 .collect();
4451 format!("{int_str}.{frac}")
4452}
4453
4454fn to_precision(n: f64, p: usize) -> String {
4459 if !n.is_finite() {
4460 return host::fmt_number(n);
4461 }
4462 if n == 0.0 {
4463 return if p == 1 {
4464 "0".into()
4465 } else {
4466 format!("0.{}", "0".repeat(p - 1))
4467 };
4468 }
4469 let neg = n < 0.0;
4470 let a = n.abs();
4471 let sci = format!("{a:.*e}", p - 1 + 25);
4475 let (mant, exp_str) = sci.split_once('e').expect("LowerExp always has 'e'");
4476 let mut e: i32 = exp_str.parse().expect("LowerExp exponent is an integer");
4477 let all: Vec<u8> = mant
4478 .chars()
4479 .filter(|c| c.is_ascii_digit())
4480 .map(|c| c as u8 - b'0')
4481 .collect();
4482 let mut s: String = all[..p].iter().map(|d| (d + b'0') as char).collect();
4483 if all.get(p).map(|&d| d >= 5).unwrap_or(false) {
4484 let mut d: Vec<u8> = all[..p].to_vec();
4487 let mut i = p;
4488 loop {
4489 if i == 0 {
4490 d.insert(0, 1);
4491 d.truncate(p);
4492 e += 1;
4493 break;
4494 }
4495 i -= 1;
4496 if d[i] == 9 {
4497 d[i] = 0;
4498 } else {
4499 d[i] += 1;
4500 break;
4501 }
4502 }
4503 s = d.iter().map(|x| (x + b'0') as char).collect();
4504 }
4505 let pp = p as i32;
4506
4507 let body = if e < -6 || e >= pp {
4508 let sign = if e >= 0 { '+' } else { '-' };
4510 let mag = e.abs();
4511 if p == 1 {
4512 format!("{s}e{sign}{mag}")
4513 } else {
4514 format!("{}.{}e{sign}{mag}", &s[..1], &s[1..])
4515 }
4516 } else if e >= 0 {
4517 let ip = (e + 1) as usize;
4519 if ip == p {
4520 s
4521 } else {
4522 format!("{}.{}", &s[..ip], &s[ip..])
4523 }
4524 } else {
4525 format!("0.{}{}", "0".repeat((-e - 1) as usize), s)
4527 };
4528 if neg {
4529 format!("-{body}")
4530 } else {
4531 body
4532 }
4533}
4534
4535fn to_radix(n: f64, radix: u32) -> String {
4541 if !n.is_finite() {
4542 return host::fmt_number(n);
4543 }
4544 let digits = b"0123456789abcdefghijklmnopqrstuvwxyz";
4545 let rf = radix as f64;
4546 let neg = n < 0.0;
4547 let value = n.abs();
4548
4549 let mut integer = value.floor();
4550 let mut fraction = value - integer;
4551
4552 let mut frac: Vec<u8> = Vec::new();
4554 let mut delta = 0.5 * (next_up(value) - value);
4556 delta = delta.max(next_up(0.0));
4557 if fraction >= delta {
4558 loop {
4559 fraction *= rf;
4561 delta *= rf;
4562 let digit = fraction as usize;
4563 frac.push(digits[digit]);
4564 fraction -= digit as f64;
4565 if (fraction > 0.5 || (fraction == 0.5 && (digit & 1) == 1)) && fraction + delta > 1.0 {
4567 loop {
4569 match frac.pop() {
4570 None => {
4571 integer += 1.0;
4573 break;
4574 }
4575 Some(c) => {
4576 let d = if c > b'9' {
4577 (c - b'a' + 10) as u32
4578 } else {
4579 (c - b'0') as u32
4580 };
4581 if d + 1 < radix {
4582 frac.push(digits[(d + 1) as usize]);
4583 break;
4584 }
4585 }
4587 }
4588 }
4589 break;
4590 }
4591 if fraction < delta {
4592 break;
4593 }
4594 }
4595 }
4596
4597 let mut int_out: Vec<u8> = Vec::new();
4599 while v8_exponent(integer / rf) > 0 {
4601 integer /= rf;
4602 int_out.push(b'0');
4603 }
4604 loop {
4605 let remainder = integer % rf;
4606 int_out.push(digits[remainder as usize]);
4607 integer = (integer - remainder) / rf;
4608 if integer <= 0.0 {
4609 break;
4610 }
4611 }
4612 int_out.reverse();
4613
4614 let mut out: Vec<u8> = Vec::new();
4615 if neg {
4616 out.push(b'-');
4617 }
4618 out.extend_from_slice(&int_out);
4619 if !frac.is_empty() {
4620 out.push(b'.');
4621 out.extend_from_slice(&frac);
4622 }
4623 String::from_utf8(out).unwrap()
4624}
4625
4626fn next_up(x: f64) -> f64 {
4628 f64::from_bits(x.to_bits() + 1)
4629}
4630
4631fn v8_exponent(x: f64) -> i32 {
4634 let biased = ((x.to_bits() >> 52) & 0x7ff) as i32;
4635 if biased == 0 {
4636 -1074 } else {
4638 biased - 1075
4639 }
4640}
4641
4642fn map_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
4645 match name {
4646 "get" => {
4647 let key = with_host(|h| host::map_key(h, &arg0(&args)));
4648 Ok(with_host(|h| match h.get(recv) {
4649 Some(JsObj::Map { entries, .. }) => entries
4650 .get(&key)
4651 .map(|(_, v)| v.clone())
4652 .unwrap_or(Value::Undef),
4653 _ => Value::Undef,
4654 }))
4655 }
4656 "set" => {
4657 let kv = arg0(&args);
4658 let vv = args.get(1).cloned().unwrap_or(Value::Undef);
4659 let key = with_host(|h| host::map_key(h, &kv));
4660 with_host(|h| {
4661 if let Some(JsObj::Map { entries, .. }) = h.get_mut(recv) {
4662 entries.insert(key, (kv, vv));
4663 }
4664 });
4665 Ok(recv.clone())
4666 }
4667 "has" => {
4668 let key = with_host(|h| host::map_key(h, &arg0(&args)));
4669 Ok(Value::Bool(with_host(
4670 |h| matches!(h.get(recv), Some(JsObj::Map { entries, .. }) if entries.contains_key(&key)),
4671 )))
4672 }
4673 "delete" => {
4674 let key = with_host(|h| host::map_key(h, &arg0(&args)));
4675 Ok(Value::Bool(with_host(|h| match h.get_mut(recv) {
4676 Some(JsObj::Map { entries, .. }) => entries.shift_remove(&key).is_some(),
4677 _ => false,
4678 })))
4679 }
4680 "clear" => {
4681 with_host(|h| {
4682 if let Some(JsObj::Map { entries, .. }) = h.get_mut(recv) {
4683 entries.clear();
4684 }
4685 });
4686 Ok(Value::Undef)
4687 }
4688 "forEach" => {
4689 let cb = arg0(&args);
4690 let pairs: Vec<(Value, Value)> = with_host(|h| match h.get(recv) {
4691 Some(JsObj::Map { entries, .. }) => entries.values().cloned().collect(),
4692 _ => Vec::new(),
4693 });
4694 for (k, v) in pairs {
4695 host::invoke(&cb, vec![v, k, recv.clone()], None)?;
4696 }
4697 Ok(Value::Undef)
4698 }
4699 "keys" | "values" | "entries" | "@@iterator" => {
4700 let items: Vec<Value> = with_host(|h| {
4701 let pairs: Vec<(Value, Value)> = match h.get(recv) {
4702 Some(JsObj::Map { entries, .. }) => entries.values().cloned().collect(),
4703 _ => Vec::new(),
4704 };
4705 pairs
4706 .into_iter()
4707 .map(|(k, v)| match name {
4708 "keys" => k,
4709 "values" => v,
4710 _ => h.new_array(vec![k, v]), })
4712 .collect()
4713 });
4714 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
4715 }
4716 _ => Err(host::type_error(&format!("map.{name} is not a function"))),
4717 }
4718}
4719
4720fn set_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
4721 match name {
4722 "add" => {
4723 let vv = arg0(&args);
4724 let key = with_host(|h| host::map_key(h, &vv));
4725 with_host(|h| {
4726 if let Some(JsObj::Set { entries, .. }) = h.get_mut(recv) {
4727 entries.insert(key, vv);
4728 }
4729 });
4730 Ok(recv.clone())
4731 }
4732 "has" => {
4733 let key = with_host(|h| host::map_key(h, &arg0(&args)));
4734 Ok(Value::Bool(with_host(
4735 |h| matches!(h.get(recv), Some(JsObj::Set { entries, .. }) if entries.contains_key(&key)),
4736 )))
4737 }
4738 "delete" => {
4739 let key = with_host(|h| host::map_key(h, &arg0(&args)));
4740 Ok(Value::Bool(with_host(|h| match h.get_mut(recv) {
4741 Some(JsObj::Set { entries, .. }) => entries.shift_remove(&key).is_some(),
4742 _ => false,
4743 })))
4744 }
4745 "clear" => {
4746 with_host(|h| {
4747 if let Some(JsObj::Set { entries, .. }) = h.get_mut(recv) {
4748 entries.clear();
4749 }
4750 });
4751 Ok(Value::Undef)
4752 }
4753 "forEach" => {
4754 let cb = arg0(&args);
4755 let vals: Vec<Value> = with_host(|h| match h.get(recv) {
4756 Some(JsObj::Set { entries, .. }) => entries.values().cloned().collect(),
4757 _ => Vec::new(),
4758 });
4759 for v in vals {
4760 host::invoke(&cb, vec![v.clone(), v, recv.clone()], None)?;
4761 }
4762 Ok(Value::Undef)
4763 }
4764 "keys" | "values" | "entries" | "@@iterator" => {
4765 let items: Vec<Value> = with_host(|h| {
4766 let vals: Vec<Value> = match h.get(recv) {
4767 Some(JsObj::Set { entries, .. }) => entries.values().cloned().collect(),
4768 _ => Vec::new(),
4769 };
4770 if name == "entries" {
4771 vals.into_iter()
4772 .map(|v| h.new_array(vec![v.clone(), v]))
4773 .collect()
4774 } else {
4775 vals
4776 }
4777 });
4778 Ok(with_host(|h| h.alloc(JsObj::Iter { items, idx: 0 })))
4779 }
4780 _ => Err(host::type_error(&format!("set.{name} is not a function"))),
4781 }
4782}
4783
4784fn generator_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
4785 match name {
4786 "next" => {
4787 let send = arg0(&args);
4788 match host::gen_resume(recv, send)? {
4789 host::GenStep::Yield(v) => Ok(iter_result(v, false)),
4790 host::GenStep::Done(v) => Ok(iter_result(v, true)),
4791 }
4792 }
4793 "return" => {
4794 match host::gen_return(recv, arg0(&args))? {
4797 host::GenStep::Yield(v) => Ok(iter_result(v, false)),
4798 host::GenStep::Done(v) => Ok(iter_result(v, true)),
4799 }
4800 }
4801 "throw" => {
4802 match host::gen_throw(recv, arg0(&args))? {
4806 host::GenStep::Yield(v) => Ok(iter_result(v, false)),
4807 host::GenStep::Done(v) => Ok(iter_result(v, true)),
4808 }
4809 }
4810 _ => Err(host::type_error(&format!(
4811 "generator.{name} is not a function"
4812 ))),
4813 }
4814}
4815
4816fn iter_result(value: Value, done: bool) -> Value {
4818 with_host(|h| {
4819 let mut m: IndexMap<String, Value> = IndexMap::new();
4820 m.insert("value".into(), value);
4821 m.insert("done".into(), Value::Bool(done));
4822 h.new_object(m)
4823 })
4824}
4825
4826fn iter_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
4829 match name {
4830 "next" => {
4831 let step = with_host(|h| {
4832 if let Some(JsObj::Iter { items, idx }) = h.get_mut(recv) {
4833 if *idx < items.len() {
4834 let v = items[*idx].clone();
4835 *idx += 1;
4836 return Some(v);
4837 }
4838 }
4839 None
4840 });
4841 Ok(match step {
4842 Some(v) => iter_result(v, false),
4843 None => iter_result(Value::Undef, true),
4844 })
4845 }
4846 "return" => {
4847 with_host(|h| {
4849 if let Some(JsObj::Iter { items, idx }) = h.get_mut(recv) {
4850 *idx = items.len();
4851 }
4852 });
4853 Ok(iter_result(arg0(&args), true))
4854 }
4855 "@@iterator" => Ok(recv.clone()),
4857 _ => Err(host::type_error(&format!(
4858 "iterator.{name} is not a function"
4859 ))),
4860 }
4861}
4862
4863fn symbol_method(recv: &Value, name: &str, _args: Vec<Value>) -> Result<Value, String> {
4864 match name {
4865 "toString" => Ok(with_host(|h| {
4866 let s = h.str_of(recv);
4867 h.new_str(s)
4868 })),
4869 _ => Err(host::type_error(&format!(
4870 "symbol.{name} is not a function"
4871 ))),
4872 }
4873}
4874
4875fn object_create(args: Vec<Value>) -> Result<Value, String> {
4878 let proto = arg0(&args);
4879 let obj = with_host(|h| h.new_object(IndexMap::new()));
4880 if !matches!(proto, Value::Undef) {
4883 with_host(|h| h.set_proto(&obj, proto));
4884 }
4885 if let Some(descs) = args.get(1).filter(|d| !matches!(d, Value::Undef)) {
4887 let entries: Vec<(String, Value)> = with_host(|h| match h.get(descs) {
4888 Some(JsObj::Object(p)) => p.iter().map(|(k, v)| (k.clone(), v.clone())).collect(),
4889 _ => Vec::new(),
4890 });
4891 for (k, d) in entries {
4892 apply_descriptor(&obj, &k, &d);
4893 }
4894 }
4895 Ok(obj)
4896}
4897
4898fn builtin_proto_method_names(ns: &str) -> Option<&'static [&'static str]> {
4902 match ns {
4903 "EventEmitter.prototype" => Some(crate::stdlib::events::METHODS),
4904 _ => None,
4905 }
4906}
4907
4908fn object_define_property(args: Vec<Value>) -> Result<Value, String> {
4909 let obj = arg0(&args);
4910 let key = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
4911 let desc = args.get(2).cloned().unwrap_or(Value::Undef);
4912 apply_descriptor(&obj, &key, &desc);
4913 Ok(obj)
4914}
4915
4916fn apply_descriptor(obj: &Value, key: &str, desc: &Value) {
4918 let (value, get, set) = with_host(|h| match h.get(desc) {
4919 Some(JsObj::Object(p)) => (
4920 p.get("value").cloned(),
4921 p.get("get").cloned(),
4922 p.get("set").cloned(),
4923 ),
4924 _ => (None, None, None),
4925 });
4926 if get.is_some() || set.is_some() {
4927 with_host(|h| h.set_accessor(obj, key, get, set));
4928 } else if let Some(v) = value {
4929 if matches!(
4932 with_host(|h| h.get(obj).cloned()),
4933 Some(JsObj::Func(_)) | Some(JsObj::Class(_))
4934 ) {
4935 with_host(|h| h.set_fn_prop(obj, key, v));
4936 } else {
4937 with_host(|h| {
4938 if let Some(JsObj::Object(p)) = h.get_mut(obj) {
4939 p.insert(key.to_string(), v);
4940 host::canonicalize_own_keys(p);
4941 }
4942 });
4943 }
4944 }
4945}
4946
4947fn object_get_own_descriptor(args: Vec<Value>) -> Result<Value, String> {
4948 let obj = arg0(&args);
4949 let key = with_host(|h| h.property_key(&args.get(1).cloned().unwrap_or(Value::Undef)));
4950 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(&obj).cloned()) {
4953 if let Some(names) = builtin_proto_method_names(&ns) {
4954 if names.contains(&key.as_str()) {
4955 return Ok(with_host(|h| {
4956 let thunk = h.alloc(JsObj::Builtin(format!(
4957 "@proto:{}:{key}",
4958 ns.trim_end_matches(".prototype")
4959 )));
4960 let mut m: IndexMap<String, Value> = IndexMap::new();
4961 m.insert("value".into(), thunk);
4962 m.insert("writable".into(), Value::Bool(true));
4963 m.insert("enumerable".into(), Value::Bool(true));
4964 m.insert("configurable".into(), Value::Bool(true));
4965 h.new_object(m)
4966 }));
4967 }
4968 }
4969 }
4970 if let Some((get, set)) = with_host(|h| h.own_accessor(&obj, &key)) {
4972 return Ok(with_host(|h| {
4973 let mut m: IndexMap<String, Value> = IndexMap::new();
4974 m.insert("get".into(), get.unwrap_or(Value::Undef));
4975 m.insert("set".into(), set.unwrap_or(Value::Undef));
4976 m.insert("enumerable".into(), Value::Bool(true));
4977 m.insert("configurable".into(), Value::Bool(true));
4978 h.new_object(m)
4979 }));
4980 }
4981 let val = with_host(|h| match h.get(&obj) {
4982 Some(JsObj::Object(p)) => p.get(&key).cloned(),
4983 Some(JsObj::Func(_)) | Some(JsObj::Class(_)) => h.fn_prop(&obj, &key),
4985 _ => None,
4986 });
4987 match val {
4988 Some(v) => Ok(with_host(|h| {
4989 let mut m: IndexMap<String, Value> = IndexMap::new();
4990 m.insert("value".into(), v);
4991 m.insert("writable".into(), Value::Bool(true));
4992 m.insert("enumerable".into(), Value::Bool(true));
4993 m.insert("configurable".into(), Value::Bool(true));
4994 h.new_object(m)
4995 })),
4996 None => Ok(Value::Undef),
4997 }
4998}
4999
5000pub fn has_property(obj: &Value, key: &str) -> bool {
5002 if let Some(JsObj::Builtin(ns)) = with_host(|h| h.get(obj).cloned()) {
5008 return !matches!(namespace_property(&ns, key), Value::Undef);
5009 }
5010 if with_host(|h| host::lookup_chain(h, obj, key)).is_some() {
5011 return true;
5012 }
5013 if with_host(|h| host::lookup_accessor(h, obj, key)).is_some() {
5014 return true;
5015 }
5016 with_host(|h| match h.get(obj) {
5017 Some(JsObj::Object(p)) => p.contains_key(key),
5018 Some(JsObj::Array(items)) => {
5019 key == "length"
5020 || key
5021 .parse::<usize>()
5022 .map(|i| i < items.len())
5023 .unwrap_or(false)
5024 }
5025 _ => false,
5026 })
5027}
5028
5029fn deep_clone(v: &Value) -> Value {
5031 match with_host(|h| h.get(v).cloned()) {
5032 Some(JsObj::Array(items)) => {
5033 let cloned: Vec<Value> = items.iter().map(deep_clone).collect();
5034 with_host(|h| h.new_array(cloned))
5035 }
5036 Some(JsObj::Object(props)) => {
5037 let cloned: IndexMap<String, Value> = props
5038 .iter()
5039 .map(|(k, val)| (k.clone(), deep_clone(val)))
5040 .collect();
5041 with_host(|h| h.new_object(cloned))
5042 }
5043 _ => v.clone(),
5044 }
5045}
5046
5047pub fn error_string(h: &host::JsHost, v: &Value) -> String {
5052 if let Some(JsObj::Object(props)) = h.get(v) {
5053 let name = props
5054 .get("name")
5055 .map(|x| h.str_of(x))
5056 .or_else(|| host::lookup_chain(h, v, "name").map(|x| h.str_of(&x)))
5057 .unwrap_or_else(|| "Error".into());
5058 if let Some(m) = props.get("message") {
5059 return format!("{name}: {}", h.str_of(m));
5060 }
5061 return name;
5062 }
5063 h.str_of(v)
5064}
5065
5066fn make_builtin(name: String) -> Value {
5067 with_host(|h| h.alloc(JsObj::Builtin(name)))
5068}
5069
5070fn new_promise(executor: Value) -> Result<Value, String> {
5073 let p = with_host(|h| h.new_promise());
5074 let id = with_host(|h| h.promise_id(&p).unwrap());
5075 let res = make_builtin(format!("@@presolve:{id}"));
5076 let rej = make_builtin(format!("@@preject:{id}"));
5077 if let Err(e) = host::invoke(&executor, vec![res, rej], None) {
5078 let ev = host::take_exc_or_error(&e);
5080 host::reject_promise_val(id, ev);
5081 }
5082 Ok(p)
5083}
5084
5085fn promise_resolve(v: Value) -> Result<Value, String> {
5086 Ok(host::promise_of(&v))
5087}
5088fn promise_reject(v: Value) -> Result<Value, String> {
5089 let p = with_host(|h| h.new_promise());
5090 let id = with_host(|h| h.promise_id(&p).unwrap());
5091 host::reject_promise_val(id, v);
5092 with_host(|h| h.promise_mark_handled(id)); Ok(p)
5094}
5095
5096fn promise_with_resolvers() -> Result<Value, String> {
5100 let p = with_host(|h| h.new_promise());
5101 let id = with_host(|h| h.promise_id(&p).unwrap());
5102 let resolve = make_builtin(format!("@@presolve:{id}"));
5103 let reject = make_builtin(format!("@@preject:{id}"));
5104 let mut props: IndexMap<String, Value> = IndexMap::new();
5105 props.insert("promise".into(), p);
5106 props.insert("resolve".into(), resolve);
5107 props.insert("reject".into(), reject);
5108 Ok(with_host(|h| h.new_object(props)))
5109}
5110
5111#[derive(Clone, Copy)]
5112enum AllMode {
5113 All,
5114 AllSettled,
5115}
5116
5117fn promise_all(args: Vec<Value>, mode: AllMode) -> Result<Value, String> {
5119 let items = host::iter_all(&arg0(&args))?;
5120 let result = with_host(|h| h.new_promise());
5121 let rid = with_host(|h| h.promise_id(&result).unwrap());
5122 let n = items.len();
5123 if n == 0 {
5124 let empty = with_host(|h| h.new_array(Vec::new()));
5125 host::resolve_promise_val(rid, empty);
5126 return Ok(result);
5127 }
5128 let slots = std::rc::Rc::new(std::cell::RefCell::new(vec![Value::Undef; n]));
5130 let remaining = std::rc::Rc::new(std::cell::RefCell::new(n));
5131 for (i, it) in items.into_iter().enumerate() {
5132 let ap = host::promise_of(&it);
5133 let aid = with_host(|h| h.promise_id(&ap).unwrap());
5134 let slots = slots.clone();
5135 let remaining = remaining.clone();
5136 host::subscribe_native(
5137 aid,
5138 Box::new(move |state, val| {
5139 let settled = match mode {
5140 AllMode::All => {
5141 if state == host::PromiseState::Rejected {
5142 host::reject_promise_val(rid, val);
5143 return Ok(());
5144 }
5145 val
5146 }
5147 AllMode::AllSettled => with_host(|h| {
5148 let mut m: IndexMap<String, Value> = IndexMap::new();
5149 if state == host::PromiseState::Rejected {
5150 m.insert("status".into(), h.new_str("rejected"));
5151 m.insert("reason".into(), val);
5152 } else {
5153 m.insert("status".into(), h.new_str("fulfilled"));
5154 m.insert("value".into(), val);
5155 }
5156 h.new_object(m)
5157 }),
5158 };
5159 slots.borrow_mut()[i] = settled;
5160 let mut r = remaining.borrow_mut();
5161 *r -= 1;
5162 if *r == 0 {
5163 let arr = with_host(|h| h.new_array(slots.borrow().clone()));
5164 host::resolve_promise_val(rid, arr);
5165 }
5166 Ok(())
5167 }),
5168 );
5169 }
5170 Ok(result)
5171}
5172
5173fn promise_race(args: Vec<Value>, any: bool) -> Result<Value, String> {
5175 let items = host::iter_all(&arg0(&args))?;
5176 let result = with_host(|h| h.new_promise());
5177 let rid = with_host(|h| h.promise_id(&result).unwrap());
5178 let n = items.len();
5179 let errors = std::rc::Rc::new(std::cell::RefCell::new(vec![Value::Undef; n]));
5180 let remaining = std::rc::Rc::new(std::cell::RefCell::new(n));
5181 for (i, it) in items.into_iter().enumerate() {
5182 let ap = host::promise_of(&it);
5183 let aid = with_host(|h| h.promise_id(&ap).unwrap());
5184 let errors = errors.clone();
5185 let remaining = remaining.clone();
5186 host::subscribe_native(
5187 aid,
5188 Box::new(move |state, val| {
5189 if any {
5190 if state == host::PromiseState::Fulfilled {
5191 host::resolve_promise_val(rid, val);
5192 } else {
5193 errors.borrow_mut()[i] = val;
5194 let mut r = remaining.borrow_mut();
5195 *r -= 1;
5196 if *r == 0 {
5197 let agg = with_host(|h| {
5199 synth_error(h, "AggregateError: All promises were rejected")
5200 });
5201 host::reject_promise_val(rid, agg);
5202 }
5203 }
5204 } else if state == host::PromiseState::Rejected {
5205 host::reject_promise_val(rid, val);
5206 } else {
5207 host::resolve_promise_val(rid, val);
5208 }
5209 Ok(())
5210 }),
5211 );
5212 }
5213 Ok(result)
5214}
5215
5216fn promise_method(recv: &Value, name: &str, args: Vec<Value>) -> Result<Value, String> {
5218 match name {
5219 "then" => Ok(host::promise_then(
5220 recv,
5221 args.first().cloned().unwrap_or(Value::Undef),
5222 args.get(1).cloned().unwrap_or(Value::Undef),
5223 )),
5224 "catch" => Ok(host::promise_then(
5225 recv,
5226 Value::Undef,
5227 args.first().cloned().unwrap_or(Value::Undef),
5228 )),
5229 "finally" => {
5230 let cb = arg0(&args);
5231 let i = match cb {
5232 Value::Obj(i) => i,
5233 _ => 0,
5234 };
5235 let pass = make_builtin(format!("@@finpass:{i}"));
5236 let throw = make_builtin(format!("@@finthrow:{i}"));
5237 Ok(host::promise_then(recv, pass, throw))
5238 }
5239 _ => Err(host::type_error(&format!(
5240 "promise.{name} is not a function"
5241 ))),
5242 }
5243}
5244
5245fn enqueue_microtask(next_tick: bool, cb: Value, args: Vec<Value>) {
5246 with_host(|h| {
5247 if next_tick {
5248 h.queue_nexttick(cb, args);
5249 } else {
5250 h.queue_micro(cb, args);
5251 }
5252 });
5253}
5254
5255fn schedule_timer(name: &str, args: Vec<Value>) -> Value {
5259 let cb = arg0(&args);
5260 let delay = if name == "setImmediate" {
5261 -1.0 } else {
5263 args.get(1)
5264 .map(|d| with_host(|h| h.to_number(d)))
5265 .unwrap_or(0.0)
5266 .max(0.0)
5267 };
5268 let extra = if name == "setImmediate" {
5269 args.get(1..).map(|s| s.to_vec()).unwrap_or_default()
5270 } else {
5271 args.get(2..).map(|s| s.to_vec()).unwrap_or_default()
5272 };
5273 let id = with_host(|h| h.add_timer(delay, cb, extra));
5274 Value::Float(id as f64)
5275}
5276
5277fn clear_timer(v: &Value) {
5278 let id = with_host(|h| h.to_number(v)) as u64;
5279 with_host(|h| h.cancel_timer(id));
5280}