Skip to content

Commit

Permalink
Fix sync generator yield expression
Browse files Browse the repository at this point in the history
  • Loading branch information
raskad committed Apr 18, 2023
1 parent 20f4a82 commit dadda0c
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 72 deletions.
119 changes: 60 additions & 59 deletions boa_engine/src/builtins/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,13 +189,7 @@ impl Generator {
context: &mut Context<'_>,
) -> JsResult<JsValue> {
// 1. Return ? GeneratorResume(this value, value, empty).
let completion = Self::generator_resume(this, args.get_or_undefined(0).clone(), context);

match completion {
CompletionRecord::Return(value) => Ok(create_iter_result_object(value, false, context)),
CompletionRecord::Normal(value) => Ok(create_iter_result_object(value, true, context)),
CompletionRecord::Throw(err) => Err(err),
}
Self::generator_resume(this, args.get_or_undefined(0).clone(), context)
}

/// `Generator.prototype.return ( value )`
Expand All @@ -216,14 +210,7 @@ impl Generator {
// 1. Let g be the this value.
// 2. Let C be Completion { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
// 3. Return ? GeneratorResumeAbrupt(g, C, empty).
let completion =
Self::generator_resume_abrupt(this, Ok(args.get_or_undefined(0).clone()), context);

match completion {
CompletionRecord::Return(value) => Ok(create_iter_result_object(value, false, context)),
CompletionRecord::Normal(value) => Ok(create_iter_result_object(value, true, context)),
CompletionRecord::Throw(err) => Err(err),
}
Self::generator_resume_abrupt(this, Ok(args.get_or_undefined(0).clone()), context)
}

/// `Generator.prototype.throw ( exception )`
Expand All @@ -245,17 +232,11 @@ impl Generator {
// 1. Let g be the this value.
// 2. Let C be ThrowCompletion(exception).
// 3. Return ? GeneratorResumeAbrupt(g, C, empty).
let completion = Self::generator_resume_abrupt(
Self::generator_resume_abrupt(
this,
Err(JsError::from_opaque(args.get_or_undefined(0).clone())),
context,
);

match completion {
CompletionRecord::Return(value) => Ok(create_iter_result_object(value, false, context)),
CompletionRecord::Normal(value) => Ok(create_iter_result_object(value, true, context)),
CompletionRecord::Throw(err) => Err(err),
}
)
}

