use std::ops::ControlFlow;
use crate::{
Context, JsValue,
builtins::async_generator::{AsyncGenerator, AsyncGeneratorState},
error::PanicError,
vm::{
CompletionRecord, GeneratorResumeKind,
opcode::{Operation, RegisterOperand},
},
};
#[derive(Debug, Clone, Copy)]
pub(crate) struct GeneratorYield;
impl GeneratorYield {
#[inline(always)]
pub(crate) fn operation(
value: RegisterOperand,
context: &mut Context,
) -> ControlFlow<CompletionRecord> {
let value = context.vm.get_register(value.into());
context.vm.set_return_value(value.clone());
context.handle_yield()
}
}
impl Operation for GeneratorYield {
const NAME: &'static str = "GeneratorYield";
const INSTRUCTION: &'static str = "INST - GeneratorYield";
const COST: u8 = 1;
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct AsyncGeneratorYield;
impl AsyncGeneratorYield {
#[inline(always)]
pub(crate) fn operation(
value: RegisterOperand,
context: &mut Context,
) -> ControlFlow<CompletionRecord> {
let Some(async_generator_object) = context.vm.async_generator_object() else {
return context.handle_error(
PanicError::new(
"`AsyncGeneratorYield` must only be called inside async generators",
)
.into(),
);
};
let Ok(async_generator_object) = async_generator_object.downcast::<AsyncGenerator>() else {
return context.handle_error(PanicError::new("must be async generator object").into());
};
let value = context.vm.get_register(value.into());
let completion = Ok(value.clone());
if let Err(err) =
AsyncGenerator::complete_step(&async_generator_object, completion, false, None, context)
{
return context.handle_error(err);
}
let mut r#gen = async_generator_object.borrow_mut();
if let Some(next) = r#gen.data().queue.front() {
let resume_kind = match next.completion.clone() {
CompletionRecord::Normal(val) => {
context.vm.stack.push(val);
GeneratorResumeKind::Normal
}
CompletionRecord::Return(val) => {
context.vm.stack.push(val);
GeneratorResumeKind::Return
}
CompletionRecord::Throw(err) => {
let err = match err.into_opaque(context) {
Ok(e) => e,
Err(e) => return context.handle_error(e),
};
context.vm.stack.push(err);
GeneratorResumeKind::Throw
}
};
context.vm.stack.push(resume_kind);
return ControlFlow::Continue(());
}
r#gen.data_mut().state = AsyncGeneratorState::SuspendedYield;
context.vm.set_return_value(JsValue::undefined());
context.handle_yield()
}
}
impl Operation for AsyncGeneratorYield {
const NAME: &'static str = "AsyncGeneratorYield";
const INSTRUCTION: &'static str = "INST - AsyncGeneratorYield";
const COST: u8 = 8;
}