use super::RegisterOperand;
use crate::{
Context, JsArgs, JsExpect, JsResult, JsValue,
builtins::{
Promise, async_generator::AsyncGenerator, generator::GeneratorContext,
promise::PromiseCapability,
},
error::PanicError,
js_string,
native_function::NativeFunction,
object::FunctionObjectBuilder,
vm::{CompletionRecord, GeneratorResumeKind, opcode::Operation},
};
use boa_gc::Gc;
use std::{cell::Cell, ops::ControlFlow};
#[derive(Debug, Clone, Copy)]
pub(crate) struct Await;
impl Await {
#[inline(always)]
pub(super) fn operation(
value: RegisterOperand,
context: &mut Context,
) -> ControlFlow<CompletionRecord> {
let value = context.vm.get_register(value.into());
let promise = match Promise::promise_resolve(
&context.intrinsics().constructors().promise().constructor(),
value.clone(),
context,
) {
Ok(promise) => match promise.downcast::<Promise>().ok() {
Some(v) => v,
None => {
return context.handle_error(
PanicError::new("%Promise% constructor must return a `Promise` object")
.into(),
);
}
},
Err(err) => return context.handle_error(err),
};
let return_value = context
.vm
.get_promise_capability()
.ok()
.map(|cap| JsValue::from(cap.promise))
.unwrap_or_default();
let r#gen = GeneratorContext::from_current(context, None);
let captures = Gc::new(Cell::new(Some(r#gen)));
let on_fulfilled = FunctionObjectBuilder::new(
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, captures, context| {
let mut r#gen = captures.take().js_expect("should only run once")?;
let async_generator = r#gen.async_generator_object()?;
r#gen.resume(
Some(args.get_or_undefined(0).clone()),
GeneratorResumeKind::Normal,
context,
);
if let Some(async_generator) = async_generator {
async_generator
.downcast_mut::<AsyncGenerator>()
.js_expect("must be async generator")?
.context = Some(r#gen);
}
Ok(JsValue::undefined())
},
captures.clone(),
),
)
.name(js_string!())
.length(1)
.build();
let on_rejected = FunctionObjectBuilder::new(
context.realm(),
NativeFunction::from_copy_closure_with_captures(
|_this, args, captures, context| {
let mut r#gen = captures.take().js_expect("should only run once")?;
let async_generator = r#gen.async_generator_object()?;
r#gen.resume(
Some(args.get_or_undefined(0).clone()),
GeneratorResumeKind::Throw,
context,
);
if let Some(async_generator) = async_generator {
async_generator
.downcast_mut::<AsyncGenerator>()
.js_expect("must be async generator")?
.context = Some(r#gen);
}
Ok(JsValue::undefined())
},
captures,
),
)
.name(js_string!())
.length(1)
.build();
Promise::perform_promise_then(
&promise,
Some(on_fulfilled),
Some(on_rejected),
None,
context,
);
context.vm.set_return_value(return_value);
context.handle_yield()
}
}
impl Operation for Await {
const NAME: &'static str = "Await";
const INSTRUCTION: &'static str = "INST - Await";
const COST: u8 = 5;
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct CreatePromiseCapability;
impl CreatePromiseCapability {
#[inline(always)]
pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {
let promise_capability = PromiseCapability::new(
&context.intrinsics().constructors().promise().constructor(),
context,
)
.js_expect("cannot fail per spec")?;
context.vm.set_promise_capability(promise_capability)
}
}
impl Operation for CreatePromiseCapability {
const NAME: &'static str = "CreatePromiseCapability";
const INSTRUCTION: &'static str = "INST - CreatePromiseCapability";
const COST: u8 = 8;
}