|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|
|
|
|
object::{FunctionObjectBuilder, JsObject}, |
|
|
|
|
realm::Realm, |
|
|
|
|
string::utf16, |
|
|
|
|
Context, JsArgs, JsData, JsResult, JsValue, |
|
|
|
|
Context, JsArgs, JsData, JsError, JsNativeError, JsResult, JsValue, |
|
|
|
|
}; |
|
|
|
|
use boa_gc::{Finalize, Trace}; |
|
|
|
|
use boa_profiler::Profiler; |
|
|
|
@ -129,8 +129,14 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
// 7. IfAbruptRejectPromise(result, promiseCapability).
|
|
|
|
|
let result = if_abrupt_reject_promise!(result, promise_capability, context); |
|
|
|
|
|
|
|
|
|
// 8. Return AsyncFromSyncIteratorContinuation(result, promiseCapability).
|
|
|
|
|
Self::continuation(&result, &promise_capability, context) |
|
|
|
|
// 8. Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true).
|
|
|
|
|
Self::continuation( |
|
|
|
|
&result, |
|
|
|
|
&promise_capability, |
|
|
|
|
sync_iterator_record, |
|
|
|
|
true, |
|
|
|
|
context, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `%AsyncFromSyncIteratorPrototype%.return ( [ value ] )`
|
|
|
|
@ -142,14 +148,15 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
fn r#return(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { |
|
|
|
|
// 1. Let O be the this value.
|
|
|
|
|
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
|
|
|
|
|
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
|
|
|
|
|
let sync_iterator = this |
|
|
|
|
// 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
|
|
|
|
|
let sync_iterator_record = this |
|
|
|
|
.as_object() |
|
|
|
|
.and_then(JsObject::downcast_ref::<Self>) |
|
|
|
|
.expect("async from sync iterator prototype must be object") |
|
|
|
|
.sync_iterator_record |
|
|
|
|
.iterator() |
|
|
|
|
.clone(); |
|
|
|
|
// 5. Let syncIterator be syncIteratorRecord.[[Iterator]].
|
|
|
|
|
let sync_iterator = sync_iterator_record.iterator().clone(); |
|
|
|
|
|
|
|
|
|
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
|
|
|
|
let promise_capability = PromiseCapability::new( |
|
|
|
@ -158,14 +165,14 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
) |
|
|
|
|
.expect("cannot fail with promise constructor"); |
|
|
|
|
|
|
|
|
|
// 5. Let return be Completion(GetMethod(syncIterator, "return")).
|
|
|
|
|
// 6. Let return be Completion(GetMethod(syncIterator, "return")).
|
|
|
|
|
let r#return = sync_iterator.get_method(utf16!("return"), context); |
|
|
|
|
|
|
|
|
|
// 6. IfAbruptRejectPromise(return, promiseCapability).
|
|
|
|
|
// 7. IfAbruptRejectPromise(return, promiseCapability).
|
|
|
|
|
let r#return = if_abrupt_reject_promise!(r#return, promise_capability, context); |
|
|
|
|
|
|
|
|
|
let result = match (r#return, args.first()) { |
|
|
|
|
// 7. If return is undefined, then
|
|
|
|
|
// 8. If return is undefined, then
|
|
|
|
|
(None, _) => { |
|
|
|
|
// a. Let iterResult be CreateIterResultObject(value, true).
|
|
|
|
|
let iter_result = |
|
|
|
@ -180,27 +187,33 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
// c. Return promiseCapability.[[Promise]].
|
|
|
|
|
return Ok(promise_capability.promise().clone().into()); |
|
|
|
|
} |
|
|
|
|
// 8. If value is present, then
|
|
|
|
|
// 9. If value is present, then
|
|
|
|
|
(Some(r#return), Some(value)) => { |
|
|
|
|
// a. Let result be Completion(Call(return, syncIterator, « value »)).
|
|
|
|
|
r#return.call(&sync_iterator.clone().into(), &[value.clone()], context) |
|
|
|
|
} |
|
|
|
|
// 9. Else,
|
|
|
|
|
// 10. Else,
|
|
|
|
|
(Some(r#return), None) => { |
|
|
|
|
// a. Let result be Completion(Call(return, syncIterator)).
|
|
|
|
|
r#return.call(&sync_iterator.clone().into(), &[], context) |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 11. If Type(result) is not Object, then
|
|
|
|
|
// 12. 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).
|
|
|
|
|
// 11. IfAbruptRejectPromise(result, promiseCapability).
|
|
|
|
|
let result = if_abrupt_reject_promise!(result, promise_capability, context); |
|
|
|
|
|
|
|
|
|
// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability).
|
|
|
|
|
Self::continuation(&result, &promise_capability, context) |
|
|
|
|
// 13. Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, false).
|
|
|
|
|
Self::continuation( |
|
|
|
|
&result, |
|
|
|
|
&promise_capability, |
|
|
|
|
sync_iterator_record, |
|
|
|
|
false, |
|
|
|
|
context, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `%AsyncFromSyncIteratorPrototype%.throw ( [ value ] )`
|
|
|
|
@ -212,14 +225,15 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
fn throw(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { |
|
|
|
|
// 1. Let O be the this value.
|
|
|
|
|
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
|
|
|
|
|
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]].
|
|
|
|
|
let sync_iterator = this |
|
|
|
|
// 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
|
|
|
|
|
let sync_iterator_record = this |
|
|
|
|
.as_object() |
|
|
|
|
.and_then(JsObject::downcast_ref::<Self>) |
|
|
|
|
.expect("async from sync iterator prototype must be object") |
|
|
|
|
.sync_iterator_record |
|
|
|
|
.iterator() |
|
|
|
|
.clone(); |
|
|
|
|
// 5. Let syncIterator be syncIteratorRecord.[[Iterator]].
|
|
|
|
|
let sync_iterator = sync_iterator_record.iterator().clone(); |
|
|
|
|
|
|
|
|
|
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
|
|
|
|
let promise_capability = PromiseCapability::new( |
|
|
|
@ -228,49 +242,67 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
) |
|
|
|
|
.expect("cannot fail with promise constructor"); |
|
|
|
|
|
|
|
|
|
// 5. Let throw be Completion(GetMethod(syncIterator, "throw")).
|
|
|
|
|
// 6. Let throw be Completion(GetMethod(syncIterator, "throw")).
|
|
|
|
|
let throw = sync_iterator.get_method(utf16!("throw"), context); |
|
|
|
|
|
|
|
|
|
// 6. IfAbruptRejectPromise(throw, promiseCapability).
|
|
|
|
|
// 7. IfAbruptRejectPromise(throw, promiseCapability).
|
|
|
|
|
let throw = if_abrupt_reject_promise!(throw, promise_capability, context); |
|
|
|
|
|
|
|
|
|
let result = match (throw, args.first()) { |
|
|
|
|
// 7. If throw is undefined, then
|
|
|
|
|
// 8. If throw is undefined, then
|
|
|
|
|
(None, _) => { |
|
|
|
|
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
|
|
|
|
|
// a. NOTE: If syncIterator does not have a throw method, close it to give it a chance to clean up before we reject the capability.
|
|
|
|
|
// b. Let closeCompletion be NormalCompletion(empty).
|
|
|
|
|
// c. Let result be Completion(IteratorClose(syncIteratorRecord, closeCompletion)).
|
|
|
|
|
let result = sync_iterator_record.close(Ok(JsValue::undefined()), context); |
|
|
|
|
// d. IfAbruptRejectPromise(result, promiseCapability).
|
|
|
|
|
if_abrupt_reject_promise!(result, promise_capability, context); |
|
|
|
|
|
|
|
|
|
// e. NOTE: The next step throws a TypeError to indicate that there was a protocol violation: syncIterator does not have a throw method.
|
|
|
|
|
// f. NOTE: If closing syncIterator does not throw then the result of that operation is ignored, even if it yields a rejected promise.
|
|
|
|
|
// g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
|
|
|
|
promise_capability |
|
|
|
|
.reject() |
|
|
|
|
.call( |
|
|
|
|
&JsValue::Undefined, |
|
|
|
|
&[args.get_or_undefined(0).clone()], |
|
|
|
|
&JsValue::undefined(), |
|
|
|
|
&[JsNativeError::typ() |
|
|
|
|
.with_message("sync iterator does not have a throw method") |
|
|
|
|
.to_opaque(context) |
|
|
|
|
.into()], |
|
|
|
|
context, |
|
|
|
|
) |
|
|
|
|
.expect("cannot fail according to spec"); |
|
|
|
|
|
|
|
|
|
// b. Return promiseCapability.[[Promise]].
|
|
|
|
|
// h. Return promiseCapability.[[Promise]].
|
|
|
|
|
return Ok(promise_capability.promise().clone().into()); |
|
|
|
|
} |
|
|
|
|
// 8. If value is present, then
|
|
|
|
|
// 9. If value is present, then
|
|
|
|
|
(Some(throw), Some(value)) => { |
|
|
|
|
// a. Let result be Completion(Call(throw, syncIterator, « value »)).
|
|
|
|
|
throw.call(&sync_iterator.clone().into(), &[value.clone()], context) |
|
|
|
|
} |
|
|
|
|
// 9. Else,
|
|
|
|
|
// 10. Else,
|
|
|
|
|
(Some(throw), None) => { |
|
|
|
|
// a. Let result be Completion(Call(throw, syncIterator)).
|
|
|
|
|
throw.call(&sync_iterator.clone().into(), &[], context) |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 11. If Type(result) is not Object, then
|
|
|
|
|
// 12. 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).
|
|
|
|
|
// 11. IfAbruptRejectPromise(result, promiseCapability).
|
|
|
|
|
let result = if_abrupt_reject_promise!(result, promise_capability, context); |
|
|
|
|
|
|
|
|
|
// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability).
|
|
|
|
|
Self::continuation(&result, &promise_capability, context) |
|
|
|
|
// 13. Return Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true).
|
|
|
|
|
Self::continuation( |
|
|
|
|
&result, |
|
|
|
|
&promise_capability, |
|
|
|
|
sync_iterator_record, |
|
|
|
|
true, |
|
|
|
|
context, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `AsyncFromSyncIteratorContinuation ( result, promiseCapability )`
|
|
|
|
@ -282,6 +314,8 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
fn continuation( |
|
|
|
|
result: &IteratorResult, |
|
|
|
|
promise_capability: &PromiseCapability, |
|
|
|
|
sync_iterator_record: IteratorRecord, |
|
|
|
|
close_on_rejection: bool, |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> JsResult<JsValue> { |
|
|
|
|
// 1. NOTE: Because promiseCapability is derived from the intrinsic %Promise%,
|
|
|
|
@ -301,18 +335,28 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
let value = if_abrupt_reject_promise!(value, promise_capability, context); |
|
|
|
|
|
|
|
|
|
// 6. Let valueWrapper be Completion(PromiseResolve(%Promise%, value)).
|
|
|
|
|
let value_wrapper = Promise::promise_resolve( |
|
|
|
|
let value_wrapper = match Promise::promise_resolve( |
|
|
|
|
&context.intrinsics().constructors().promise().constructor(), |
|
|
|
|
value, |
|
|
|
|
context, |
|
|
|
|
); |
|
|
|
|
) { |
|
|
|
|
// 7. If valueWrapper is an abrupt completion, done is false, and closeOnRejection is
|
|
|
|
|
// true, then
|
|
|
|
|
Err(e) if !done && close_on_rejection => { |
|
|
|
|
// a. Set valueWrapper to Completion(IteratorClose(syncIteratorRecord, valueWrapper)).
|
|
|
|
|
Err(sync_iterator_record.close(Err(e), context).expect_err( |
|
|
|
|
"closing an iterator with an error must always return an error back", |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
other => other, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 7. IfAbruptRejectPromise(valueWrapper, promiseCapability).
|
|
|
|
|
// 8. IfAbruptRejectPromise(valueWrapper, promiseCapability).
|
|
|
|
|
let value_wrapper = if_abrupt_reject_promise!(value_wrapper, promise_capability, context); |
|
|
|
|
|
|
|
|
|
// 8. Let unwrap be a new Abstract Closure with parameters (value)
|
|
|
|
|
// 9. Let unwrap be a new Abstract Closure with parameters (value)
|
|
|
|
|
// that captures done and performs the following steps when called:
|
|
|
|
|
// 9. Let onFulfilled be CreateBuiltinFunction(unwrap, 1, "", « »).
|
|
|
|
|
// 10. Let onFulfilled be CreateBuiltinFunction(unwrap, 1, "", « »).
|
|
|
|
|
let on_fulfilled = FunctionObjectBuilder::new( |
|
|
|
|
context.realm(), |
|
|
|
|
NativeFunction::from_copy_closure(move |_this, args, context| { |
|
|
|
@ -328,20 +372,51 @@ impl AsyncFromSyncIterator {
|
|
|
|
|
.length(1) |
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
// 10. NOTE: onFulfilled is used when processing the "value" property of an
|
|
|
|
|
// 11. NOTE: onFulfilled is used when processing the "value" property of an
|
|
|
|
|
// IteratorResult object in order to wait for its value if it is a promise and
|
|
|
|
|
// re-package the result in a new "unwrapped" IteratorResult object.
|
|
|
|
|
|
|
|
|
|
// 11. Perform PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability).
|
|
|
|
|
// 12. If done is true, or if closeOnRejection is false, then
|
|
|
|
|
let on_rejected = if done || !close_on_rejection { |
|
|
|
|
// a. Let onRejected be undefined.
|
|
|
|
|
None |
|
|
|
|
} else { |
|
|
|
|
// 13. Else,
|
|
|
|
|
// a. Let closeIterator be a new Abstract Closure with parameters (error) that
|
|
|
|
|
// captures syncIteratorRecord and performs the following steps when called:
|
|
|
|
|
// b. Let onRejected be CreateBuiltinFunction(closeIterator, 1, "", « »).
|
|
|
|
|
// c. NOTE: onRejected is used to close the Iterator when the "value" property of an
|
|
|
|
|
// IteratorResult object it yields is a rejected promise.
|
|
|
|
|
Some( |
|
|
|
|
FunctionObjectBuilder::new( |
|
|
|
|
context.realm(), |
|
|
|
|
NativeFunction::from_copy_closure_with_captures( |
|
|
|
|
|_this, args, iter, context| { |
|
|
|
|
// i. Return ? IteratorClose(syncIteratorRecord, ThrowCompletion(error)).
|
|
|
|
|
iter.close( |
|
|
|
|
Err(JsError::from_opaque(args.get_or_undefined(0).clone())), |
|
|
|
|
context, |
|
|
|
|
) |
|
|
|
|
}, |
|
|
|
|
sync_iterator_record, |
|
|
|
|
), |
|
|
|
|
) |
|
|
|
|
.name("") |
|
|
|
|
.length(1) |
|
|
|
|
.build(), |
|
|
|
|
) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// 14. Perform PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability).
|
|
|
|
|
Promise::perform_promise_then( |
|
|
|
|
&value_wrapper, |
|
|
|
|
Some(on_fulfilled), |
|
|
|
|
None, |
|
|
|
|
on_rejected, |
|
|
|
|
Some(promise_capability.clone()), |
|
|
|
|
context, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// 12. Return promiseCapability.[[Promise]].
|
|
|
|
|
// 15. Return promiseCapability.[[Promise]].
|
|
|
|
|
Ok(promise_capability.promise().clone().into()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|