Browse Source

Implement ECMA262's normative PR 2600 (#3633)

pull/3636/head
José Julián Espina 11 months ago committed by GitHub
parent
commit
8010052944
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 157
      core/engine/src/builtins/iterable/async_from_sync_iterator.rs

157
core/engine/src/builtins/iterable/async_from_sync_iterator.rs

@ -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
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
// 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())
}
}

Loading…
Cancel
Save