pub(crate) mod yield_stm;
use crate::{
Context, JsExpect, JsObject, JsResult,
builtins::{
async_generator::{AsyncGenerator as NativeAsyncGenerator, AsyncGeneratorState},
generator::{Generator as NativeGenerator, GeneratorContext, GeneratorState},
},
error::PanicError,
object::PROTOTYPE,
vm::{CompletionRecord, opcode::Operation},
};
use std::{collections::VecDeque, ops::ControlFlow};
pub(crate) use yield_stm::*;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Generator;
impl Generator {
#[inline(always)]
pub(super) fn operation((): (), context: &mut Context) -> ControlFlow<CompletionRecord> {
let Some(this_function_object) = context.vm.stack.get_function(context.vm.frame()) else {
return context.handle_error(
PanicError::new("active function should be set to the generator").into(),
);
};
let proto = this_function_object
.get(PROTOTYPE, context)
.ok()
.and_then(|v| v.as_object())
.unwrap_or_else(|| context.intrinsics().objects().generator());
let generator = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
proto,
NativeGenerator {
state: GeneratorState::Completed,
},
);
let gen_ctx = GeneratorContext::from_current(context, None);
generator.borrow_mut().data_mut().state =
GeneratorState::SuspendedStart { context: gen_ctx };
context.vm.set_return_value(generator.upcast().into());
context.handle_yield()
}
}
impl Operation for Generator {
const NAME: &'static str = "Generator";
const INSTRUCTION: &'static str = "INST - Generator";
const COST: u8 = 4;
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct AsyncGenerator;
impl AsyncGenerator {
#[inline(always)]
pub(super) fn operation((): (), context: &mut Context) -> ControlFlow<CompletionRecord> {
let Some(this_function_object) = context.vm.stack.get_function(context.vm.frame()) else {
return context.handle_error(
PanicError::new("active function should be set to the generator").into(),
);
};
let proto = this_function_object
.get(PROTOTYPE, context)
.ok()
.and_then(|v| v.as_object())
.unwrap_or_else(|| context.intrinsics().objects().async_generator());
let generator = JsObject::from_proto_and_data_with_shared_shape(
context.root_shape(),
proto,
NativeAsyncGenerator {
state: AsyncGeneratorState::SuspendedStart,
context: None,
queue: VecDeque::new(),
},
);
let gen_ctx = GeneratorContext::from_current(context, Some(generator.clone().upcast()));
generator.borrow_mut().data_mut().context = Some(gen_ctx);
context.vm.set_return_value(generator.upcast().into());
context.handle_yield()
}
}
impl Operation for AsyncGenerator {
const NAME: &'static str = "AsyncGenerator";
const INSTRUCTION: &'static str = "INST - AsyncGenerator";
const COST: u8 = 4;
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct AsyncGeneratorClose;
impl AsyncGeneratorClose {
#[inline(always)]
pub(super) fn operation((): (), context: &mut Context) -> JsResult<()> {
let generator = context
.vm
.async_generator_object()
.js_expect("There should be a object")?
.downcast::<NativeAsyncGenerator>()
.ok()
.js_expect("must be async generator")?;
let mut r#gen = generator.borrow_mut();
r#gen.data_mut().state = AsyncGeneratorState::DrainingQueue;
let return_value = context.vm.take_return_value();
let result = context
.vm
.pending_exception
.take()
.map_or(Ok(return_value), Err);
drop(r#gen);
NativeAsyncGenerator::complete_step(&generator, result, true, None, context)?;
NativeAsyncGenerator::drain_queue(&generator, context)?;
Ok(())
}
}
impl Operation for AsyncGeneratorClose {
const NAME: &'static str = "AsyncGeneratorClose";
const INSTRUCTION: &'static str = "INST - AsyncGeneratorClose";
const COST: u8 = 8;
}