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}, object::{FunctionObjectBuilder, JsObject},
realm::Realm, realm::Realm,
string::utf16, string::utf16,
Context, JsArgs, JsData, JsResult, JsValue, Context, JsArgs, JsData, JsError, JsNativeError, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -129,8 +129,14 @@ impl AsyncFromSyncIterator {
// 7. IfAbruptRejectPromise(result, promiseCapability). // 7. IfAbruptRejectPromise(result, promiseCapability).
let result = if_abrupt_reject_promise!(result, promise_capability, context); let result = if_abrupt_reject_promise!(result, promise_capability, context);
// 8. Return AsyncFromSyncIteratorContinuation(result, promiseCapability). // 8. Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true).
Self::continuation(&result, &promise_capability, context) Self::continuation(
&result,
&promise_capability,
sync_iterator_record,
true,
context,
)
} }
/// `%AsyncFromSyncIteratorPrototype%.return ( [ value ] )` /// `%AsyncFromSyncIteratorPrototype%.return ( [ value ] )`
@ -142,14 +148,15 @@ impl AsyncFromSyncIterator {
fn r#return(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn r#return(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let O be the this value. // 1. Let O be the this value.
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot. // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]]. // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
let sync_iterator = this let sync_iterator_record = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.expect("async from sync iterator prototype must be object") .expect("async from sync iterator prototype must be object")
.sync_iterator_record .sync_iterator_record
.iterator()
.clone(); .clone();
// 5. Let syncIterator be syncIteratorRecord.[[Iterator]].
let sync_iterator = sync_iterator_record.iterator().clone();
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
let promise_capability = PromiseCapability::new( let promise_capability = PromiseCapability::new(
@ -158,14 +165,14 @@ impl AsyncFromSyncIterator {
) )
.expect("cannot fail with promise constructor"); .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); 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 r#return = if_abrupt_reject_promise!(r#return, promise_capability, context);
let result = match (r#return, args.first()) { let result = match (r#return, args.first()) {
// 7. If return is undefined, then // 8. If return is undefined, then
(None, _) => { (None, _) => {
// a. Let iterResult be CreateIterResultObject(value, true). // a. Let iterResult be CreateIterResultObject(value, true).
let iter_result = let iter_result =
@ -180,27 +187,33 @@ impl AsyncFromSyncIterator {
// c. Return promiseCapability.[[Promise]]. // c. Return promiseCapability.[[Promise]].
return Ok(promise_capability.promise().clone().into()); return Ok(promise_capability.promise().clone().into());
} }
// 8. If value is present, then // 9. If value is present, then
(Some(r#return), Some(value)) => { (Some(r#return), Some(value)) => {
// a. Let result be Completion(Call(return, syncIterator, « value »)). // a. Let result be Completion(Call(return, syncIterator, « value »)).
r#return.call(&sync_iterator.clone().into(), &[value.clone()], context) r#return.call(&sync_iterator.clone().into(), &[value.clone()], context)
} }
// 9. Else, // 10. Else,
(Some(r#return), None) => { (Some(r#return), None) => {
// a. Let result be Completion(Call(return, syncIterator)). // a. Let result be Completion(Call(return, syncIterator)).
r#return.call(&sync_iterator.clone().into(), &[], context) 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 »). // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
let result = result.and_then(IteratorResult::from_value); 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); let result = if_abrupt_reject_promise!(result, promise_capability, context);
// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability). // 13. Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, false).
Self::continuation(&result, &promise_capability, context) Self::continuation(
&result,
&promise_capability,
sync_iterator_record,
false,
context,
)
} }
/// `%AsyncFromSyncIteratorPrototype%.throw ( [ value ] )` /// `%AsyncFromSyncIteratorPrototype%.throw ( [ value ] )`
@ -212,14 +225,15 @@ impl AsyncFromSyncIterator {
fn throw(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { fn throw(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
// 1. Let O be the this value. // 1. Let O be the this value.
// 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot. // 2. Assert: O is an Object that has a [[SyncIteratorRecord]] internal slot.
// 4. Let syncIterator be O.[[SyncIteratorRecord]].[[Iterator]]. // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
let sync_iterator = this let sync_iterator_record = this
.as_object() .as_object()
.and_then(JsObject::downcast_ref::<Self>) .and_then(JsObject::downcast_ref::<Self>)
.expect("async from sync iterator prototype must be object") .expect("async from sync iterator prototype must be object")
.sync_iterator_record .sync_iterator_record
.iterator()
.clone(); .clone();
// 5. Let syncIterator be syncIteratorRecord.[[Iterator]].
let sync_iterator = sync_iterator_record.iterator().clone();
// 3. Let promiseCapability be ! NewPromiseCapability(%Promise%). // 3. Let promiseCapability be ! NewPromiseCapability(%Promise%).
let promise_capability = PromiseCapability::new( let promise_capability = PromiseCapability::new(
@ -228,49 +242,67 @@ impl AsyncFromSyncIterator {
) )
.expect("cannot fail with promise constructor"); .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); 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 throw = if_abrupt_reject_promise!(throw, promise_capability, context);
let result = match (throw, args.first()) { let result = match (throw, args.first()) {
// 7. If throw is undefined, then // 8. If throw is undefined, then
(None, _) => { (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 promise_capability
.reject() .reject()
.call( .call(
&JsValue::Undefined, &JsValue::undefined(),
&[args.get_or_undefined(0).clone()], &[JsNativeError::typ()
.with_message("sync iterator does not have a throw method")
.to_opaque(context)
.into()],
context, context,
) )
.expect("cannot fail according to spec"); .expect("cannot fail according to spec");
// b. Return promiseCapability.[[Promise]]. // h. Return promiseCapability.[[Promise]].
return Ok(promise_capability.promise().clone().into()); return Ok(promise_capability.promise().clone().into());
} }
// 8. If value is present, then // 9. If value is present, then
(Some(throw), Some(value)) => { (Some(throw), Some(value)) => {
// a. Let result be Completion(Call(throw, syncIterator, « value »)). // a. Let result be Completion(Call(throw, syncIterator, « value »)).
throw.call(&sync_iterator.clone().into(), &[value.clone()], context) throw.call(&sync_iterator.clone().into(), &[value.clone()], context)
} }
// 9. Else, // 10. Else,
(Some(throw), None) => { (Some(throw), None) => {
// a. Let result be Completion(Call(throw, syncIterator)). // a. Let result be Completion(Call(throw, syncIterator)).
throw.call(&sync_iterator.clone().into(), &[], context) 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 »). // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
let result = result.and_then(IteratorResult::from_value); 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); let result = if_abrupt_reject_promise!(result, promise_capability, context);
// 12. Return AsyncFromSyncIteratorContinuation(result, promiseCapability). // 13. Return Return AsyncFromSyncIteratorContinuation(result, promiseCapability, syncIteratorRecord, true).
Self::continuation(&result, &promise_capability, context) Self::continuation(
&result,
&promise_capability,
sync_iterator_record,
true,
context,
)
} }
/// `AsyncFromSyncIteratorContinuation ( result, promiseCapability )` /// `AsyncFromSyncIteratorContinuation ( result, promiseCapability )`
@ -282,6 +314,8 @@ impl AsyncFromSyncIterator {
fn continuation( fn continuation(
result: &IteratorResult, result: &IteratorResult,
promise_capability: &PromiseCapability, promise_capability: &PromiseCapability,
sync_iterator_record: IteratorRecord,
close_on_rejection: bool,
context: &mut Context, context: &mut Context,
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. NOTE: Because promiseCapability is derived from the intrinsic %Promise%, // 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); let value = if_abrupt_reject_promise!(value, promise_capability, context);
// 6. Let valueWrapper be Completion(PromiseResolve(%Promise%, value)). // 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(), &context.intrinsics().constructors().promise().constructor(),
value, value,
context, 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); 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: // 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( let on_fulfilled = FunctionObjectBuilder::new(
context.realm(), context.realm(),
NativeFunction::from_copy_closure(move |_this, args, context| { NativeFunction::from_copy_closure(move |_this, args, context| {
@ -328,20 +372,51 @@ impl AsyncFromSyncIterator {
.length(1) .length(1)
.build(); .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 // 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. // 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( Promise::perform_promise_then(
&value_wrapper, &value_wrapper,
Some(on_fulfilled), Some(on_fulfilled),
None, on_rejected,
Some(promise_capability.clone()), Some(promise_capability.clone()),
context, context,
); );
// 12. Return promiseCapability.[[Promise]]. // 15. Return promiseCapability.[[Promise]].
Ok(promise_capability.promise().clone().into()) Ok(promise_capability.promise().clone().into())
} }
} }

Loading…
Cancel
Save