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}