Skip to content

Commit b14ff35

Browse files
committed
Make JsAsyncGenerator fully type safe
1 parent 5acb570 commit b14ff35

File tree

2 files changed

+95
-56
lines changed

2 files changed

+95
-56
lines changed

core/engine/src/builtins/async_generator/mod.rs

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::{
2626
vm::{CompletionRecord, GeneratorResumeKind},
2727
};
2828
use boa_gc::{Finalize, Trace};
29-
use std::collections::VecDeque;
29+
use std::{collections::VecDeque, slice};
3030

3131
use super::{BuiltInBuilder, IntrinsicObject};
3232

@@ -143,6 +143,21 @@ impl AsyncGenerator {
143143
});
144144
let generator = if_abrupt_reject_promise!(result, promise_capability, context);
145145

146+
Self::inner_next(
147+
&generator,
148+
promise_capability,
149+
args.get_or_undefined(0).clone(),
150+
context,
151+
)
152+
.map(JsValue::from)
153+
}
154+
155+
pub(crate) fn inner_next(
156+
generator: &JsObject<AsyncGenerator>,
157+
cap: PromiseCapability,
158+
value: JsValue,
159+
context: &mut Context,
160+
) -> JsResult<JsObject> {
146161
// 5. Let state be generator.[[AsyncGeneratorState]].
147162
let state = generator.borrow().data().state;
148163

@@ -152,21 +167,18 @@ impl AsyncGenerator {
152167
let iterator_result = create_iter_result_object(JsValue::undefined(), true, context);
153168

154169
// b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
155-
promise_capability.resolve().call(
156-
&JsValue::undefined(),
157-
&[iterator_result],
158-
context,
159-
)?;
170+
cap.resolve()
171+
.call(&JsValue::undefined(), &[iterator_result], context)?;
160172

161173
// c. Return promiseCapability.[[Promise]].
162-
return Ok(promise_capability.promise().clone().into());
174+
return Ok(cap.promise);
163175
}
164176

165177
// 7. Let completion be NormalCompletion(value).
166-
let completion = CompletionRecord::Normal(args.get_or_undefined(0).clone());
178+
let completion = CompletionRecord::Normal(value);
167179

168180
// 8. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
169-
Self::enqueue(&generator, completion.clone(), promise_capability.clone());
181+
Self::enqueue(&generator, completion.clone(), cap.clone());
170182

171183
// 9. If state is either suspendedStart or suspendedYield, then
172184
if state == AsyncGeneratorState::SuspendedStart
@@ -177,7 +189,7 @@ impl AsyncGenerator {
177189
}
178190

179191
// 11. Return promiseCapability.[[Promise]].
180-
Ok(promise_capability.promise().clone().into())
192+
Ok(cap.promise)
181193
}
182194

183195
/// `AsyncGenerator.prototype.return ( value )`
@@ -216,12 +228,26 @@ impl AsyncGenerator {
216228
});
217229
let generator = if_abrupt_reject_promise!(result, promise_capability, context);
218230