/// `27.5.3.3 GeneratorResume ( generator, value, generatorBrand )`
Expand All @@ -268,18 +249,18 @@ impl Generator {
gen: &JsValue,
value: JsValue,
context: &mut Context<'_>,
) -> CompletionRecord {
) -> JsResult<JsValue> {
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
let Some(generator_obj) = gen.as_object() else {
return CompletionRecord::Throw(
return Err(
JsNativeError::typ()
.with_message("Generator method called on non generator")
.into()
);
};
let mut generator_obj_mut = generator_obj.borrow_mut();
let Some(generator) = generator_obj_mut.as_generator_mut() else {
return CompletionRecord::Throw(
return Err(
JsNativeError::typ()
.with_message("generator resumed on non generator object")
.into()
Expand All @@ -293,16 +274,18 @@ impl Generator {
let (mut generator_context, first_execution) =
match std::mem::replace(&mut generator.state, GeneratorState::Executing) {
GeneratorState::Executing => {
return CompletionRecord::Throw(
JsNativeError::typ()
.with_message("Generator should not be executing")
.into(),
);
return Err(JsNativeError::typ()
.with_message("Generator should not be executing")
.into());
}
// 2. If state is completed, return CreateIterResultObject(undefined, true).
GeneratorState::Completed => {
generator.state = GeneratorState::Completed;
return CompletionRecord::Normal(JsValue::undefined());
return Ok(create_iter_result_object(
JsValue::undefined(),
true,
context,
));
}
// 3. Assert: state is either suspendedStart or suspendedYield.
GeneratorState::SuspendedStart { context } => (context, true),
Expand All @@ -322,18 +305,26 @@ impl Generator {
.as_generator_mut()
.expect("already checked this object type");

generator.state = match record {
CompletionRecord::Return(_) => GeneratorState::SuspendedYield {
context: generator_context,
},
CompletionRecord::Normal(_) | CompletionRecord::Throw(_) => GeneratorState::Completed,
};

// 8. Push genContext onto the execution context stack; genContext is now the running execution context.
// 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as the result of the operation that suspended it. Let result be the value returned by the resumed computation.
// 10. Assert: When we return here, genContext has already been removed from the execution context stack and methodContext is the currently running execution context.
// 11. Return Completion(result).
record
match record {
CompletionRecord::Return(value) => {
generator.state = GeneratorState::SuspendedYield {
context: generator_context,
};
Ok(value)
}
CompletionRecord::Normal(value) => {
generator.state = GeneratorState::Completed;
Ok(create_iter_result_object(value, true, context))
}
CompletionRecord::Throw(err) => {
generator.state = GeneratorState::Completed;
Err(err)
}
}
}

/// `27.5.3.4 GeneratorResumeAbrupt ( generator, abruptCompletion, generatorBrand )`
Expand All @@ -346,18 +337,18 @@ impl Generator {
gen: &JsValue,
abrupt_completion: JsResult<JsValue>,
context: &mut Context<'_>,
) -> CompletionRecord {
) -> JsResult<JsValue> {
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
let Some(generator_obj) = gen.as_object() else {
return CompletionRecord::Throw(
return Err(
JsNativeError::typ()
.with_message("Generator method called on non generator")
.into()
);
};
let mut generator_obj_mut = generator_obj.borrow_mut();
let Some(generator) = generator_obj_mut.as_generator_mut() else {
return CompletionRecord::Throw(
return Err(
JsNativeError::typ()
.with_message("generator resumed on non generator object")
.into()
Expand All @@ -372,11 +363,9 @@ impl Generator {
let mut generator_context =
match std::mem::replace(&mut generator.state, GeneratorState::Executing) {
GeneratorState::Executing => {
return CompletionRecord::Throw(
JsNativeError::typ()
.with_message("Generator should not be executing")
.into(),
);
return Err(JsNativeError::typ()
.with_message("Generator should not be executing")
.into());
}
// 2. If state is suspendedStart, then
// 3. If state is completed, then
Expand All @@ -389,10 +378,14 @@ impl Generator {
// with generator can be discarded at this point.

// a. If abruptCompletion.[[Type]] is return, then
// i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).
if let Ok(value) = abrupt_completion {
// i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).
let value = create_iter_result_object(value, true, context);
return Ok(value);
}

// b. Return Completion(abruptCompletion).
return abrupt_completion
.map_or_else(CompletionRecord::Throw, CompletionRecord::Normal);
return abrupt_completion;
}
GeneratorState::SuspendedYield { context } => context,
};
Expand All @@ -415,13 +408,21 @@ impl Generator {
.as_generator_mut()
.expect("already checked this object type");

generator.state = match record {
CompletionRecord::Return(_) => GeneratorState::SuspendedYield {
context: generator_context,
},
CompletionRecord::Normal(_) | CompletionRecord::Throw(_) => GeneratorState::Completed,
};

record
match record {
CompletionRecord::Return(value) => {
generator.state = GeneratorState::SuspendedYield {
context: generator_context,
};
Ok(value)
}
CompletionRecord::Normal(value) => {
generator.state = GeneratorState::Completed;
Ok(create_iter_result_object(value, true, context))
}
CompletionRecord::Throw(err) => {
generator.state = GeneratorState::Completed;
Err(err)
}
}
}
}
22 changes: 9 additions & 13 deletions boa_engine/src/vm/opcode/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
string::utf16,
vm::{
call_frame::{AbruptCompletionRecord, GeneratorResumeKind},
opcode::Operation,
opcode::{control_flow::Return, Operation},
CompletionType,
},
Context, JsError, JsResult, JsValue,
Expand Down Expand Up @@ -139,8 +139,9 @@ impl Operation for GeneratorNextDelegate {

match context.vm.frame().generator_resume_kind {
GeneratorResumeKind::Normal => {
let result = next_method.call(&iterator.clone().into(), &[received], context)?;
let result = result.as_object().ok_or_else(|| {
let result_value =
next_method.call(&iterator.clone().into(), &[received], context)?;
let result = result_value.as_object().ok_or_else(|| {
JsNativeError::typ().with_message("generator next method returned non-object")
})?;
// TODO: This is wrong for async generators, since we need to await the result first.
Expand All @@ -151,10 +152,9 @@ impl Operation for GeneratorNextDelegate {
context.vm.push(value);
return Ok(CompletionType::Normal);
}
let value = result.get(utf16!("value"), context)?;
context.vm.push(iterator.clone());
context.vm.push(next_method.clone());
context.vm.push(value);
context.vm.push(result_value);
context.vm.frame_mut().r#yield = true;
Ok(CompletionType::Return)
}
Expand All @@ -173,10 +173,9 @@ impl Operation for GeneratorNextDelegate {
context.vm.push(value);
return Ok(CompletionType::Normal);
}
let value = result_object.get(utf16!("value"), context)?;
context.vm.push(iterator.clone());
context.vm.push(next_method.clone());
context.vm.push(value);
context.vm.push(result);
context.vm.frame_mut().r#yield = true;
return Ok(CompletionType::Return);
}
Expand All @@ -198,21 +197,18 @@ impl Operation for GeneratorNextDelegate {
})?;
let done = result_object.get(utf16!("done"), context)?.to_boolean();
if done {
context.vm.frame_mut().pc = done_address as usize;
let value = result_object.get(utf16!("value"), context)?;
context.vm.push(value);
return Ok(CompletionType::Return);
return Return::execute(context);
}
let value = result_object.get(utf16!("value"), context)?;
context.vm.push(iterator.clone());
context.vm.push(next_method.clone());
context.vm.push(value);
context.vm.push(result);
context.vm.frame_mut().r#yield = true;
return Ok(CompletionType::Return);
}
context.vm.frame_mut().pc = done_address as usize;
context.vm.push(received);
Ok(CompletionType::Return)
Return::execute(context)
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions boa_engine/src/vm/opcode/generator/yield_stm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
builtins::iterable::create_iter_result_object,
vm::{opcode::Operation, CompletionType},
Context, JsResult,
};
Expand All @@ -15,6 +16,9 @@ impl Operation for Yield {
const INSTRUCTION: &'static str = "INST - Yield";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let value = context.vm.pop();
let value = create_iter_result_object(value, false, context);
context.vm.push(value);
context.vm.frame_mut().r#yield = true;
Ok(CompletionType::Return)
}
Expand Down

0 comments on commit dadda0c

Please sign in to comment.