Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify async iterators and iterators compilation #2976

Merged
merged 8 commits into from
Jun 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
19 changes: 8 additions & 11 deletions boa_engine/src/builtins/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ impl Array {
};

// c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator).
let iterator_record =
let mut iterator_record =
items.get_iterator(context, Some(IteratorHint::Sync), Some(using_iterator))?;

// d. Let k be 0.
Expand All @@ -571,19 +571,16 @@ impl Array {
// x. Set k to k + 1.
for k in 0..9_007_199_254_740_991_u64 {
// iii. Let next be ? IteratorStep(iteratorRecord).
let next = iterator_record.step(context)?;
if iterator_record.step(context)? {
// 1. Perform ? Set(A, "length", 𝔽(k), true).
a.set(utf16!("length"), k, true, context)?;
// 2. Return A.
return Ok(a.into());
}

// iv. If next is false, then
let Some(next) = next else {
// 1. Perform ? Set(A, "length", 𝔽(k), true).
a.set(utf16!("length"), k, true, context)?;

// 2. Return A.
return Ok(a.into());
};

// v. Let nextValue be ? IteratorValue(next).
let next_value = next.value(context)?;
let next_value = iterator_record.value(context)?;

// vi. If mapping is true, then
let mapped_value = if let Some(mapfn) = mapping {
Expand Down
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
6 changes: 3 additions & 3 deletions boa_engine/src/builtins/intl/list_format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ fn string_list_from_iterable(
}

// 2. Let iteratorRecord be ? GetIterator(iterable).
let iterator = iterable.get_iterator(context, None, None)?;
let mut iterator = iterable.get_iterator(context, None, None)?;

// 3. Let list be a new empty List.
let mut list = Vec::new();
Expand All @@ -478,9 +478,9 @@ fn string_list_from_iterable(
// 5. Repeat, while next is not false,
// a. Set next to ? IteratorStep(iteratorRecord).
// b. If next is not false, then
while let Some(item) = iterator.step(context)? {
while !iterator.step(context)? {
let item = iterator.value(context)?;
// i. Let nextValue be ? IteratorValue(next).
let item = item.value(context)?;
// ii. If Type(nextValue) is not String, then
let Some(s) = item.as_string().cloned() else {
// 1. Let error be ThrowCompletion(a newly created TypeError object).
Expand Down
76 changes: 21 additions & 55 deletions boa_engine/src/builtins/iterable/async_from_sync_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
object::{FunctionObjectBuilder, JsObject, ObjectData},
realm::Realm,
string::utf16,
Context, JsArgs, JsNativeError, JsResult, JsValue,
Context, JsArgs, JsResult, JsValue,
};
use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler;
Expand Down Expand Up @@ -84,7 +84,7 @@ impl AsyncFromSyncIterator {

// 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
// 5. Return iteratorRecord.
IteratorRecord::new(async_iterator, next_method, false)
IteratorRecord::new(async_iterator, next_method)
}

/// `%AsyncFromSyncIteratorPrototype%.next ( [ value ] )`
Expand Down Expand Up @@ -117,7 +117,15 @@ impl AsyncFromSyncIterator {
// a. Let result be Completion(IteratorNext(syncIteratorRecord, value)).
// 6. Else,
// a. Let result be Completion(IteratorNext(syncIteratorRecord)).
let result = sync_iterator_record.next(args.get(0), context);
let iterator = sync_iterator_record.iterator().clone();
let next = sync_iterator_record.next_method();
let result = next
.call(
&iterator.into(),
args.get(0).map_or(&[], std::slice::from_ref),
context,
)
.and_then(IteratorResult::from_value);

// 7. IfAbruptRejectPromise(result, promiseCapability).
if_abrupt_reject_promise!(result, promise_capability, context);
Expand Down Expand Up @@ -187,36 +195,15 @@ impl AsyncFromSyncIterator {
}
};

// 11. If Type(result) is not Object, then
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
let result = result.and_then(IteratorResult::from_value);

// 10. IfAbruptRejectPromise(result, promiseCapability).
if_abrupt_reject_promise!(result, promise_capability, context);

let Some(result) = result.as_object() else {
// 11. If Type(result) is not Object, then
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
promise_capability
.reject()
.call(
&JsValue::Undefined,
&[JsNativeError::typ()
.with_message("iterator return function returned non-object")
.to_opaque(context)
.into()],
context,
)
.expect("cannot fail according to spec");

// b. Return promiseCapability.[[Promise]].
return Ok(promise_capability.promise().clone().into());
};

// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability).
Self::continuation(
&IteratorResult {
object: result.clone(),
},
&promise_capability,
context,
)
Self::continuation(&result, &promise_capability, context)
}

/// `%AsyncFromSyncIteratorPrototype%.throw ( [ value ] )`
Expand Down Expand Up @@ -280,36 +267,15 @@ impl AsyncFromSyncIterator {
}
};

// 11. If Type(result) is not Object, then
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
let result = result.and_then(IteratorResult::from_value);

// 10. IfAbruptRejectPromise(result, promiseCapability).
if_abrupt_reject_promise!(result, promise_capability, context);

let Some(result) = result.as_object() else {
// 11. If Type(result) is not Object, then
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
promise_capability
.reject()
.call(
&JsValue::Undefined,
&[JsNativeError::typ()
.with_message("iterator throw function returned non-object")
.to_opaque(context)
.into()],
context,
)
.expect("cannot fail according to spec");

// b. Return promiseCapability.[[Promise]].
return Ok(promise_capability.promise().clone().into());
};

// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability).
Self::continuation(
&IteratorResult {
object: result.clone(),
},
&promise_capability,
context,
)
Self::continuation(&result, &promise_capability, context)
}

/// `AsyncFromSyncIteratorContinuation ( result, promiseCapability )`
Expand Down
Loading