231+
Self::inner_return(
232+
&generator,
233+
promise_capability,
234+
args.get_or_undefined(0).clone(),
235+
context,
236+
)
237+
.map(JsValue::from)
238+
}
239+
240+
pub(crate) fn inner_return(
241+
generator: &JsObject<AsyncGenerator>,
242+
cap: PromiseCapability,
243+
return_value: JsValue,
244+
context: &mut Context,
245+
) -> JsResult<JsObject> {
219246
// 5. Let completion be Completion Record { [[Type]]: return, [[Value]]: value, [[Target]]: empty }.
220-
let return_value = args.get_or_undefined(0).clone();
221247
let completion = CompletionRecord::Return(return_value.clone());
222248

223249
// 6. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
224-
Self::enqueue(&generator, completion.clone(), promise_capability.clone());
250+
Self::enqueue(&generator, completion.clone(), cap.clone());
225251

226252
// 7. Let state be generator.[[AsyncGeneratorState]].
227253
let state = generator.borrow().data().state;
@@ -243,7 +269,7 @@ impl AsyncGenerator {
243269
// a. Assert: state is either executing or draining-queue.
244270

245271
// 11. Return promiseCapability.[[Promise]].
246-
Ok(promise_capability.promise().clone().into())
272+
Ok(cap.promise)
247273
}
248274

249275
/// `AsyncGenerator.prototype.throw ( exception )`
@@ -281,6 +307,21 @@ impl AsyncGenerator {
281307
.into()
282308
});
283309
let generator = if_abrupt_reject_promise!(result, promise_capability, context);
310+
Self::inner_throw(
311+
&generator,
312+
promise_capability,
313+
args.get_or_undefined(0).clone(),
314+
context,
315+
)
316+
.map(JsValue::from)
317+
}
318+
319+
pub(crate) fn inner_throw(
320+
generator: &JsObject<AsyncGenerator>,
321+
cap: PromiseCapability,
322+
error_value: JsValue,
323+
context: &mut Context,
324+
) -> JsResult<JsObject> {
284325
let mut r#gen = generator.borrow_mut();
285326

286327
// 5. Let state be generator.[[AsyncGeneratorState]].
@@ -301,22 +342,21 @@ impl AsyncGenerator {
301342
// 7. If state is completed, then
302343
if state == AsyncGeneratorState::Completed {
303344
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »).
304-
promise_capability.reject().call(
345+
cap.reject().call(
305346
&JsValue::undefined(),
306-
&[args.get_or_undefined(0).clone()],
347+
slice::from_ref(&error_value),
307348
context,
308349
)?;
309350

310351
// b. Return promiseCapability.[[Promise]].
311-
return Ok(promise_capability.promise().clone().into());
352+
return Ok(cap.promise().clone().into());
312353
}
313354

314355
// 8. Let completion be ThrowCompletion(exception).
315-
let completion =
316-
CompletionRecord::Throw(JsError::from_opaque(args.get_or_undefined(0).clone()));
356+
let completion = CompletionRecord::Throw(JsError::from_opaque(error_value));
317357

318358
// 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability).
319-
Self::enqueue(&generator, completion.clone(), promise_capability.clone());
359+
Self::enqueue(&generator, completion.clone(), cap.clone());
320360

321361
// 10. If state is suspended-yield, then
322362
if state == AsyncGeneratorState::SuspendedYield {
@@ -328,7 +368,7 @@ impl AsyncGenerator {
328368
// a. Assert: state is either executing or draining-queue.
329369

330370
// 12. Return promiseCapability.[[Promise]].
331-
Ok(promise_capability.promise().clone().into())
371+
Ok(cap.promise().clone().into())
332372
}
333373

334374
/// `AsyncGeneratorEnqueue ( generator, completion, promiseCapability )`

core/engine/src/object/builtins/jsasyncgenerator.rs

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
//! A Rust API wrapper for Boa's `AsyncGenerator` Builtin ECMAScript Object
22
use super::JsPromise;
33
use crate::{
4-
Context, JsNativeError, JsResult, JsValue, builtins::async_generator::AsyncGenerator,
5-
object::JsObject, value::TryFromJs,
4+
Context, JsNativeError, JsResult, JsValue,
5+
builtins::{async_generator::AsyncGenerator, promise::PromiseCapability},
6+
js_error,
7+
object::JsObject,
8+
value::TryFromJs,
69
};
710
use boa_gc::{Finalize, Trace};
811
use std::ops::Deref;
912

1013
/// `JsAsyncGenerator` provides a wrapper for Boa's implementation of the ECMAScript `AsyncGenerator` builtin object.
1114
#[derive(Debug, Clone, Trace, Finalize)]
15+
#[boa_gc(unsafe_no_drop)]
1216
pub struct JsAsyncGenerator {
13-
inner: JsObject,
17+
inner: JsObject<AsyncGenerator>,
1418
}
1519

1620
impl JsAsyncGenerator {
@@ -22,13 +26,10 @@ impl JsAsyncGenerator {
2226
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator
2327
#[inline]
2428
pub fn from_object(object: JsObject) -> JsResult<Self> {
25-
if object.is::<AsyncGenerator>() {
26-
Ok(Self { inner: object })
27-
} else {
28-
Err(JsNativeError::typ()
29-
.with_message("object is not an AsyncGenerator")
30-
.into())
31-
}
29+
object
30+
.downcast::<AsyncGenerator>()
31+
.map(|inner| Self { inner })
32+
.map_err(|_| js_error!(TypeError: "object is not an AsyncGenerator object"))
3233
}
3334

3435
/// Calls `AsyncGenerator.prototype.next()`.
@@ -41,14 +42,14 @@ impl JsAsyncGenerator {
4142
where
4243
T: Into<JsValue>,
4344
{
44-
let value = AsyncGenerator::next(&self.inner.clone().into(), &[value.into()], context)?;
45-
let obj = value
46-
.as_object()
47-
.ok_or_else(|| {
48-
JsNativeError::typ().with_message("async generator did not return a Promise")
49-
})?
50-
.clone();
51-
JsPromise::from_object(obj)
45+
let (typed_promise, functions) = JsPromise::new_pending(context);
46+
let capability = PromiseCapability {
47+
functions,
48+
promise: (&*typed_promise).clone().upcast(),
49+
};
50+
AsyncGenerator::inner_next(&self.inner, capability, value.into(), context)?;
51+
52+
Ok(typed_promise)
5253
}
5354

5455
/// Calls `AsyncGenerator.prototype.return()`.
@@ -61,14 +62,13 @@ impl JsAsyncGenerator {
6162
where
6263
T: Into<JsValue>,
6364
{
64-
let value = AsyncGenerator::r#return(&self.inner.clone().into(), &[value.into()], context)?;
65-
let obj = value
66-
.as_object()
67-
.ok_or_else(|| {
68-
JsNativeError::typ().with_message("async generator did not return a Promise")
69-
})?
70-
.clone();
71-
JsPromise::from_object(obj)
65+
let (typed_promise, functions) = JsPromise::new_pending(context);
66+
let capability = PromiseCapability {
67+
functions,
68+
promise: (&*typed_promise).clone().upcast(),
69+
};
70+
AsyncGenerator::inner_return(&self.inner, capability, value.into(), context)?;
71+
Ok(typed_promise)
7272
}
7373

7474
/// Calls `AsyncGenerator.prototype.throw()`.
@@ -81,21 +81,20 @@ impl JsAsyncGenerator {
8181
where
8282
T: Into<JsValue>,
8383
{
84-
let value = AsyncGenerator::throw(&self.inner.clone().into(), &[value.into()], context)?;
85-
let obj = value
86-
.as_object()
87-
.ok_or_else(|| {
88-
JsNativeError::typ().with_message("async generator did not return a Promise")
89-
})?
90-
.clone();
91-
JsPromise::from_object(obj)
84+
let (typed_promise, functions) = JsPromise::new_pending(context);
85+
let capability = PromiseCapability {
86+
functions,
87+
promise: (&*typed_promise).clone().upcast(),
88+
};
89+
AsyncGenerator::inner_throw(&self.inner, capability, value.into(), context)?;
90+
Ok(typed_promise)
9291
}
9392
}
9493

9594
impl From<JsAsyncGenerator> for JsObject {
9695
#[inline]
9796
fn from(o: JsAsyncGenerator) -> Self {
98-
o.inner.clone()
97+
o.inner.upcast()
9998
}
10099
}
101100

@@ -107,7 +106,7 @@ impl From<JsAsyncGenerator> for JsValue {
107106
}
108107

109108
impl Deref for JsAsyncGenerator {
110-
type Target = JsObject;
109+
type Target = JsObject<AsyncGenerator>;
111110

112111
#[inline]
113112
fn deref(&self) -> &Self::Target {

0 commit comments

Comments
 (0)