Skip to content
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
27 changes: 4 additions & 23 deletions nova_vm/src/ecmascript/builtins/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ use crate::{
},
engine::{Bindable, GcScope, NoGcScope},
heap::{
CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, HeapSweepWeakReference,
WorkQueues, arena_vec_access, {BaseIndex, HeapIndexHandle},
{ElementArrays, ElementDescriptor, ElementStorageMut, ElementStorageRef, ElementsVector},
ArenaAccessSoA, ArenaAccessSoAMut, BaseIndex, CompactionLists, CreateHeapData,
ElementArrays, ElementDescriptor, ElementStorageMut, ElementStorageRef, ElementsVector,
Heap, HeapIndexHandle, HeapMarkAndSweep, HeapSweepWeakReference, WorkQueues,
arena_vec_access,
},
};

Expand Down Expand Up @@ -111,26 +112,6 @@ impl<'a> Array<'a> {
elems.reserve(elements, elems.len().saturating_add(additional))
}

pub(crate) fn get<'agent>(
self,
agent: &'agent impl AsRef<SoAVec<ArrayHeapData<'static>>>,
) -> ArrayHeapDataRef<'agent, 'a> {
agent
.as_ref()
.get(self.0.get_index_u32())
.expect("Invalid Array reference")
}

pub(crate) fn get_mut<'agent>(
self,
agent: &'agent mut impl AsMut<SoAVec<ArrayHeapData<'static>>>,
) -> ArrayHeapDataMut<'agent, 'static> {
agent
.as_mut()
.get_mut(self.0.get_index_u32())
.expect("Invalid Array reference")
}

pub(crate) fn get_elements(
self,
agent: &impl AsRef<SoAVec<ArrayHeapData<'static>>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
},
heap::{
ArenaAccess, ArenaAccessMut, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep,
WorkQueues, arena_vec_access, {BaseIndex, HeapIndexHandle, index_handle},
WorkQueues, arena_vec_access, {BaseIndex, index_handle},
},
};

Expand All @@ -23,11 +23,6 @@ index_handle!(AwaitReaction);
arena_vec_access!(AwaitReaction, 'a, AwaitReactionRecord, await_reactions);

impl AwaitReaction<'_> {
pub(crate) fn last(scripts: &[AwaitReactionRecord]) -> Self {
let index = scripts.len() - 1;
Self::from_index(index)
}

pub(crate) fn resume(
self,
agent: &mut Agent,
Expand All @@ -38,16 +33,21 @@ impl AwaitReaction<'_> {
let reaction = self.bind(gc.nogc());
let value = value.bind(gc.nogc());
// [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await)
// 3. c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
// 3. c. Push asyncContext onto the execution context stack;
// asyncContext is now the running execution context.
let record = reaction.get_mut(agent);
let execution_context = record.execution_context.take().unwrap();
let vm = record.vm.take().unwrap();
let async_function = record.async_executable.unwrap().bind(gc.nogc());
agent.push_execution_context(execution_context);

let reaction = reaction.scope(agent, gc.nogc());
// 3. d. Resume the suspended evaluation of asyncContext using NormalCompletion(v) as the result of the operation that suspended it.
// 5. d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that suspended it.
// 3. d. Resume the suspended evaluation of asyncContext using
// NormalCompletion(v) as the result of the operation that
// suspended it.
// 5. d. Resume the suspended evaluation of asyncContext using
// ThrowCompletion(reason) as the result of the operation that
// suspended it.
let execution_result = match reaction_type {
PromiseReactionType::Fulfill => {
let executable = async_function.get_executable(agent).scope(agent, gc.nogc());
Expand All @@ -63,12 +63,17 @@ impl AwaitReaction<'_> {
}
};

// SAFETY: reaction is not shared.
let reaction = unsafe { reaction.take(agent) }.bind(gc.nogc());
match execution_result {
ExecutionResult::Return(result) => {
// SAFETY: reaction is not shared.
let reaction = unsafe { reaction.take(agent) }.bind(gc.nogc());

// [27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )](https://tc39.es/ecma262/#sec-asyncblockstart)
// 2. d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
//
// 2. d. Remove acAsyncContext from the execution context stack
// and restore the execution context that is at the top of
// the execution context stack as the running execution
// context.
agent.pop_execution_context();
// 2. e. If result is a normal completion, then
// i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »).
Expand All @@ -82,8 +87,15 @@ impl AwaitReaction<'_> {
.resolve(agent, result.unbind(), gc);
}
ExecutionResult::Throw(err) => {
// SAFETY: reaction is not shared.
let reaction = unsafe { reaction.take(agent) }.bind(gc.nogc());

// [27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )](https://tc39.es/ecma262/#sec-asyncblockstart)
// 2. d. Remove acAsyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
//
// 2. d. Remove acAsyncContext from the execution context stack
// and restore the execution context that is at the top of
// the execution context stack as the running execution
// context.
agent.pop_execution_context();
// 2. g. i. Assert: result is a throw completion.
// ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »).
Expand All @@ -95,21 +107,29 @@ impl AwaitReaction<'_> {
}
ExecutionResult::Await { vm, awaited_value } => {
// [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await)
// 8. Remove asyncContext from the execution context stack and restore the execution context that is at the top of the execution context stack as the running execution context.
// 8. Remove asyncContext from the execution context stack and
// restore the execution context that is at the top of the
// execution context stack as the running execution context.
let execution_context = agent.pop_execution_context().unwrap();
let data = reaction.get_mut(agent);
let data = reaction.get(agent).bind(gc.nogc()).get_mut(agent);
data.vm = Some(vm);
data.execution_context = Some(execution_context);

// `handler` corresponds to the `fulfilledClosure` and `rejectedClosure` functions,
// which resume execution of the function.
let handler = PromiseReactionHandler::Await(self);
// 2. Let promise be ? PromiseResolve(%Promise%, value).
let promise = Promise::resolve(agent, awaited_value.unbind(), gc.reborrow())
.unbind()
.bind(gc.nogc());
let promise =
Promise::resolve(agent, awaited_value.unbind(), gc.reborrow()).unbind();
let gc = gc.into_nogc();
let promise = promise.bind(gc);

// SAFETY: not shared.
let reaction = unsafe { reaction.take(agent) }.bind(gc);

// `handler` corresponds to the `fulfilledClosure` and
// `rejectedClosure` functions, which resume execution of the
// function.
let handler = PromiseReactionHandler::Await(reaction);
// 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
inner_promise_then(agent, promise, handler, handler, None, gc.nogc());
inner_promise_then(agent, promise, handler, handler, None, gc);
}
ExecutionResult::Yield { .. } => unreachable!(),
}
Expand Down Expand Up @@ -167,7 +187,7 @@ impl<'a> CreateHeapData<AwaitReactionRecord<'a>, AwaitReaction<'a>> for Heap {
fn create(&mut self, data: AwaitReactionRecord<'a>) -> AwaitReaction<'a> {
self.await_reactions.push(data.unbind());
self.alloc_counter += core::mem::size_of::<AwaitReactionRecord<'static>>();
AwaitReaction::last(&self.await_reactions)
AwaitReaction(BaseIndex::last(&self.await_reactions))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use crate::{
},
engine::{Bindable, GcScope, Scopable, bindable_handle},
heap::{
CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, HeapSweepWeakReference,
WorkQueues, arena_vec_access, {BaseIndex, HeapIndexHandle},
ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
HeapMarkAndSweep, HeapSweepWeakReference, WorkQueues, arena_vec_access,
},
};

Expand Down Expand Up @@ -69,22 +69,6 @@ impl<'f> BuiltinPromiseFinallyFunction<'f> {
});
(then_finally_closure, catch_finally_closure)
}

fn get(self, agent: &Agent) -> &PromiseFinallyFunctionHeapData<'f> {
agent
.heap
.promise_finally_functions
.get(self.get_index())
.expect("Promise.prototype.finally handler not found")
}

