Skip to content

Commit

Permalink
Fix remaining async generator tests
Browse files Browse the repository at this point in the history
* Fix remaining async generator tests
  • Loading branch information
jedel1043 authored Jun 2, 2023
1 parent 1c5e8b7 commit 6a86988
Show file tree
Hide file tree
Showing 18 changed files with 420 additions and 296 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"program": "${workspaceFolder}/target/debug/boa",
"args": ["${workspaceFolder}/tests/js/test.js", "--debug-object"],
"sourceLanguages": ["rust"],
"preLaunchTask": "Cargo Build"
"preLaunchTask": "Cargo Build boa_cli"
},
{
"type": "lldb",
Expand All @@ -32,7 +32,7 @@
"tests/js"
],
"sourceLanguages": ["rust"],
"preLaunchTask": "Cargo Build"
"preLaunchTask": "Cargo Build boa_cli"
}
]
}
10 changes: 10 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
"clear": true
}
},
{
"type": "process",
"label": "Cargo Build boa_cli",
"command": "cargo",
"args": ["build", "-p", "boa_cli"],
"group": "build",
"presentation": {
"clear": true
}
},
{
"type": "process",
"label": "Cargo Run",
Expand Down
4 changes: 2 additions & 2 deletions boa_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ struct Jobs(RefCell<VecDeque<NativeJob>>);

impl JobQueue for Jobs {
fn enqueue_promise_job(&self, job: NativeJob, _: &mut Context<'_>) {
self.0.borrow_mut().push_front(job);
self.0.borrow_mut().push_back(job);
}

fn run_jobs(&self, context: &mut Context<'_>) {
Expand All @@ -509,6 +509,6 @@ impl JobQueue for Jobs {

fn enqueue_future_job(&self, future: FutureJob, _: &mut Context<'_>) {
let job = pollster::block_on(future);
self.0.borrow_mut().push_front(job);
self.0.borrow_mut().push_back(job);
}
}
67 changes: 21 additions & 46 deletions boa_engine/src/builtins/async_generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
realm::Realm,
symbol::JsSymbol,
value::JsValue,
vm::GeneratorResumeKind,
vm::{CompletionRecord, GeneratorResumeKind},
Context, JsArgs, JsError, JsResult,
};
use boa_gc::{Finalize, Trace};
Expand Down Expand Up @@ -46,7 +46,7 @@ pub(crate) enum AsyncGeneratorState {
#[derive(Debug, Clone, Finalize, Trace)]
pub(crate) struct AsyncGeneratorRequest {
/// The `[[Completion]]` slot.
pub(crate) completion: (JsResult<JsValue>, bool),
pub(crate) completion: CompletionRecord,

/// The `[[Capability]]` slot.
capability: PromiseCapability,
Expand Down Expand Up @@ -164,7 +164,7 @@ impl AsyncGenerator {
}

// 7. Let completion be NormalCompletion(value).
let completion = (Ok(args.get_or_undefined(0).clone()), false);
let completion = CompletionRecord::Normal(args.get_or_undefined(0).clone());

// 8. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
generator.enqueue(completion.clone(), promise_capability.clone());
Expand Down Expand Up @@ -232,7 +232,8 @@ impl AsyncGenerator {
if_abrupt_reject_promise!(generator, promise_capability, context);

// 5. Let completion be Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
let completion = (Ok(args.get_or_undefined(0).clone()), true);
let return_value = args.get_or_undefined(0).clone();
let completion = CompletionRecord::Return(return_value.clone());

// 6. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
generator.enqueue(completion.clone(), promise_capability.clone());
Expand All @@ -246,14 +247,8 @@ impl AsyncGenerator {
generator.state = AsyncGeneratorState::AwaitingReturn;

// b. Perform ! AsyncGeneratorAwaitReturn(generator).
let next = generator
.queue
.front()
.cloned()
.expect("queue cannot be empty here");
drop(generator_obj_mut);
let (completion, _) = &next.completion;
Self::await_return(generator_object.clone(), completion.clone(), context);
Self::await_return(generator_object.clone(), return_value, context);
}
// 9. Else if state is suspendedYield, then
else if state == AsyncGeneratorState::SuspendedYield {
Expand Down Expand Up @@ -346,10 +341,8 @@ impl AsyncGenerator {
}

// 8. Let completion be ThrowCompletion(exception).
let completion = (
Err(JsError::from_opaque(args.get_or_undefined(0).clone())),
false,
);
let completion =
CompletionRecord::Throw(JsError::from_opaque(args.get_or_undefined(0).clone()));

// 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
generator.enqueue(completion.clone(), promise_capability.clone());
Expand Down Expand Up @@ -384,7 +377,7 @@ impl AsyncGenerator {
/// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorenqueue
pub(crate) fn enqueue(
&mut self,
completion: (JsResult<JsValue>, bool),
completion: CompletionRecord,
promise_capability: PromiseCapability,
) {
// 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }.
Expand Down Expand Up @@ -469,7 +462,7 @@ impl AsyncGenerator {
generator: &JsObject,
state: AsyncGeneratorState,
mut generator_context: GeneratorContext,
completion: (JsResult<JsValue>, bool),
completion: CompletionRecord,
context: &mut Context<'_>,
) {
// 1. Assert: generator.[[AsyncGeneratorState]] is either suspendedStart or suspendedYield.
Expand All @@ -491,15 +484,9 @@ impl AsyncGenerator {
.state = AsyncGeneratorState::Executing;

let (value, resume_kind) = match completion {
(Ok(value), r#return) => (
value,
if r#return {
GeneratorResumeKind::Return
} else {
GeneratorResumeKind::Normal
},
),
(Err(value), _) => (value.to_opaque(context), GeneratorResumeKind::Throw),
CompletionRecord::Normal(val) => (val, GeneratorResumeKind::Normal),
CompletionRecord::Return(val) => (val, GeneratorResumeKind::Return),
CompletionRecord::Throw(err) => (err.to_opaque(context), GeneratorResumeKind::Throw),
};
// 6. Push genContext onto the execution context stack; genContext is now the running execution context.

Expand Down Expand Up @@ -527,19 +514,12 @@ impl AsyncGenerator {
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn
pub(crate) fn await_return(
generator: JsObject,
completion: JsResult<JsValue>,
context: &mut Context<'_>,
) {
pub(crate) fn await_return(generator: JsObject, value: JsValue, context: &mut Context<'_>) {
// 1. Let queue be generator.[[AsyncGeneratorQueue]].
// 2. Assert: queue is not empty.
// 3. Let next be the first element of queue.
// 4. Let completion be Completion(next.[[Completion]]).

// 5. Assert: completion.[[Type]] is return.
let value = completion.expect("completion must be a return completion");

// Note: The spec is currently broken here.
// See: https://github.com/tc39/ecma262/pull/2683

Expand Down Expand Up @@ -681,29 +661,24 @@ impl AsyncGenerator {
let next = queue.front().expect("must have entry");

// b. Let completion be Completion(next.[[Completion]]).
match &next.completion {
match next.completion.clone() {
// c. If completion.[[Type]] is return, then
(completion, true) => {
CompletionRecord::Return(val) => {
// i. Set generator.[[AsyncGeneratorState]] to awaiting-return.
gen.state = AsyncGeneratorState::AwaitingReturn;
drop(generator_borrow_mut);

// ii. Perform ! AsyncGeneratorAwaitReturn(generator).
let completion = completion.clone();
drop(generator_borrow_mut);
Self::await_return(generator.clone(), completion, context);
Self::await_return(generator.clone(), val, context);

// iii. Set done to true.
break;
}
// d. Else,
(completion, false) => {
completion => {
// i. If completion.[[Type]] is normal, then
let completion = if completion.is_ok() {
// 1. Set completion to NormalCompletion(undefined).
Ok(JsValue::undefined())
} else {
completion.clone()
};
// 1. Set completion to NormalCompletion(undefined).
let completion = completion.consume().map(|_| JsValue::undefined());

// ii. Perform AsyncGeneratorCompleteStep(generator, completion, true).
let next = queue.pop_front().expect("must have entry");
Expand Down
10 changes: 9 additions & 1 deletion boa_engine/src/bytecompiler/declarations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ impl ByteCompiler<'_, '_> {
self.context,
);

// Ensures global functions are printed when generating the global flowgraph.
self.functions.push(code.clone());

// b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv.
let function = if generator {
create_generator_function_object(code, r#async, None, self.context)
Expand Down Expand Up @@ -686,6 +689,9 @@ impl ByteCompiler<'_, '_> {

// c. If varEnv is a Global Environment Record, then
if var_environment_is_global {
// Ensures global functions are printed when generating the global flowgraph.
self.functions.push(code.clone());

// b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv.
let function = if generator {
create_generator_function_object(code, r#async, None, self.context)
Expand Down Expand Up @@ -988,7 +994,9 @@ impl ByteCompiler<'_, '_> {
}
if generator {
self.emit_opcode(Opcode::PushUndefined);
self.emit_opcode(Opcode::Yield);
// Don't need to use `AsyncGeneratorYield` since
// we just want to stop the execution of the generator.
self.emit_opcode(Opcode::GeneratorYield);
}

// 27. If hasParameterExpressions is false, then
Expand Down
27 changes: 9 additions & 18 deletions boa_engine/src/bytecompiler/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,39 +179,30 @@ impl ByteCompiler<'_, '_> {

let (return_gen, exit) =
self.emit_opcode_with_two_operands(Opcode::GeneratorDelegateResume);
if self.in_async_generator {
self.emit_opcode(Opcode::IteratorValue);
self.async_generator_yield();
} else {
self.emit_opcode(Opcode::IteratorResult);
self.emit_opcode(Opcode::GeneratorYield);
}
self.emit(Opcode::Jump, &[start_address]);

self.patch_jump(return_gen);
self.patch_jump(return_method_undefined);
if self.in_async_generator {
self.emit_opcode(Opcode::Await);
}
self.close_active_iterators(true);
self.close_active_iterators();
self.emit_opcode(Opcode::GeneratorResumeReturn);

self.patch_jump(throw_method_undefined);
self.iterator_close(self.in_async_generator);
self.emit_opcode(Opcode::Throw);

self.patch_jump(exit);
} else if self.in_async_generator {
self.emit_opcode(Opcode::Await);
let (skip_yield, skip_yield_await) =
self.emit_opcode_with_two_operands(Opcode::AsyncGeneratorNext);
self.emit_opcode(Opcode::PushUndefined);
self.emit_opcode(Opcode::Yield);
let normal_completion =
self.emit_opcode_with_operand(Opcode::GeneratorAsyncResumeYield);
self.patch_jump(skip_yield);
self.emit_opcode(Opcode::Await);
self.close_active_iterators(true);
self.emit_opcode(Opcode::GeneratorResumeReturn);

self.patch_jump(skip_yield_await);
self.patch_jump(normal_completion);
} else {
self.emit_opcode(Opcode::Yield);
self.emit_opcode(Opcode::GeneratorNext);
self.r#yield();
}

if !use_expr {
Expand Down
22 changes: 22 additions & 0 deletions boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,28 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
(Label { index }, Label { index: index + 4 })
}

/// Emit an opcode with three dummy operands.
/// Return the `Label`s of the three operands.
pub(crate) fn emit_opcode_with_three_operands(
&mut self,
opcode: Opcode,
) -> (Label, Label, Label) {
let index = self.next_opcode_location();
self.emit(
opcode,
&[
Self::DUMMY_ADDRESS,
Self::DUMMY_ADDRESS,
Self::DUMMY_ADDRESS,
],
);
(
Label { index },
Label { index: index + 4 },
Label { index: index + 8 },
)
}

pub(crate) fn patch_jump_with_target(&mut self, label: Label, target: u32) {
const U32_SIZE: usize = std::mem::size_of::<u32>();

Expand Down
4 changes: 4 additions & 0 deletions boa_engine/src/bytecompiler/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ impl ByteCompiler<'_, '_> {
Statement::Return(ret) => {
if let Some(expr) = ret.target() {
self.compile_expr(expr, true);
if self.in_async_generator {
self.emit_opcode(Opcode::Await);
self.emit_opcode(Opcode::GeneratorNext);
}
} else {
self.emit(Opcode::PushUndefined, &[]);
}
Expand Down
56 changes: 54 additions & 2 deletions boa_engine/src/bytecompiler/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,64 @@ impl ByteCompiler<'_, '_> {
}

/// Closes all active iterators in the current [`CallFrame`][crate::vm::CallFrame].
pub(super) fn close_active_iterators(&mut self, async_: bool) {
pub(super) fn close_active_iterators(&mut self) {
let start = self.next_opcode_location();
self.emit_opcode(Opcode::IteratorStackEmpty);
let empty = self.jump_if_true();
self.iterator_close(async_);
self.iterator_close(self.in_async_generator);
self.emit(Opcode::Jump, &[start]);
self.patch_jump(empty);
}

/// Yields from the current generator.
///
/// This is equivalent to the [`Yield ( value )`][yield] operation from the spec.
///
/// stack:
/// - value **=>** received
///
/// [yield]: https://tc39.es/ecma262/#sec-yield
pub(super) fn r#yield(&mut self) {
// 1. Let generatorKind be GetGeneratorKind().
if self.in_async_generator {
// 2. If generatorKind is async, return ? AsyncGeneratorYield(? Await(value)).
self.emit_opcode(Opcode::Await);
self.emit_opcode(Opcode::GeneratorNext);
self.async_generator_yield();
} else {
// 3. Otherwise, return ? GeneratorYield(CreateIterResultObject(value, false)).
self.emit_opcode(Opcode::CreateIteratorResult);
self.emit_u8(u8::from(false));
self.emit_opcode(Opcode::GeneratorYield);
}
self.emit_opcode(Opcode::GeneratorNext);
}

/// Yields from the current async generator.
///
/// This is equivalent to the [`AsyncGeneratorYield ( value )`][async_yield] operation from the spec.
///
/// stack:
/// - value **=>** received
///
/// [async_yield]: https://tc39.es/ecma262/#sec-asyncgeneratoryield
pub(super) fn async_generator_yield(&mut self) {
self.emit_opcode(Opcode::AsyncGeneratorYield);
let (normal, throw, r#return) =
self.emit_opcode_with_three_operands(Opcode::GeneratorJumpOnResumeKind);
{
self.patch_jump(r#return);
self.emit_opcode(Opcode::Await);

let (normal, throw, r#return) =
self.emit_opcode_with_three_operands(Opcode::GeneratorJumpOnResumeKind);
self.patch_jump(normal);
self.emit_opcode(Opcode::GeneratorSetReturn);

self.patch_jump(throw);
self.patch_jump(r#return);
}
self.patch_jump(normal);
self.patch_jump(throw);
}
}
Loading

0 comments on commit 6a86988

Please sign in to comment.