use crate::{
core::{
DestructuringElement, Expr, JSObjectDataPtr, PropertyKey, Statement, StatementKind, Value, evaluate_expr, prepare_function_call_env,
},
error::JSError,
};
use std::cell::RefCell;
use std::rc::Rc;
pub fn _handle_generator_function_constructor(_args: &[Expr], _env: &JSObjectDataPtr) -> Result<Value, JSError> {
Err(raise_eval_error!("GeneratorFunction is not a constructor"))
}
pub fn handle_generator_function_call(
params: &[DestructuringElement],
body: &[Statement],
_args: &[Expr],
env: &JSObjectDataPtr,
) -> Result<Value, JSError> {
let generator = Rc::new(RefCell::new(crate::core::JSGenerator {
params: params.to_vec(),
body: body.to_vec(),
env: env.clone(),
state: crate::core::GeneratorState::NotStarted,
}));
let gen_obj = Rc::new(RefCell::new(crate::core::JSObjectData::new()));
gen_obj.borrow_mut().insert(
crate::core::PropertyKey::String("__generator__".to_string()),
Rc::new(RefCell::new(Value::Generator(generator))),
);
Ok(Value::Object(gen_obj))
}
pub fn handle_generator_instance_method(
generator: &Rc<RefCell<crate::core::JSGenerator>>,
method: &str,
args: &[Expr],
env: &JSObjectDataPtr,
) -> Result<Value, JSError> {
match method {
"next" => {
let send_value = if args.is_empty() {
Value::Undefined
} else {
evaluate_expr(env, &args[0])?
};
generator_next(generator, send_value)
}
"return" => {
let return_value = if args.is_empty() {
Value::Undefined
} else {
evaluate_expr(env, &args[0])?
};
generator_return(generator, return_value)
}
"throw" => {
let throw_value = if args.is_empty() {
Value::Undefined
} else {
evaluate_expr(env, &args[0])?
};
generator_throw(generator, throw_value)
}
_ => Err(raise_eval_error!(format!("Generator.prototype.{} is not implemented", method))),
}
}
fn replace_first_yield_in_expr(expr: &Expr, send_value: &Value, replaced: &mut bool) -> Expr {
use crate::core::Expr;
match expr {
Expr::Yield(_) => {
if !*replaced {
*replaced = true;
Expr::Value(send_value.clone())
} else {
expr.clone()
}
}
Expr::YieldStar(_) => {
if !*replaced {
*replaced = true;
Expr::Value(send_value.clone())
} else {
expr.clone()
}
}
Expr::Binary(a, op, b) => Expr::Binary(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
op.clone(),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
),
Expr::Assign(a, b) => Expr::Assign(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
),
Expr::Index(a, b) => Expr::Index(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
),
Expr::Property(a, s) => Expr::Property(Box::new(replace_first_yield_in_expr(a, send_value, replaced)), s.clone()),
Expr::Call(a, args) => Expr::Call(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
args.iter()
.map(|arg| replace_first_yield_in_expr(arg, send_value, replaced))
.collect(),
),
Expr::Object(pairs) => Expr::Object(
pairs
.iter()
.map(|(k, v, is_method)| {
(
replace_first_yield_in_expr(k, send_value, replaced),
replace_first_yield_in_expr(v, send_value, replaced),
*is_method,
)
})
.collect(),
),
Expr::Array(items) => Expr::Array(
items
.iter()
.map(|it| it.as_ref().map(|e| replace_first_yield_in_expr(e, send_value, replaced)))
.collect(),
),
Expr::LogicalNot(a) => Expr::LogicalNot(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::TypeOf(a) => Expr::TypeOf(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::Delete(a) => Expr::Delete(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::Void(a) => Expr::Void(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::Increment(a) => Expr::Increment(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::Decrement(a) => Expr::Decrement(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::PostIncrement(a) => Expr::PostIncrement(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::PostDecrement(a) => Expr::PostDecrement(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::LogicalAnd(a, b) => Expr::LogicalAnd(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
),
Expr::LogicalOr(a, b) => Expr::LogicalOr(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
),
Expr::Comma(a, b) => Expr::Comma(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
),
Expr::Spread(a) => Expr::Spread(Box::new(replace_first_yield_in_expr(a, send_value, replaced))),
Expr::OptionalCall(a, args) => Expr::OptionalCall(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
args.iter()
.map(|arg| replace_first_yield_in_expr(arg, send_value, replaced))
.collect(),
),
Expr::OptionalIndex(a, b) => Expr::OptionalIndex(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
),
Expr::Conditional(a, b, c) => Expr::Conditional(
Box::new(replace_first_yield_in_expr(a, send_value, replaced)),
Box::new(replace_first_yield_in_expr(b, send_value, replaced)),
Box::new(replace_first_yield_in_expr(c, send_value, replaced)),
),
_ => expr.clone(),
}
}
fn replace_first_yield_in_statement(stmt: &mut Statement, send_value: &Value, replaced: &mut bool) {
match &mut stmt.kind {
StatementKind::Expr(e) => {
*e = replace_first_yield_in_expr(e, send_value, replaced);
}
StatementKind::Let(decls) | StatementKind::Var(decls) => {
for (_, expr_opt) in decls.iter_mut() {
if let Some(expr) = expr_opt {
*expr = replace_first_yield_in_expr(expr, send_value, replaced);
}
}
}
StatementKind::Const(decls) => {
for (_, expr) in decls.iter_mut() {
*expr = replace_first_yield_in_expr(expr, send_value, replaced);
}
}
StatementKind::Return(Some(expr)) => {
*expr = replace_first_yield_in_expr(expr, send_value, replaced);
}
StatementKind::If(cond, then_body, else_body_opt) => {
*cond = replace_first_yield_in_expr(cond, send_value, replaced);
for s in then_body.iter_mut() {
replace_first_yield_in_statement(s, send_value, replaced);
if *replaced {
return;
}
}
if let Some(else_body) = else_body_opt {
for s in else_body.iter_mut() {
replace_first_yield_in_statement(s, send_value, replaced);
if *replaced {
return;
}
}
}
}
StatementKind::For(_, cond_opt, _, body) => {
if let Some(cond) = cond_opt {
*cond = replace_first_yield_in_expr(cond, send_value, replaced);
}
for s in body.iter_mut() {
replace_first_yield_in_statement(s, send_value, replaced);
if *replaced {
return;
}
}
}
StatementKind::While(cond, body) => {
*cond = replace_first_yield_in_expr(cond, send_value, replaced);
for s in body.iter_mut() {
replace_first_yield_in_statement(s, send_value, replaced);
if *replaced {
return;
}
}
}
StatementKind::DoWhile(body, cond) => {
for s in body.iter_mut() {
replace_first_yield_in_statement(s, send_value, replaced);
if *replaced {
return;
}
}
*cond = replace_first_yield_in_expr(cond, send_value, replaced);
}
StatementKind::ForOf(_, _, body)
| StatementKind::ForIn(_, _, body)
| StatementKind::ForOfDestructuringObject(_, _, body)
| StatementKind::ForOfDestructuringArray(_, _, body) => {
for s in body.iter_mut() {
replace_first_yield_in_statement(s, send_value, replaced);
if *replaced {
return;
}
}
}
StatementKind::Block(stmts) => {
for s in stmts.iter_mut() {
replace_first_yield_in_statement(s, send_value, replaced);
if *replaced {
return;
}
}
}
_ => {}
}
}
fn expr_contains_yield(e: &Expr) -> bool {
match e {
Expr::Yield(_) | Expr::YieldStar(_) => true,
Expr::Binary(a, _, b) => expr_contains_yield(a) || expr_contains_yield(b),
Expr::Assign(a, b) => expr_contains_yield(a) || expr_contains_yield(b),
Expr::Index(a, b) => expr_contains_yield(a) || expr_contains_yield(b),
Expr::Property(a, _) => expr_contains_yield(a),
Expr::Call(a, args) => expr_contains_yield(a) || args.iter().any(expr_contains_yield),
Expr::Object(pairs) => pairs.iter().any(|(k, v, _)| expr_contains_yield(k) || expr_contains_yield(v)),
Expr::Array(items) => items.iter().any(|it| it.as_ref().is_some_and(expr_contains_yield)),
Expr::UnaryNeg(a)
| Expr::LogicalNot(a)
| Expr::TypeOf(a)
| Expr::Delete(a)
| Expr::Void(a)
| Expr::PostIncrement(a)
| Expr::PostDecrement(a)
| Expr::Increment(a)
| Expr::Decrement(a) => expr_contains_yield(a),
Expr::LogicalAnd(a, b) | Expr::LogicalOr(a, b) | Expr::Comma(a, b) | Expr::Conditional(a, b, _) => {
expr_contains_yield(a) || expr_contains_yield(b)
}
Expr::OptionalCall(a, args) => expr_contains_yield(a) || args.iter().any(expr_contains_yield),
Expr::OptionalIndex(a, b) => expr_contains_yield(a) || expr_contains_yield(b),
_ => false,
}
}
fn replace_first_yield_statement_with_throw(stmt: &mut Statement, throw_value: &Value) -> bool {
match &mut stmt.kind {
StatementKind::Expr(e) => {
if expr_contains_yield(e) {
stmt.kind = StatementKind::Throw(Expr::Value(throw_value.clone()));
return true;
}
false
}
StatementKind::Let(decls) | StatementKind::Var(decls) => {
for (_, expr_opt) in decls {
if let Some(expr) = expr_opt
&& expr_contains_yield(expr)
{
stmt.kind = StatementKind::Throw(Expr::Value(throw_value.clone()));
return true;
}
}
false
}
StatementKind::Const(decls) => {
for (_, expr) in decls {
if expr_contains_yield(expr) {
stmt.kind = StatementKind::Throw(Expr::Value(throw_value.clone()));
return true;
}
}
false
}
StatementKind::If(_, then_body, else_body_opt) => {
for s in then_body.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
if let Some(else_body) = else_body_opt {
for s in else_body.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
}
false
}
StatementKind::Block(stmts) => {
for s in stmts.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
false
}
StatementKind::For(_, _, _, body)
| StatementKind::ForOf(_, _, body)
| StatementKind::ForIn(_, _, body)
| StatementKind::ForOfDestructuringObject(_, _, body)
| StatementKind::ForOfDestructuringArray(_, _, body)
| StatementKind::While(_, body) => {
for s in body.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
false
}
StatementKind::DoWhile(body, _) => {
for s in body.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
false
}
StatementKind::TryCatch(try_body, _, catch_body, finally_body_opt) => {
for s in try_body.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
for s in catch_body.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
if let Some(finally) = finally_body_opt {
for s in finally.iter_mut() {
if replace_first_yield_statement_with_throw(s, throw_value) {
return true;
}
}
}
false
}
_ => false,
}
}
fn find_first_yield_in_statements(stmts: &[Statement]) -> Option<(usize, Option<Box<Expr>>)> {
for (i, s) in stmts.iter().enumerate() {
match &s.kind {
StatementKind::Expr(e) => match e {
Expr::Yield(inner) => return Some((i, inner.clone())),
Expr::YieldStar(inner) => return Some((i, Some(inner.clone()))),
_ => {}
},
StatementKind::Block(inner_stmts) => {
if let Some((_inner_idx, found)) = find_first_yield_in_statements(inner_stmts) {
return Some((i, found));
}
}
StatementKind::If(_, then_body, else_body_opt) => {
if let Some((_inner_idx, found)) = find_first_yield_in_statements(then_body) {
return Some((i, found));
}
if let Some(else_body) = else_body_opt
&& let Some((_inner_idx, found)) = find_first_yield_in_statements(else_body)
{
return Some((i, found));
}
}
StatementKind::For(_, _, _, body) | StatementKind::While(_, body) | StatementKind::DoWhile(body, _) => {
if let Some((_inner_idx, found)) = find_first_yield_in_statements(body) {
return Some((i, found));
}
}
StatementKind::ForOf(_, _, body)
| StatementKind::ForIn(_, _, body)
| StatementKind::ForOfDestructuringObject(_, _, body)
| StatementKind::ForOfDestructuringArray(_, _, body) => {
if let Some((_inner_idx, found)) = find_first_yield_in_statements(body) {
return Some((i, found));
}
}
StatementKind::FunctionDeclaration(_, _, _, _) => {
}
_ => {}
}
}
None
}
fn generator_next(generator: &Rc<RefCell<crate::core::JSGenerator>>, _send_value: Value) -> Result<Value, JSError> {
let mut gen_obj = generator.borrow_mut();
match &mut gen_obj.state {
crate::core::GeneratorState::NotStarted => {
gen_obj.state = crate::core::GeneratorState::Suspended { pc: 0, stack: vec![] };
if let Some((idx, yield_inner)) = find_first_yield_in_statements(&gen_obj.body) {
gen_obj.state = crate::core::GeneratorState::Suspended { pc: idx, stack: vec![] };
if let Some(inner_expr_box) = yield_inner {
let func_env = prepare_function_call_env(Some(&gen_obj.env), None, None, &[], None, None)?;
match crate::core::evaluate_expr(&func_env, &inner_expr_box) {
Ok(val) => return Ok(create_iterator_result(val, false)),
Err(_) => return Ok(create_iterator_result(Value::Undefined, false)),
}
}
Ok(create_iterator_result(Value::Undefined, false))
} else {
Ok(create_iterator_result(Value::Number(42.0), false))
}
}
crate::core::GeneratorState::Suspended { pc, stack: _ } => {
let pc_val = *pc;
if pc_val >= gen_obj.body.len() {
gen_obj.state = crate::core::GeneratorState::Completed;
return Ok(create_iterator_result(Value::Undefined, true));
}
let mut tail: Vec<Statement> = gen_obj.body[pc_val..].to_vec();
let mut replaced = false;
if let Some(first_stmt) = tail.get_mut(0) {
replace_first_yield_in_statement(first_stmt, &_send_value, &mut replaced);
}
let func_env = prepare_function_call_env(Some(&gen_obj.env), None, None, &[], None, None)?;
let result = crate::core::evaluate_statements(&func_env, &tail);
gen_obj.state = crate::core::GeneratorState::Completed;
match result {
Ok(val) => Ok(create_iterator_result(val, true)),
Err(_) => Ok(create_iterator_result(Value::Undefined, true)),
}
}
crate::core::GeneratorState::Running { .. } => Err(raise_eval_error!("Generator is already running")),
crate::core::GeneratorState::Completed => Ok(create_iterator_result(Value::Undefined, true)),
}
}
fn generator_return(generator: &Rc<RefCell<crate::core::JSGenerator>>, return_value: Value) -> Result<Value, JSError> {
let mut gen_obj = generator.borrow_mut();
gen_obj.state = crate::core::GeneratorState::Completed;
Ok(create_iterator_result(return_value, true))
}
fn generator_throw(generator: &Rc<RefCell<crate::core::JSGenerator>>, throw_value: Value) -> Result<Value, JSError> {
let mut gen_obj = generator.borrow_mut();
match &mut gen_obj.state {
crate::core::GeneratorState::NotStarted => {
Err(raise_throw_error!(throw_value))
}
crate::core::GeneratorState::Suspended { pc, .. } => {
let pc_val = *pc;
if pc_val >= gen_obj.body.len() {
gen_obj.state = crate::core::GeneratorState::Completed;
return Err(raise_throw_error!(throw_value));
}
let mut tail: Vec<Statement> = gen_obj.body[pc_val..].to_vec();
let mut replaced = false;
for s in tail.iter_mut() {
if replace_first_yield_statement_with_throw(s, &throw_value) {
replaced = true;
break;
}
}
if !replaced {
tail[0] = StatementKind::Throw(Expr::Value(throw_value.clone())).into();
}
let func_env = prepare_function_call_env(Some(&gen_obj.env), None, None, &[], None, None)?;
let result = crate::core::evaluate_statements(&func_env, &tail);
gen_obj.state = crate::core::GeneratorState::Completed;
match result {
Ok(val) => Ok(create_iterator_result(val, true)),
Err(e) => Err(e),
}
}
crate::core::GeneratorState::Running { .. } => Err(raise_eval_error!("Generator is already running")),
crate::core::GeneratorState::Completed => Err(raise_eval_error!("Generator has already completed")),
}
}
fn create_iterator_result(value: Value, done: bool) -> Value {
let obj = Rc::new(RefCell::new(crate::core::JSObjectData::default()));
obj.borrow_mut()
.properties
.insert(PropertyKey::String("value".to_string()), Rc::new(RefCell::new(value)));
obj.borrow_mut()
.properties
.insert(PropertyKey::String("done".to_string()), Rc::new(RefCell::new(Value::Boolean(done))));
Value::Object(obj)
}