fn get_mut(self, agent: &mut Agent) -> &mut PromiseFinallyFunctionHeapData<'static> {
agent
.heap
.promise_finally_functions
.get_mut(self.get_index())
.expect("Promise.prototype.finally handler not found")
}
}

impl<'a> FunctionInternalProperties<'a> for BuiltinPromiseFinallyFunction<'a> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use crate::{
ecmascript::{
Agent, Array, BUILTIN_STRING_MEMORY, ErrorHeapData, ExceptionType, OrdinaryObject, Promise,
PromiseCapability, PromiseReactionType, PropertyDescriptor, Value,
define_property_or_throw,
try_define_property_or_throw, unwrap_try,
},
engine::{Bindable, GcScope, NoGcScope, bindable_handle},
heap::{
CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, ObjectEntry, WorkQueues,
arena_vec_access, {BaseIndex, HeapIndexHandle, index_handle},
ArenaAccess, ArenaAccessMut, BaseIndex, CompactionLists, CreateHeapData, Heap,
HeapMarkAndSweep, ObjectEntry, WorkQueues, arena_vec_access, index_handle,
},
};

Expand All @@ -36,7 +36,7 @@ pub struct PromiseGroup<'a>(BaseIndex<'a, PromiseGroupRecord<'static>>);
index_handle!(PromiseGroup);
arena_vec_access!(PromiseGroup, 'a, PromiseGroupRecord, promise_group_records);

impl<'a> PromiseGroupRecord<'static> {
impl<'a> PromiseGroupRecord<'a> {
fn take_result_and_promise(&mut self) -> (Array<'a>, Option<Promise<'a>>) {
self.remaining_elements_count = self.remaining_elements_count.saturating_sub(1);

Expand All @@ -57,56 +57,67 @@ impl<'a> PromiseGroup<'a> {
value: Value<'a>,
mut gc: GcScope<'a, '_>,
) {
let group = self.bind(gc.nogc());
let value = value.bind(gc.nogc());
let record = self.get(agent);
let record = group.get(agent);

match record.promise_group_type {
PromiseGroupType::All => match reaction_type {
PromiseReactionType::Fulfill => {
self.fulfill(agent, index, value.unbind(), gc.reborrow());
group
.unbind()
.fulfill(agent, index, value.unbind(), gc.reborrow());
}
PromiseReactionType::Reject => {
self.immediately_reject(agent, value.unbind(), gc.nogc());
group
.unbind()
.immediately_reject(agent, value.unbind(), gc.nogc());
}
},
PromiseGroupType::AllSettled => {
let obj = self
.to_all_settled_obj(agent, reaction_type, value.unbind(), gc.nogc())
.bind(gc.nogc());
self.fulfill(agent, index, obj.unbind(), gc.reborrow());
let obj = PromiseGroup::to_all_settled_obj(agent, reaction_type, value, gc.nogc());
group
.unbind()
.fulfill(agent, index, obj.unbind(), gc.reborrow());
}
PromiseGroupType::Any => match reaction_type {
PromiseReactionType::Fulfill => {
self.immediately_resolve(agent, value.unbind(), gc.reborrow());
group
.unbind()
.immediately_resolve(agent, value.unbind(), gc.reborrow());
}
PromiseReactionType::Reject => {
self.reject(agent, index, value.unbind(), gc.reborrow());
group
.unbind()
.reject(agent, index, value.unbind(), gc.into_nogc());
}
},
}
}

fn fulfill(self, agent: &mut Agent, index: u32, value: Value<'a>, mut gc: GcScope<'a, '_>) {
let promise_group = self.bind(gc.nogc());
let group = self.bind(gc.nogc());
let value = value.bind(gc.nogc());

let promise_group_record = promise_group.get_mut(agent);
let promise_group_record = group.get_mut(agent);
let (result_array, promise_to_resolve) = promise_group_record.take_result_and_promise();

let elements = result_array.as_mut_slice(agent);
elements[index as usize] = Some(value.unbind());

if let Some(promise_to_resolve) = promise_to_resolve {
promise_group.pop_empty_records(agent);
group.pop_empty_records(agent);

let capability = PromiseCapability::from_promise(promise_to_resolve, true);
capability.resolve(agent, result_array.unbind().into(), gc.reborrow());
capability
.unbind()
.resolve(agent, result_array.unbind().into(), gc.reborrow());
}
}

fn reject(self, agent: &mut Agent, index: u32, error: Value<'a>, mut gc: GcScope<'a, '_>) {
let promise_group = self.bind(gc.nogc());
let error = error.bind(gc.nogc());
fn reject(self, agent: &mut Agent, index: u32, error: Value<'a>, gc: NoGcScope<'a, '_>) {
let promise_group = self.bind(gc);
let error = error.bind(gc);

let promise_group_record = promise_group.get_mut(agent);
let (result_array, promise_to_resolve) = promise_group_record.take_result_and_promise();
Expand All @@ -117,14 +128,17 @@ impl<'a> PromiseGroup<'a> {
if let Some(promise_to_resolve) = promise_to_resolve {
promise_group.pop_empty_records(agent);

let aggregate_error = agent.heap.create(ErrorHeapData::new(
ExceptionType::AggregateError,
None,
None,
));
let aggregate_error = agent
.heap
.create(ErrorHeapData::new(
ExceptionType::AggregateError,
None,
None,
))
.bind(gc);

let capability = PromiseCapability::from_promise(promise_to_resolve, true);
define_property_or_throw(
unwrap_try(try_define_property_or_throw(
agent,
aggregate_error,
BUILTIN_STRING_MEMORY.errors.into(),
Expand All @@ -136,11 +150,11 @@ impl<'a> PromiseGroup<'a> {
enumerable: Some(true),
configurable: Some(true),
},
gc.reborrow(),
)
.unwrap();
None,
gc,
));

capability.reject(agent, aggregate_error.into(), gc.nogc());
capability.reject(agent, aggregate_error.into(), gc);
}
}

Expand All @@ -152,7 +166,7 @@ impl<'a> PromiseGroup<'a> {
let capability = PromiseCapability::from_promise(data.promise, true);

promise_group.pop_empty_records(agent);
capability.resolve(agent, value.unbind(), gc);
capability.unbind().resolve(agent, value.unbind(), gc);
}

fn immediately_reject(self, agent: &mut Agent, value: Value<'a>, gc: NoGcScope<'a, '_>) {
Expand All @@ -167,7 +181,6 @@ impl<'a> PromiseGroup<'a> {
}

fn to_all_settled_obj(
self,
agent: &mut Agent,
reaction_type: PromiseReactionType,
value: Value<'a>,
Expand Down Expand Up @@ -209,22 +222,6 @@ impl<'a> PromiseGroup<'a> {
obj.unbind().into()
}

pub(crate) fn get(self, agent: &Agent) -> &PromiseGroupRecord<'a> {
agent
.heap
.promise_group_records
.get(self.get_index())
.expect("PromiseGroupRecord not found")
}

pub(crate) fn get_mut(self, agent: &mut Agent) -> &mut PromiseGroupRecord<'static> {
agent
.heap
.promise_group_records
.get_mut(self.get_index())
.expect("PromiseGroupRecord not found")
}

fn pop_empty_records(self, agent: &mut Agent) {
while let Some(last) = agent.heap.promise_group_records.last() {
if last.remaining_elements_count == 0 {
Expand Down
Loading