boa_engine/vm/opcode/await/
mod.rs1use std::cell::Cell;
2
3use boa_gc::Gc;
4
5use crate::{
6 builtins::{
7 async_generator::AsyncGenerator, generator::GeneratorContext, promise::PromiseCapability,
8 Promise,
9 },
10 js_string,
11 native_function::NativeFunction,
12 object::FunctionObjectBuilder,
13 vm::{opcode::Operation, CompletionType, GeneratorResumeKind},
14 Context, JsArgs, JsResult, JsValue,
15};
16
17#[derive(Debug, Clone, Copy)]
22pub(crate) struct Await;
23
24impl Operation for Await {
25 const NAME: &'static str = "Await";
26 const INSTRUCTION: &'static str = "INST - Await";
27 const COST: u8 = 5;
28
29 fn execute(context: &mut Context) -> JsResult<CompletionType> {
30 let value = context.vm.pop();
31
32 let promise = Promise::promise_resolve(
34 &context.intrinsics().constructors().promise().constructor(),
35 value,
36 context,
37 )?;
38
39 let return_value = context
40 .vm
41 .frame()
42 .promise_capability(&context.vm.stack)
43 .as_ref()
44 .map(PromiseCapability::promise)
45 .cloned()
46 .map(JsValue::from)
47 .unwrap_or_default();
48
49 let gen = GeneratorContext::from_current(context);
50
51 let captures = Gc::new(Cell::new(Some(gen)));
52
53 let on_fulfilled = FunctionObjectBuilder::new(
56 context.realm(),
57 NativeFunction::from_copy_closure_with_captures(
58 |_this, args, captures, context| {
59 let mut gen = captures.take().expect("should only run once");
64
65 let async_generator = gen.async_generator_object();
67
68 gen.resume(
69 Some(args.get_or_undefined(0).clone()),
70 GeneratorResumeKind::Normal,
71 context,
72 );
73
74 if let Some(async_generator) = async_generator {
75 async_generator
76 .downcast_mut::<AsyncGenerator>()
77 .expect("must be async generator")
78 .context = Some(gen);
79 }
80
81 Ok(JsValue::undefined())
84 },
85 captures.clone(),
86 ),
87 )
88 .name(js_string!())
89 .length(1)
90 .build();
91
92 let on_rejected = FunctionObjectBuilder::new(
95 context.realm(),
96 NativeFunction::from_copy_closure_with_captures(
97 |_this, args, captures, context| {
98 let mut gen = captures.take().expect("should only run once");
105
106 let async_generator = gen.async_generator_object();
108
109 gen.resume(
110 Some(args.get_or_undefined(0).clone()),
111 GeneratorResumeKind::Throw,
112 context,
113 );
114
115 if let Some(async_generator) = async_generator {
116 async_generator
117 .downcast_mut::<AsyncGenerator>()
118 .expect("must be async generator")
119 .context = Some(gen);
120 }
121
122 Ok(JsValue::undefined())
123 },
124 captures,
125 ),
126 )
127 .name(js_string!())
128 .length(1)
129 .build();
130
131 Promise::perform_promise_then(
133 &promise,
134 Some(on_fulfilled),
135 Some(on_rejected),
136 None,
137 context,
138 );
139
140 context.vm.set_return_value(return_value);
141 Ok(CompletionType::Yield)
142 }
143}
144
145#[derive(Debug, Clone, Copy)]
150pub(crate) struct CreatePromiseCapability;
151
152impl Operation for CreatePromiseCapability {
153 const NAME: &'static str = "CreatePromiseCapability";
154 const INSTRUCTION: &'static str = "INST - CreatePromiseCapability";
155 const COST: u8 = 8;
156
157 fn execute(context: &mut Context) -> JsResult<CompletionType> {
158 if context
159 .vm
160 .frame()
161 .promise_capability(&context.vm.stack)
162 .is_some()
163 {
164 return Ok(CompletionType::Normal);
165 }
166
167 let promise_capability = PromiseCapability::new(
168 &context.intrinsics().constructors().promise().constructor(),
169 context,
170 )
171 .expect("cannot fail per spec");
172
173 context
174 .vm
175 .frame
176 .set_promise_capability(&mut context.vm.stack, Some(&promise_capability));
177 Ok(CompletionType::Normal)
178 }
179}
180
181#[derive(Debug, Clone, Copy)]
186pub(crate) struct CompletePromiseCapability;
187
188impl Operation for CompletePromiseCapability {
189 const NAME: &'static str = "CompletePromiseCapability";
190 const INSTRUCTION: &'static str = "INST - CompletePromiseCapability";
191 const COST: u8 = 8;
192
193 fn execute(context: &mut Context) -> JsResult<CompletionType> {
194 let Some(promise_capability) = context.vm.frame().promise_capability(&context.vm.stack)
197 else {
198 return if context.vm.pending_exception.is_some() {
199 Ok(CompletionType::Throw)
200 } else {
201 Ok(CompletionType::Normal)
202 };
203 };
204
205 if let Some(error) = context.vm.pending_exception.take() {
206 promise_capability
207 .reject()
208 .call(&JsValue::undefined(), &[error.to_opaque(context)], context)
209 .expect("cannot fail per spec");
210 } else {
211 let return_value = context.vm.get_return_value();
212 promise_capability
213 .resolve()
214 .call(&JsValue::undefined(), &[return_value], context)
215 .expect("cannot fail per spec");
216 };
217
218 context
219 .vm
220 .set_return_value(promise_capability.promise().clone().into());
221
222 Ok(CompletionType::Normal)
223 }
224}