Skip to main content

pipa/builtins/
promise.rs

1use crate::host::HostFunction;
2use crate::object::function::JSFunction;
3use crate::object::object::JSObject;
4use crate::runtime::context::JSContext;
5use crate::value::JSValue;
6use std::collections::VecDeque;
7
8const PROMISE_STATE_SLOT: &str = "__promise_state__";
9const PROMISE_RESULT_SLOT: &str = "__promise_result__";
10const PROMISE_REACTIONS_SLOT: &str = "__promise_reactions__";
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum PromiseState {
14    Pending,
15    Fulfilled,
16    Rejected,
17}
18
19impl PromiseState {
20    pub fn is_pending(&self) -> bool {
21        matches!(self, PromiseState::Pending)
22    }
23}
24
25#[derive(Clone)]
26pub struct Reaction {
27    pub handler: JSValue,
28    pub is_reject: bool,
29    pub is_all_settled: bool,
30    pub is_finally: bool,
31
32    pub target_promise: JSValue,
33}
34
35pub struct MicrotaskQueue {
36    queue: VecDeque<Microtask>,
37}
38
39pub enum Microtask {
40    Reaction(Reaction, JSValue),
41
42    UserCallback(JSValue, Vec<JSValue>),
43}
44
45impl MicrotaskQueue {
46    pub fn new() -> Self {
47        MicrotaskQueue {
48            queue: VecDeque::new(),
49        }
50    }
51
52    pub fn enqueue(&mut self, task: Microtask) {
53        self.queue.push_back(task);
54    }
55
56    pub fn is_empty(&self) -> bool {
57        self.queue.is_empty()
58    }
59
60    pub fn dequeue(&mut self) -> Option<Microtask> {
61        self.queue.pop_front()
62    }
63}
64
65impl Default for MicrotaskQueue {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71fn create_builtin_function(ctx: &mut JSContext, name: &str) -> JSValue {
72    create_builtin_function_arity(ctx, name, 1)
73}
74
75fn create_builtin_function_arity(ctx: &mut JSContext, name: &str, arity: u32) -> JSValue {
76    let mut func = JSFunction::new_builtin(ctx.intern(name), arity);
77    func.set_builtin_marker(ctx, name);
78    let ptr = Box::into_raw(Box::new(func)) as usize;
79    ctx.runtime_mut().gc_heap_mut().track_function(ptr);
80    JSValue::new_function(ptr)
81}
82
83fn intern_str(ctx: &mut JSContext, s: &str) -> crate::runtime::atom::Atom {
84    ctx.intern(s)
85}
86
87fn get_array_element(obj: &JSObject, index: usize, ctx: &mut JSContext) -> JSValue {
88    if obj.is_dense_array() {
89        let ptr = obj as *const JSObject as *const crate::object::array_obj::JSArrayObject;
90        let arr = unsafe { &*ptr };
91        if index < arr.elements.len() {
92            return arr.elements[index];
93        }
94        return JSValue::undefined();
95    }
96    if let Some(val) = obj.get_indexed(index) {
97        return val;
98    }
99    let idx_atom = ctx.intern(&index.to_string());
100    obj.get(idx_atom).unwrap_or(JSValue::undefined())
101}
102
103fn get_array_length(obj: &JSObject, ctx: &mut JSContext) -> usize {
104    if obj.is_dense_array() {
105        let ptr = obj as *const JSObject as *const crate::object::array_obj::JSArrayObject;
106        let arr = unsafe { &*ptr };
107        return arr.elements.len();
108    }
109    let length_atom = intern_str(ctx, "length");
110    let len_val = obj.get(length_atom).unwrap_or(JSValue::new_int(0));
111    if len_val.is_int() {
112        len_val.get_int().max(0) as usize
113    } else {
114        0
115    }
116}
117
118fn get_promise_state(ctx: &mut JSContext, obj: &JSObject) -> PromiseState {
119    let state_atom = intern_str(ctx, PROMISE_STATE_SLOT);
120    if let Some(state_val) = obj.get(state_atom) {
121        let val = state_val.get_int();
122        match val {
123            1 => PromiseState::Fulfilled,
124            2 => PromiseState::Rejected,
125            _ => PromiseState::Pending,
126        }
127    } else {
128        PromiseState::Pending
129    }
130}
131
132fn set_promise_state(ctx: &mut JSContext, obj: &mut JSObject, state: PromiseState) {
133    let state_val = match state {
134        PromiseState::Pending => JSValue::new_int(0),
135        PromiseState::Fulfilled => JSValue::new_int(1),
136        PromiseState::Rejected => JSValue::new_int(2),
137    };
138    let state_atom = intern_str(ctx, PROMISE_STATE_SLOT);
139    obj.set(state_atom, state_val);
140}
141
142fn get_promise_result(ctx: &mut JSContext, obj: &JSObject) -> JSValue {
143    let result_atom = intern_str(ctx, PROMISE_RESULT_SLOT);
144    obj.get(result_atom).unwrap_or(JSValue::undefined())
145}
146
147fn set_promise_result(ctx: &mut JSContext, obj: &mut JSObject, value: JSValue) {
148    let result_atom = intern_str(ctx, PROMISE_RESULT_SLOT);
149    obj.set(result_atom, value);
150}
151
152fn get_promise_reactions(ctx: &mut JSContext, obj: &JSObject) -> Vec<Reaction> {
153    let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
154    if let Some(reactions_val) = obj.get(reactions_atom) {
155        if reactions_val.is_object() {
156            let ptr = reactions_val.get_ptr();
157
158            if ptr == 0 {
159                return Vec::new();
160            }
161            let arr = reactions_val.as_object();
162            let len_atom = intern_str(ctx, "length");
163            let len = arr.get(len_atom).map(|v| v.get_int() as usize).unwrap_or(0);
164            let mut reactions = Vec::new();
165            for i in 0..len {
166                let idx_atom = intern_str(ctx, &i.to_string());
167                if let Some(r_val) = arr.get(idx_atom) {
168                    if r_val.is_object() {
169                        let r_obj = r_val.as_object();
170                        let handler_atom = intern_str(ctx, "handler");
171                        let is_reject_atom = intern_str(ctx, "isReject");
172                        let all_settled_atom = intern_str(ctx, "allSettled");
173                        let finally_atom = intern_str(ctx, "isFinally");
174                        let target_atom = intern_str(ctx, "target");
175                        if let Some(handler) = r_obj.get(handler_atom) {
176                            let is_reject = r_obj
177                                .get(is_reject_atom)
178                                .map(|v| v.get_bool())
179                                .unwrap_or(false);
180                            let is_all_settled = r_obj
181                                .get(all_settled_atom)
182                                .map(|v| v.get_bool())
183                                .unwrap_or(false);
184                            let is_finally = r_obj
185                                .get(finally_atom)
186                                .map(|v| v.get_bool())
187                                .unwrap_or(false);
188                            let target_promise = r_obj
189                                .get(target_atom)
190                                .unwrap_or(JSValue::undefined());
191                            reactions.push(Reaction {
192                                handler,
193                                is_reject,
194                                is_all_settled,
195                                is_finally,
196                                target_promise,
197                            });
198                        }
199                    }
200                }
201            }
202            return reactions;
203        }
204    }
205    Vec::new()
206}
207
208fn create_reaction_array(ctx: &mut JSContext, reactions: Vec<Reaction>) -> JSValue {
209    let mut arr = JSObject::new_array();
210    let len_atom = intern_str(ctx, "length");
211    arr.set(len_atom, JSValue::new_int(reactions.len() as i64));
212    for (i, reaction) in reactions.into_iter().enumerate() {
213        let mut r_obj = JSObject::new();
214        let handler_atom = intern_str(ctx, "handler");
215        let is_reject_atom = intern_str(ctx, "isReject");
216        let all_settled_atom = intern_str(ctx, "allSettled");
217        let finally_atom = intern_str(ctx, "isFinally");
218        let target_atom = intern_str(ctx, "target");
219        r_obj.set(handler_atom, reaction.handler);
220        r_obj.set(is_reject_atom, JSValue::bool(reaction.is_reject));
221        r_obj.set(all_settled_atom, JSValue::bool(reaction.is_all_settled));
222        r_obj.set(finally_atom, JSValue::bool(reaction.is_finally));
223        r_obj.set(target_atom, reaction.target_promise);
224        let r_ptr = Box::into_raw(Box::new(r_obj)) as usize;
225        ctx.runtime_mut().gc_heap_mut().track(r_ptr);
226        let idx_atom = intern_str(ctx, &i.to_string());
227        arr.set(idx_atom, JSValue::new_object(r_ptr));
228    }
229    let ptr = Box::into_raw(Box::new(arr)) as usize;
230    ctx.runtime_mut().gc_heap_mut().track(ptr);
231    JSValue::new_object(ptr)
232}
233
234pub fn is_promise(value: &JSValue) -> bool {
235    if !value.is_object() {
236        return false;
237    }
238    let obj = value.as_object();
239    obj.is_promise()
240}
241
242fn create_promise(ctx: &mut JSContext) -> JSObject {
243    let mut promise = JSObject::new_promise();
244
245    if let Some(proto_ptr) = ctx.get_promise_prototype() {
246        promise.prototype = Some(proto_ptr);
247    }
248    let state_atom = intern_str(ctx, PROMISE_STATE_SLOT);
249    let result_atom = intern_str(ctx, PROMISE_RESULT_SLOT);
250    let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
251    promise.set(state_atom, JSValue::new_int(0));
252    promise.set(result_atom, JSValue::undefined());
253    promise.set(reactions_atom, JSValue::null());
254    promise
255}
256
257fn fulfill_promise(ctx: &mut JSContext, promise: &mut JSObject, value: JSValue) {
258    set_promise_state(ctx, promise, PromiseState::Fulfilled);
259    set_promise_result(ctx, promise, value);
260    process_reactions(ctx, promise, PromiseState::Fulfilled);
261}
262
263fn reject_promise(ctx: &mut JSContext, promise: &mut JSObject, reason: JSValue) {
264    set_promise_state(ctx, promise, PromiseState::Rejected);
265    set_promise_result(ctx, promise, reason);
266    process_reactions(ctx, promise, PromiseState::Rejected);
267}
268
269fn process_reactions(ctx: &mut JSContext, promise: &mut JSObject, state: PromiseState) {
270    let reactions = get_promise_reactions(ctx, promise);
271    for reaction in reactions {
272        let argument = get_promise_result(ctx, promise);
273        let microtask = if reaction.is_reject && state == PromiseState::Rejected {
274            Microtask::Reaction(reaction, argument)
275        } else if !reaction.is_reject && state == PromiseState::Fulfilled {
276            Microtask::Reaction(reaction, argument)
277        } else {
278            continue;
279        };
280        ctx.microtask_enqueue(microtask);
281    }
282}
283
284fn promise_resolve(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
285    let value = if args.len() >= 2 {
286        args[1]
287    } else if !args.is_empty() {
288        args[0]
289    } else {
290        JSValue::undefined()
291    };
292    if value.is_object() {
293        let obj = value.as_object();
294        if obj.is_promise() {
295            return value;
296        }
297    }
298    let mut promise = create_promise(ctx);
299    fulfill_promise(ctx, &mut promise, value);
300    let ptr = Box::into_raw(Box::new(promise)) as usize;
301    JSValue::new_object(ptr)
302}
303
304fn promise_reject(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
305    let reason = if args.len() >= 2 {
306        args[1]
307    } else if !args.is_empty() {
308        args[0]
309    } else {
310        JSValue::undefined()
311    };
312    let mut promise = create_promise(ctx);
313    reject_promise(ctx, &mut promise, reason);
314    let ptr = Box::into_raw(Box::new(promise)) as usize;
315    JSValue::new_object(ptr)
316}
317
318fn promise_then(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
319    let this_val = args.get(0).copied().unwrap_or(JSValue::undefined());
320    if !this_val.is_object() {
321        let mut err = JSObject::new();
322        err.set(ctx.common_atoms.name, JSValue::new_string(ctx.intern("TypeError")));
323        err.set(ctx.common_atoms.message, JSValue::new_string(ctx.intern("Promise.prototype.then called on incompatible receiver")));
324        if let Some(proto) = ctx.get_type_error_prototype() {
325            err.prototype = Some(proto);
326        }
327        let ptr = Box::into_raw(Box::new(err)) as usize;
328        ctx.runtime_mut().gc_heap_mut().track(ptr);
329        ctx.pending_exception = Some(JSValue::new_object(ptr));
330        return JSValue::undefined();
331    }
332    let promise = this_val.as_object_mut();
333    if !promise.is_promise() {
334        return JSValue::undefined();
335    }
336    let on_fulfilled = args.get(1).copied().unwrap_or(JSValue::undefined());
337    let on_rejected = args.get(2).copied().unwrap_or(JSValue::undefined());
338    let new_promise = create_promise(ctx);
339    let ptr_new = Box::into_raw(Box::new(new_promise)) as usize;
340    let target = JSValue::new_object(ptr_new);
341    let state = get_promise_state(ctx, promise);
342    let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
343    match state {
344        PromiseState::Fulfilled => {
345            let result = get_promise_result(ctx, promise);
346            let reaction = Reaction {
347                handler: on_fulfilled,
348                is_reject: false,
349                is_all_settled: false, is_finally: false,
350                target_promise: target,
351            };
352            ctx.microtask_enqueue(Microtask::Reaction(reaction, result));
353        }
354        PromiseState::Rejected => {
355            let reason = get_promise_result(ctx, promise);
356            let reaction = Reaction {
357                handler: on_rejected,
358                is_reject: true,
359                is_all_settled: false, is_finally: false,
360                target_promise: target,
361            };
362            ctx.microtask_enqueue(Microtask::Reaction(reaction, reason));
363        }
364        PromiseState::Pending => {
365            let mut reactions = get_promise_reactions(ctx, promise);
366            reactions.push(Reaction {
367                handler: on_fulfilled,
368                is_reject: false,
369                is_all_settled: false, is_finally: false,
370                target_promise: target,
371            });
372            reactions.push(Reaction {
373                handler: on_rejected,
374                is_reject: true,
375                is_all_settled: false, is_finally: false,
376                target_promise: target,
377            });
378            let reactions_arr = create_reaction_array(ctx, reactions);
379            promise.set(reactions_atom, reactions_arr);
380        }
381    }
382    target
383}
384
385fn promise_catch(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
386    let this_val = args.get(0).copied().unwrap_or(JSValue::undefined());
387    let on_rejected = args.get(1).copied().unwrap_or(JSValue::undefined());
388
389    let then_atom = ctx.intern("then");
390    let then_fn = if this_val.is_object() {
391        this_val.as_object().get(then_atom)
392    } else {
393        None
394    };
395
396    if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
397        let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
398        match then_fn {
399            Some(f) if f.is_function() => {
400                let result = vm.call_function_with_this(
401                    ctx,
402                    f,
403                    this_val,
404                    &[JSValue::undefined(), on_rejected],
405                );
406                match result {
407                    Ok(v) => v,
408                    Err(_) => {
409                        if let Some(exc) = vm.last_caught_exception.take() {
410                            vm.pending_throw = Some(exc);
411                        }
412                        JSValue::undefined()
413                    }
414                }
415            }
416            _ => {
417                let mut err = JSObject::new();
418                err.set(ctx.common_atoms.name, JSValue::new_string(ctx.intern("TypeError")));
419                err.set(ctx.common_atoms.message, JSValue::new_string(ctx.intern("Promise.prototype.catch called on non-Promise")));
420                if let Some(proto) = ctx.get_type_error_prototype() {
421                    err.prototype = Some(proto);
422                }
423                let ptr = Box::into_raw(Box::new(err)) as usize;
424                ctx.runtime_mut().gc_heap_mut().track(ptr);
425                ctx.pending_exception = Some(JSValue::new_object(ptr));
426                JSValue::undefined()
427            }
428        }
429    } else {
430        JSValue::undefined()
431    }
432}
433
434fn promise_finally(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
435    let this_val = args.get(0).copied().unwrap_or(JSValue::undefined());
436    let on_finally = args.get(1).copied().unwrap_or(JSValue::undefined());
437
438    let then_atom = ctx.intern("then");
439    let then_fn = if this_val.is_object() {
440        this_val.as_object().get(then_atom)
441    } else {
442        None
443    };
444
445    if let Some(vm_ptr) = ctx.get_register_vm_ptr() {
446        let vm = unsafe { &mut *(vm_ptr as *mut crate::runtime::vm::VM) };
447        match then_fn {
448            Some(f) if f.is_function() => {
449                let result = vm.call_function_with_this(
450                    ctx,
451                    f,
452                    this_val,
453                    &[on_finally, on_finally],
454                );
455                match result {
456                    Ok(v) => v,
457                    Err(_) => {
458                        if let Some(exc) = vm.last_caught_exception.take() {
459                            vm.pending_throw = Some(exc);
460                        }
461                        JSValue::undefined()
462                    }
463                }
464            }
465            _ => {
466                let mut err = JSObject::new();
467                err.set(ctx.common_atoms.name, JSValue::new_string(ctx.intern("TypeError")));
468                err.set(ctx.common_atoms.message, JSValue::new_string(ctx.intern("Promise.prototype.finally called on non-Promise")));
469                if let Some(proto) = ctx.get_type_error_prototype() {
470                    err.prototype = Some(proto);
471                }
472                let ptr = Box::into_raw(Box::new(err)) as usize;
473                ctx.runtime_mut().gc_heap_mut().track(ptr);
474                ctx.pending_exception = Some(JSValue::new_object(ptr));
475                JSValue::undefined()
476            }
477        }
478    } else {
479        JSValue::undefined()
480    }
481}
482
483fn promise_all(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
484    let iterable = args.get(0).copied().unwrap_or(JSValue::undefined());
485
486    let result_promise = create_promise(ctx);
487    let result_promise_ptr = Box::into_raw(Box::new(result_promise)) as usize;
488    let result_promise_val = JSValue::new_object(result_promise_ptr);
489    ctx.runtime_mut().gc_heap_mut().track(result_promise_ptr);
490
491    let rp = result_promise_val.as_object_mut();
492
493    if !iterable.is_object() {
494        let msg = JSValue::new_string(ctx.intern("TypeError: object is not iterable (cannot read property Symbol.iterator)"));
495        reject_promise(ctx, rp, msg);
496        return result_promise_val;
497    }
498
499    let obj = iterable.as_object();
500
501    let sym_iter = crate::builtins::symbol::get_symbol_iterator(ctx);
502    if sym_iter.is_symbol() {
503        let sym_atom = crate::runtime::atom::Atom(0x40000000 | sym_iter.get_symbol_id());
504        let iter_fn = obj.get(sym_atom).or_else(|| {
505            let mut current = obj.prototype;
506            while let Some(p) = current {
507                let pobj = unsafe { &*p };
508                if let Some(v) = pobj.get(sym_atom) {
509                    return Some(v);
510                }
511                current = pobj.prototype;
512            }
513            None
514        });
515        match iter_fn {
516            Some(f) if f.is_function() => {}
517            _ => {
518                let msg = JSValue::new_string(ctx.intern("TypeError: object is not iterable (cannot read property Symbol.iterator)"));
519                reject_promise(ctx, rp, msg);
520                return result_promise_val;
521            }
522        }
523    }
524
525    let len = get_array_length(&obj, ctx);
526
527    if len == 0 {
528        let mut empty_arr = JSObject::new_array();
529        let length_atom = intern_str(ctx, "length");
530        empty_arr.set(length_atom, JSValue::new_int(0));
531        let arr_ptr = Box::into_raw(Box::new(empty_arr)) as usize;
532        ctx.runtime_mut().gc_heap_mut().track(arr_ptr);
533        let rp = result_promise_val.as_object_mut();
534        fulfill_promise(ctx, rp, JSValue::new_object(arr_ptr));
535        return result_promise_val;
536    }
537
538    let mut results_arr = JSObject::new_array();
539    let length_atom = intern_str(ctx, "length");
540    results_arr.set(length_atom, JSValue::new_int(len as i64));
541    let results_ptr = Box::into_raw(Box::new(results_arr)) as usize;
542    ctx.runtime_mut().gc_heap_mut().track(results_ptr);
543    let results_val = JSValue::new_object(results_ptr);
544
545    let remaining_atom = ctx.intern("__all_remaining__");
546    let results_slot_atom = ctx.intern("__all_results__");
547    let rp = result_promise_val.as_object_mut();
548    rp.set(remaining_atom, JSValue::new_int(len as i64));
549    rp.set(results_slot_atom, results_val);
550
551    for i in 0..len {
552        let item = get_array_element(&obj, i, ctx);
553
554        let resolved = resolve_value_as_promise(ctx, item);
555        let promise = resolved.as_object_mut();
556        let state = get_promise_state(ctx, promise);
557        match state {
558            PromiseState::Fulfilled => {
559                let value = get_promise_result(ctx, promise);
560                let reaction = Reaction {
561                    handler: JSValue::new_int(i as i64),
562                    is_reject: false,
563                    is_all_settled: false, is_finally: false,
564                    target_promise: result_promise_val,
565                };
566                ctx.microtask_enqueue(Microtask::Reaction(reaction, value));
567            }
568            PromiseState::Rejected => {
569                let reason = get_promise_result(ctx, promise);
570                let rp = result_promise_val.as_object_mut();
571                reject_promise(ctx, rp, reason);
572                return result_promise_val;
573            }
574            PromiseState::Pending => {
575                let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
576                let mut reactions = get_promise_reactions(ctx, promise);
577                reactions.push(Reaction {
578                    handler: JSValue::new_int(i as i64),
579                    is_reject: false,
580                    is_all_settled: false, is_finally: false,
581                    target_promise: result_promise_val,
582                });
583                reactions.push(Reaction {
584                    handler: JSValue::new_int(i as i64),
585                    is_reject: true,
586                    is_all_settled: false, is_finally: false,
587                    target_promise: result_promise_val,
588                });
589                let reactions_arr = create_reaction_array(ctx, reactions);
590                promise.set(reactions_atom, reactions_arr);
591            }
592        }
593    }
594
595    result_promise_val
596}
597
598fn promise_race(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
599    let iterable = args.get(0).copied().unwrap_or(JSValue::undefined());
600
601    let result_promise = create_promise(ctx);
602    let result_promise_ptr = Box::into_raw(Box::new(result_promise)) as usize;
603    let result_promise_val = JSValue::new_object(result_promise_ptr);
604    ctx.runtime_mut().gc_heap_mut().track(result_promise_ptr);
605
606    if !iterable.is_object() {
607        let rp = result_promise_val.as_object_mut();
608        let msg = JSValue::new_string(ctx.intern("TypeError: object is not iterable (cannot read property Symbol.iterator)"));
609        reject_promise(ctx, rp, msg);
610        return result_promise_val;
611    }
612
613    let obj = iterable.as_object();
614    let len = get_array_length(&obj, ctx);
615
616    for i in 0..len {
617        let item = get_array_element(&obj, i, ctx);
618        let resolved = resolve_value_as_promise(ctx, item);
619        let promise = resolved.as_object_mut();
620        let state = get_promise_state(ctx, promise);
621
622        match state {
623            PromiseState::Fulfilled => {
624                let value = get_promise_result(ctx, promise);
625                let rp = result_promise_val.as_object_mut();
626                fulfill_promise(ctx, rp, value);
627                return result_promise_val;
628            }
629            PromiseState::Rejected => {
630                let reason = get_promise_result(ctx, promise);
631                let rp = result_promise_val.as_object_mut();
632                reject_promise(ctx, rp, reason);
633                return result_promise_val;
634            }
635            PromiseState::Pending => {
636                let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
637                let mut reactions = get_promise_reactions(ctx, promise);
638                reactions.push(Reaction {
639                    handler: JSValue::new_int(-1),
640                    is_reject: false,
641                    is_all_settled: false, is_finally: false,
642                    target_promise: result_promise_val,
643                });
644                reactions.push(Reaction {
645                    handler: JSValue::new_int(-1),
646                    is_reject: true,
647                    is_all_settled: false, is_finally: false,
648                    target_promise: result_promise_val,
649                });
650                let reactions_arr = create_reaction_array(ctx, reactions);
651                promise.set(reactions_atom, reactions_arr);
652            }
653        }
654    }
655
656    result_promise_val
657}
658
659fn promise_all_settled(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
660    let iterable = args.get(0).copied().unwrap_or(JSValue::undefined());
661
662    let result_promise = create_promise(ctx);
663    let result_promise_ptr = Box::into_raw(Box::new(result_promise)) as usize;
664    let result_promise_val = JSValue::new_object(result_promise_ptr);
665
666    if !iterable.is_object() {
667        let rp = result_promise_val.as_object_mut();
668        let msg = JSValue::new_string(ctx.intern("TypeError: object is not iterable (cannot read property Symbol.iterator)"));
669        reject_promise(ctx, rp, msg);
670        return result_promise_val;
671    }
672
673    let obj = iterable.as_object();
674
675    let len = get_array_length(&obj, ctx);
676
677    if len == 0 {
678        let mut empty_arr = JSObject::new_array();
679        let length_atom = intern_str(ctx, "length");
680        empty_arr.set(length_atom, JSValue::new_int(0));
681        let arr_ptr = Box::into_raw(Box::new(empty_arr)) as usize;
682        let rp = result_promise_val.as_object_mut();
683        fulfill_promise(ctx, rp, JSValue::new_object(arr_ptr));
684        ctx.runtime_mut().gc_heap_mut().track(arr_ptr);
685        return result_promise_val;
686    }
687
688    let mut results_arr = JSObject::new_array();
689    let length_atom = intern_str(ctx, "length");
690    results_arr.set(length_atom, JSValue::new_int(len as i64));
691    let results_ptr = Box::into_raw(Box::new(results_arr)) as usize;
692    ctx.runtime_mut().gc_heap_mut().track(results_ptr);
693    let results_val = JSValue::new_object(results_ptr);
694
695    let remaining_atom = ctx.intern("__allSettled_remaining__");
696    let results_slot_atom = ctx.intern("__allSettled_results__");
697    let rp = result_promise_val.as_object_mut();
698    rp.set(remaining_atom, JSValue::new_int(len as i64));
699    rp.set(results_slot_atom, results_val);
700
701    for i in 0..len {
702        let item = get_array_element(&obj, i, ctx);
703
704        let resolved = resolve_value_as_promise(ctx, item);
705
706        let promise = resolved.as_object_mut();
707        let state = get_promise_state(ctx, promise);
708        match state {
709            PromiseState::Fulfilled => {
710                let value = get_promise_result(ctx, promise);
711                let reaction = Reaction {
712                    handler: JSValue::new_int(i as i64),
713                    is_reject: false,
714                    is_all_settled: true, is_finally: false,
715                    target_promise: result_promise_val,
716                };
717                ctx.microtask_enqueue(Microtask::Reaction(reaction, value));
718            }
719            PromiseState::Rejected => {
720                let reason = get_promise_result(ctx, promise);
721                let reaction = Reaction {
722                    handler: JSValue::new_int(i as i64),
723                    is_reject: true,
724                    is_all_settled: true, is_finally: false,
725                    target_promise: result_promise_val,
726                };
727                ctx.microtask_enqueue(Microtask::Reaction(reaction, reason));
728            }
729            PromiseState::Pending => {
730                let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
731                let mut reactions = get_promise_reactions(ctx, promise);
732                reactions.push(Reaction {
733                    handler: JSValue::new_int(i as i64),
734                    is_reject: false,
735                    is_all_settled: true, is_finally: false,
736                    target_promise: result_promise_val,
737                });
738                reactions.push(Reaction {
739                    handler: JSValue::new_int(i as i64),
740                    is_reject: true,
741                    is_all_settled: true, is_finally: false,
742                    target_promise: result_promise_val,
743                });
744                let reactions_arr = create_reaction_array(ctx, reactions);
745                promise.set(reactions_atom, reactions_arr);
746            }
747        }
748    }
749
750    result_promise_val
751}
752
753fn promise_any(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
754    let iterable = args.get(0).copied().unwrap_or(JSValue::undefined());
755
756    let result_promise = create_promise(ctx);
757    let result_promise_ptr = Box::into_raw(Box::new(result_promise)) as usize;
758    let result_promise_val = JSValue::new_object(result_promise_ptr);
759
760    if !iterable.is_object() {
761        let msg = JSValue::new_string(ctx.intern("TypeError: object is not iterable (cannot read property Symbol.iterator)"));
762        let rp = result_promise_val.as_object_mut();
763        reject_promise(ctx, rp, msg);
764        return result_promise_val;
765    }
766
767    let obj = iterable.as_object();
768
769    let len = get_array_length(&obj, ctx);
770
771    if len == 0 {
772        let rp = result_promise_val.as_object_mut();
773        let mut err = JSObject::new();
774        err.set(intern_str(ctx, "name"), JSValue::new_string(ctx.intern("AggregateError")));
775        err.set(intern_str(ctx, "message"), JSValue::new_string(ctx.intern("All promises were rejected")));
776        if let Some(proto) = ctx.get_error_prototype() {
777            err.prototype = Some(proto);
778        }
779        let err_ptr = Box::into_raw(Box::new(err)) as usize;
780        ctx.runtime_mut().gc_heap_mut().track(err_ptr);
781        reject_promise(ctx, rp, JSValue::new_object(err_ptr));
782        return result_promise_val;
783    }
784
785    let remaining_atom = ctx.intern("__any_remaining__");
786    let rp = result_promise_val.as_object_mut();
787    rp.set(remaining_atom, JSValue::new_int(len as i64));
788
789    for i in 0..len {
790        let item = get_array_element(&obj, i, ctx);
791
792        let resolved = resolve_value_as_promise(ctx, item);
793        let promise = resolved.as_object_mut();
794        let state = get_promise_state(ctx, promise);
795
796        match state {
797            PromiseState::Fulfilled => {
798                let value = get_promise_result(ctx, promise);
799                let rp = result_promise_val.as_object_mut();
800                fulfill_promise(ctx, rp, value);
801                return result_promise_val;
802            }
803            PromiseState::Rejected | PromiseState::Pending => {
804                if state == PromiseState::Pending {
805                    let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
806                    let mut reactions = get_promise_reactions(ctx, promise);
807                    reactions.push(Reaction {
808                        handler: JSValue::new_int(i as i64),
809                        is_reject: false,
810                        is_all_settled: false, is_finally: false,
811                        target_promise: result_promise_val,
812                    });
813                    reactions.push(Reaction {
814                        handler: JSValue::new_int(i as i64),
815                        is_reject: true,
816                        is_all_settled: false, is_finally: false,
817                        target_promise: result_promise_val,
818                    });
819                    let reactions_arr = create_reaction_array(ctx, reactions);
820                    promise.set(reactions_atom, reactions_arr);
821                } else {
822                    let reason = get_promise_result(ctx, promise);
823                    let reaction = Reaction {
824                        handler: JSValue::new_int(i as i64),
825                        is_reject: true,
826                        is_all_settled: false, is_finally: false,
827                        target_promise: result_promise_val,
828                    };
829                    ctx.microtask_enqueue(Microtask::Reaction(reaction, reason));
830                }
831            }
832        }
833    }
834
835    result_promise_val
836}
837
838fn promise_with_resolvers(ctx: &mut JSContext, _args: &[JSValue]) -> JSValue {
839    let mut promise = create_promise(ctx);
840    let ptr = Box::into_raw(Box::new(promise)) as usize;
841    ctx.runtime_mut().gc_heap_mut().track(ptr);
842    let promise_val = JSValue::new_object(ptr);
843
844    let (resolve_val, reject_val) = create_resolve_reject_fns(ctx, promise_val);
845
846    let mut result = JSObject::new();
847    let promise_atom = ctx.intern("promise");
848    let resolve_atom = ctx.intern("resolve");
849    let reject_atom = ctx.intern("reject");
850    result.set(promise_atom, promise_val);
851    result.set(resolve_atom, resolve_val);
852    result.set(reject_atom, reject_val);
853    let result_ptr = Box::into_raw(Box::new(result)) as usize;
854    ctx.runtime_mut().gc_heap_mut().track(result_ptr);
855    JSValue::new_object(result_ptr)
856}
857
858fn resolve_value_as_promise(ctx: &mut JSContext, value: JSValue) -> JSValue {
859    if value.is_object() {
860        let obj = value.as_object();
861        if obj.is_promise() {
862            return value;
863        }
864    }
865    let mut promise = create_promise(ctx);
866    fulfill_promise(ctx, &mut promise, value);
867    let ptr = Box::into_raw(Box::new(promise)) as usize;
868    ctx.runtime_mut().gc_heap_mut().track(ptr);
869    JSValue::new_object(ptr)
870}
871
872fn promise_internal_resolve(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
873    let callee_fn = args.get(0).copied().unwrap_or(JSValue::undefined());
874    let value = args.get(1).copied().unwrap_or(JSValue::undefined());
875    if !callee_fn.is_function() {
876        return JSValue::undefined();
877    }
878    let target_atom = ctx.intern("__target_promise__");
879    let func_obj = callee_fn.as_function();
880    let promise_val = func_obj.base.get(target_atom).unwrap_or(JSValue::undefined());
881    if !promise_val.is_object() {
882        return JSValue::undefined();
883    }
884    let promise = promise_val.as_object_mut();
885    if !promise.is_promise() || !get_promise_state(ctx, promise).is_pending() {
886        return JSValue::undefined();
887    }
888    if value.is_object() {
889        let val_obj = value.as_object();
890        if val_obj.is_promise() {
891            let state = get_promise_state(ctx, val_obj);
892            let result_val = get_promise_result(ctx, val_obj);
893            match state {
894                PromiseState::Fulfilled => fulfill_promise(ctx, promise, result_val),
895                PromiseState::Rejected => reject_promise(ctx, promise, result_val),
896                PromiseState::Pending => {
897                    let already_atom = ctx.intern("__already_resolved__");
898                    promise.set(already_atom, JSValue::bool(true));
899                    let reactions_atom = intern_str(ctx, PROMISE_REACTIONS_SLOT);
900                    let mut reactions = get_promise_reactions(ctx, val_obj);
901                    let target = promise_val;
902                    reactions.push(Reaction {
903                        handler: JSValue::undefined(),
904                        is_reject: false,
905                        is_all_settled: false, is_finally: false,
906                        target_promise: target,
907                    });
908                    reactions.push(Reaction {
909                        handler: JSValue::undefined(),
910                        is_reject: true,
911                        is_all_settled: false, is_finally: false,
912                        target_promise: target,
913                    });
914                    let reactions_arr = create_reaction_array(ctx, reactions);
915                    let val_mut = value.as_object_mut();
916                    val_mut.set(reactions_atom, reactions_arr);
917                    return JSValue::undefined();
918                }
919            }
920            return JSValue::undefined();
921        }
922    }
923    fulfill_promise(ctx, promise, value);
924    JSValue::undefined()
925}
926
927fn promise_internal_reject(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
928    let callee_fn = args.get(0).copied().unwrap_or(JSValue::undefined());
929    let reason = args.get(1).copied().unwrap_or(JSValue::undefined());
930    if !callee_fn.is_function() {
931        return JSValue::undefined();
932    }
933    let target_atom = ctx.intern("__target_promise__");
934    let func_obj = callee_fn.as_function();
935    let promise_val = func_obj.base.get(target_atom).unwrap_or(JSValue::undefined());
936    if !promise_val.is_object() {
937        return JSValue::undefined();
938    }
939    let promise = promise_val.as_object_mut();
940    if promise.is_promise() && get_promise_state(ctx, promise).is_pending() {
941        reject_promise(ctx, promise, reason);
942    }
943    JSValue::undefined()
944}
945
946fn create_resolve_reject_fns(ctx: &mut JSContext, promise_val: JSValue) -> (JSValue, JSValue) {
947    let empty_name = ctx.intern("");
948    let mut resolve_fn = crate::object::function::JSFunction::new_builtin(empty_name, 1);
949    resolve_fn.set_builtin_marker(ctx, "promise_internal_resolve");
950    resolve_fn.name = empty_name;
951    let name_desc = crate::object::object::PropertyDescriptor {
952        value: Some(JSValue::new_string(empty_name)),
953        writable: false,
954        enumerable: false,
955        configurable: true,
956        get: None,
957        set: None,
958    };
959    resolve_fn.base.define_property(ctx.common_atoms.name, name_desc);
960    let resolve_target_atom = ctx.intern("__target_promise__");
961    resolve_fn.base.set(resolve_target_atom, promise_val);
962    let resolve_ptr = Box::into_raw(Box::new(resolve_fn)) as usize;
963    ctx.runtime_mut().gc_heap_mut().track_function(resolve_ptr);
964    let resolve_val = JSValue::new_function(resolve_ptr);
965
966    let mut reject_fn = crate::object::function::JSFunction::new_builtin(empty_name, 1);
967    reject_fn.set_builtin_marker(ctx, "promise_internal_reject");
968    reject_fn.name = empty_name;
969    let name_desc = crate::object::object::PropertyDescriptor {
970        value: Some(JSValue::new_string(empty_name)),
971        writable: false,
972        enumerable: false,
973        configurable: true,
974        get: None,
975        set: None,
976    };
977    reject_fn.base.define_property(ctx.common_atoms.name, name_desc);
978    let reject_target_atom = ctx.intern("__target_promise__");
979    reject_fn.base.set(reject_target_atom, promise_val);
980    let reject_ptr = Box::into_raw(Box::new(reject_fn)) as usize;
981    ctx.runtime_mut().gc_heap_mut().track_function(reject_ptr);
982    let reject_val = JSValue::new_function(reject_ptr);
983
984    (resolve_val, reject_val)
985}
986
987fn promise_executor(ctx: &mut JSContext, args: &[JSValue]) -> JSValue {
988    let executor_fn = args.get(0).copied().unwrap_or(JSValue::undefined());
989    if !executor_fn.is_function() {
990        let mut promise = create_promise(ctx);
991        let ptr = Box::into_raw(Box::new(promise)) as usize;
992        ctx.runtime_mut().gc_heap_mut().track(ptr);
993        let promise_val = JSValue::new_object(ptr);
994        let rp = promise_val.as_object_mut();
995        let msg = JSValue::new_string(ctx.intern("TypeError: Promise resolver undefined is not a function"));
996        reject_promise(ctx, rp, msg);
997        return promise_val;
998    }
999
1000    let mut promise = create_promise(ctx);
1001    let ptr = Box::into_raw(Box::new(promise)) as usize;
1002    ctx.runtime_mut().gc_heap_mut().track(ptr);
1003    let promise_val = JSValue::new_object(ptr);
1004
1005    let (resolve_val, reject_val) = create_resolve_reject_fns(ctx, promise_val);
1006
1007    if let Err(e) = call_callback(ctx, executor_fn, &[resolve_val, reject_val]) {
1008        let rp = promise_val.as_object_mut();
1009        let msg = JSValue::new_string(ctx.intern(&e));
1010        reject_promise(ctx, rp, msg);
1011    }
1012
1013    promise_val
1014}
1015
1016fn call_callback(
1017    ctx: &mut JSContext,
1018    callback: JSValue,
1019    args: &[JSValue],
1020) -> Result<JSValue, String> {
1021    if let Some(ptr) = ctx.get_register_vm_ptr() {
1022        let vm = unsafe { &mut *(ptr as *mut crate::runtime::vm::VM) };
1023        vm.call_function(ctx, callback, args)
1024    } else {
1025        Err("VM not available".to_string())
1026    }
1027}
1028
1029fn create_promise_prototype(ctx: &mut JSContext) -> JSValue {
1030    let mut proto = JSObject::new_promise();
1031    let constructor_atom = intern_str(ctx, "constructor");
1032    let then_atom = intern_str(ctx, "then");
1033    let catch_atom = intern_str(ctx, "catch");
1034    let finally_atom = intern_str(ctx, "finally");
1035    proto.set(constructor_atom, JSValue::null());
1036
1037    use crate::builtins::global::set_non_enumerable;
1038    set_non_enumerable(&mut proto, then_atom, create_builtin_function_arity(ctx, "promise_then", 2));
1039    set_non_enumerable(&mut proto, catch_atom, create_builtin_function(ctx, "promise_catch"));
1040    set_non_enumerable(&mut proto, finally_atom, create_builtin_function(ctx, "promise_finally"));
1041
1042    let to_string_tag = crate::builtins::symbol::get_symbol_to_string_tag(ctx);
1043    if to_string_tag.is_symbol() {
1044        let sym_atom = crate::runtime::atom::Atom(0x40000000 | to_string_tag.get_symbol_id());
1045        set_non_enumerable(&mut proto, sym_atom, JSValue::new_string(ctx.intern("Promise")));
1046    }
1047
1048    if let Some(obj_proto_ptr) = ctx.get_object_prototype() {
1049        proto.prototype = Some(obj_proto_ptr);
1050    }
1051    let ptr = Box::into_raw(Box::new(proto)) as usize;
1052    ctx.runtime_mut().gc_heap_mut().track(ptr);
1053    JSValue::new_object(ptr)
1054}
1055
1056pub fn init_promise(ctx: &mut JSContext) {
1057    let promise_atom = ctx.intern("Promise");
1058    let mut promise_func = crate::object::function::JSFunction::new_builtin(promise_atom, 1);
1059    promise_func.set_builtin_marker(ctx, "promise_executor");
1060    let proto_value = create_promise_prototype(ctx);
1061
1062    let proto_ptr = proto_value.get_ptr();
1063    ctx.set_promise_prototype(proto_ptr);
1064    use crate::builtins::global::set_non_enumerable;
1065    set_non_enumerable(&mut promise_func.base, ctx.intern("prototype"), proto_value);
1066    set_non_enumerable(&mut promise_func.base, ctx.intern("resolve"), create_builtin_function(ctx, "promise_resolve"));
1067    set_non_enumerable(&mut promise_func.base, ctx.intern("reject"), create_builtin_function(ctx, "promise_reject"));
1068    set_non_enumerable(&mut promise_func.base, ctx.intern("all"), create_builtin_function(ctx, "promise_all"));
1069    set_non_enumerable(&mut promise_func.base, ctx.intern("race"), create_builtin_function(ctx, "promise_race"));
1070    set_non_enumerable(&mut promise_func.base, ctx.intern("allSettled"), create_builtin_function(ctx, "promise_allSettled"));
1071    set_non_enumerable(&mut promise_func.base, ctx.intern("any"), create_builtin_function(ctx, "promise_any"));
1072    set_non_enumerable(&mut promise_func.base, ctx.intern("withResolvers"), create_builtin_function(ctx, "promise_with_resolvers"));
1073    let promise_ptr = Box::into_raw(Box::new(promise_func)) as usize;
1074    ctx.runtime_mut().gc_heap_mut().track_function(promise_ptr);
1075    let promise_value = JSValue::new_function(promise_ptr);
1076
1077    crate::builtins::symbol::install_species_accessor(ctx, &promise_value);
1078
1079    let proto_mut = unsafe { &mut *(proto_ptr as *mut JSObject) };
1080    proto_mut.set(ctx.intern("constructor"), promise_value);
1081
1082    let global = ctx.global();
1083    if global.is_object() {
1084        let global_obj = global.as_object_mut();
1085        crate::builtins::global::set_non_enumerable(global_obj, promise_atom, promise_value);
1086    }
1087}
1088
1089pub fn create_resolved_promise(ctx: &mut JSContext, value: JSValue) -> JSValue {
1090    let mut promise = create_promise(ctx);
1091    fulfill_promise(ctx, &mut promise, value);
1092    let ptr = Box::into_raw(Box::new(promise)) as usize;
1093    let result = JSValue::new_object(ptr);
1094    result
1095}
1096
1097pub fn create_rejected_promise(ctx: &mut JSContext, reason: JSValue) -> JSValue {
1098    let mut promise = create_promise(ctx);
1099    reject_promise(ctx, &mut promise, reason);
1100    let ptr = Box::into_raw(Box::new(promise)) as usize;
1101    JSValue::new_object(ptr)
1102}
1103
1104#[cfg(any(feature = "process", feature = "fetch"))]
1105pub(crate) fn fulfill_promise_with_value(
1106    ctx: &mut JSContext,
1107    promise_obj_ptr: usize,
1108    value: JSValue,
1109) {
1110    let promise = unsafe { &mut *(promise_obj_ptr as *mut JSObject) };
1111    fulfill_promise(ctx, promise, value);
1112}
1113
1114#[cfg(any(feature = "process", feature = "fetch"))]
1115pub(crate) fn reject_promise_with_value(
1116    ctx: &mut JSContext,
1117    promise_obj_ptr: usize,
1118    reason: JSValue,
1119) {
1120    let promise = unsafe { &mut *(promise_obj_ptr as *mut JSObject) };
1121    reject_promise(ctx, promise, reason);
1122}
1123
1124#[cfg(feature = "fetch")]
1125pub fn create_pending_promise(ctx: &mut JSContext) -> JSValue {
1126    let promise = create_promise(ctx);
1127    let ptr = Box::into_raw(Box::new(promise)) as usize;
1128    JSValue::new_object(ptr)
1129}
1130
1131pub fn run_microtasks_with_vm(ctx: &mut JSContext, vm: &mut crate::runtime::vm::VM) {
1132    while let Some(task) = ctx.microtask_dequeue() {
1133        match task {
1134            Microtask::Reaction(reaction, argument) => {
1135                if reaction.is_all_settled {
1136                    handle_all_settled_reaction(ctx, vm, &reaction, argument);
1137                    continue;
1138                }
1139                let handler = reaction.handler;
1140                if handler.is_int() && reaction.target_promise.is_object() {
1141                    let target = reaction.target_promise.as_object_mut();
1142                    if target.is_promise() && get_promise_state(ctx, target) == PromiseState::Pending {
1143                        let all_remaining_atom = ctx.intern("__all_remaining__");
1144                        let any_remaining_atom = ctx.intern("__any_remaining__");
1145                        if target.get(all_remaining_atom).is_some() {
1146                            if reaction.is_reject {
1147                                reject_promise(ctx, target, argument);
1148                            } else {
1149                                let idx = handler.get_int() as usize;
1150                                let results_slot = ctx.intern("__all_results__");
1151                                if let Some(results_val) = target.get(results_slot) {
1152                                    if results_val.is_object() {
1153                                        let results_arr = results_val.as_object_mut();
1154                                        let idx_atom = ctx.intern(&idx.to_string());
1155                                        results_arr.set(idx_atom, argument);
1156                                    }
1157                                }
1158                                let remaining = target.get(all_remaining_atom).unwrap().get_int() - 1;
1159                                target.set(all_remaining_atom, JSValue::new_int(remaining));
1160                                if remaining <= 0 {
1161                                    let results = target.get(results_slot).unwrap_or(JSValue::undefined());
1162                                    fulfill_promise(ctx, target, results);
1163                                }
1164                            }
1165                        } else if target.get(any_remaining_atom).is_some() {
1166                            if reaction.is_reject {
1167                                let remaining = target.get(any_remaining_atom).unwrap().get_int() - 1;
1168                                target.set(any_remaining_atom, JSValue::new_int(remaining));
1169                                if remaining <= 0 {
1170                                    let mut err = JSObject::new();
1171                                    err.set(intern_str(ctx, "name"), JSValue::new_string(ctx.intern("AggregateError")));
1172                                    err.set(intern_str(ctx, "message"), JSValue::new_string(ctx.intern("All promises were rejected")));
1173                                    if let Some(proto) = ctx.get_error_prototype() {
1174                                        err.prototype = Some(proto);
1175                                    }
1176                                    let err_ptr = Box::into_raw(Box::new(err)) as usize;
1177                                    ctx.runtime_mut().gc_heap_mut().track(err_ptr);
1178                                    reject_promise(ctx, target, JSValue::new_object(err_ptr));
1179                                }
1180                            } else {
1181                                fulfill_promise(ctx, target, argument);
1182                            }
1183                        } else {
1184                            if reaction.is_reject {
1185                                reject_promise(ctx, target, argument);
1186                            } else {
1187                                fulfill_promise(ctx, target, argument);
1188                            }
1189                        }
1190                    }
1191                    continue;
1192                }
1193                if handler.is_function() {
1194                    let result = vm.call_function(ctx, handler, &[argument]);
1195                    if reaction.target_promise.is_object() {
1196                        let target = reaction.target_promise.as_object_mut();
1197                        if target.is_promise() {
1198                            if reaction.is_finally {
1199                                match result {
1200                                    Ok(_) => {
1201                                        if reaction.is_reject {
1202                                            reject_promise(ctx, target, argument);
1203                                        } else {
1204                                            fulfill_promise(ctx, target, argument);
1205                                        }
1206                                    }
1207                                    Err(e) => {
1208                                        let msg = JSValue::new_string(ctx.intern(&e));
1209                                        reject_promise(ctx, target, msg);
1210                                    }
1211                                }
1212                            } else {
1213                                match result {
1214                                    Ok(val) => fulfill_promise(ctx, target, val),
1215                                    Err(_) => {
1216                                        reject_promise(ctx, target, JSValue::undefined());
1217                                    }
1218                                }
1219                            }
1220                        }
1221                    }
1222                }
1223            }
1224            Microtask::UserCallback(callback, args) => {
1225                if callback.is_function() {
1226                    let _result = vm.call_function(ctx, callback, &args);
1227                }
1228            }
1229        }
1230    }
1231}
1232
1233fn handle_all_settled_reaction(ctx: &mut JSContext, vm: &mut crate::runtime::vm::VM, reaction: &Reaction, argument: JSValue) {
1234    let idx = reaction.handler.get_int() as usize;
1235    let target = reaction.target_promise;
1236    if !target.is_object() {
1237        return;
1238    }
1239
1240    let mut result_obj = JSObject::new();
1241    let status_atom = ctx.intern("status");
1242    if reaction.is_reject {
1243        result_obj.set(status_atom, JSValue::new_string(ctx.intern("rejected")));
1244        result_obj.set(ctx.intern("reason"), argument);
1245    } else {
1246        result_obj.set(status_atom, JSValue::new_string(ctx.intern("fulfilled")));
1247        result_obj.set(ctx.intern("value"), argument);
1248    }
1249    let r_ptr = Box::into_raw(Box::new(result_obj)) as usize;
1250    ctx.runtime_mut().gc_heap_mut().track(r_ptr);
1251    let result_val = JSValue::new_object(r_ptr);
1252
1253    let target_obj = target.as_object_mut();
1254    let results_slot = ctx.intern("__allSettled_results__");
1255    if let Some(results_val) = target_obj.get(results_slot) {
1256        if results_val.is_object() {
1257            let results_arr = results_val.as_object_mut();
1258            let idx_atom = ctx.intern(&idx.to_string());
1259            results_arr.set(idx_atom, result_val);
1260        }
1261    }
1262
1263    let remaining_atom = ctx.intern("__allSettled_remaining__");
1264    if let Some(rem_val) = target_obj.get(remaining_atom) {
1265        let remaining = rem_val.get_int() - 1;
1266        target_obj.set(remaining_atom, JSValue::new_int(remaining));
1267        if remaining <= 0 {
1268            let results = target_obj.get(results_slot).unwrap_or(JSValue::undefined());
1269            fulfill_promise(ctx, target_obj, results);
1270        }
1271    }
1272}
1273
1274pub fn register_builtins(ctx: &mut JSContext) {
1275    ctx.register_builtin(
1276        "promise_executor",
1277        HostFunction::ctor("executor", 1, promise_executor),
1278    );
1279    ctx.register_builtin(
1280        "promise_internal_resolve",
1281        HostFunction::new("resolve", 1, promise_internal_resolve),
1282    );
1283    ctx.register_builtin(
1284        "promise_internal_reject",
1285        HostFunction::new("reject", 1, promise_internal_reject),
1286    );
1287    ctx.register_builtin(
1288        "promise_resolve",
1289        HostFunction::new("resolve", 1, promise_resolve),
1290    );
1291    ctx.register_builtin(
1292        "promise_reject",
1293        HostFunction::new("reject", 1, promise_reject),
1294    );
1295    ctx.register_builtin(
1296        "promise_then",
1297        HostFunction::method("then", 2, promise_then),
1298    );
1299    ctx.register_builtin(
1300        "promise_catch",
1301        HostFunction::method("catch", 2, promise_catch),
1302    );
1303    ctx.register_builtin(
1304        "promise_finally",
1305        HostFunction::method("finally", 2, promise_finally),
1306    );
1307    ctx.register_builtin("promise_all", HostFunction::new("all", 1, promise_all));
1308    ctx.register_builtin("promise_race", HostFunction::new("race", 1, promise_race));
1309    ctx.register_builtin(
1310        "promise_allSettled",
1311        HostFunction::new("allSettled", 1, promise_all_settled),
1312    );
1313    ctx.register_builtin("promise_any", HostFunction::new("any", 1, promise_any));
1314    ctx.register_builtin(
1315        "promise_with_resolvers",
1316        HostFunction::new("withResolvers", 0, promise_with_resolvers),
1317    );
1318}