Skip to content

Commit

Permalink
feat(vm): Reimplement FunctionDeclarationInstantiation as part of fun…
Browse files Browse the repository at this point in the history
…ction compilation

FunctionDeclarationInstantiation is an abstract operation in the spec
which runs at the start of a function call and sets up the parameters,
lexical environments and variables for the function. Until now, we
implemented it as a Rust function called before the function body was
executed.

Since this operation is not implemented using bytecode, and it binds
the function arguments, it couldn't support complex bindings (such as
default values or object/array bindings) without reimplementing them.

This patch therefore instead implements this operation as bytecode
instructions emitted before the function body. That way the binding of
arguments can be compiled into a regular array binding pattern.

Doing this requires some way to pass the arguments into the VM, and
have them stored in its state until they are evaluated. We do this by
having `Vm::execute` take an optional arguments slice, which it stores
on the iterator stack.

For generators, currently FunctionDeclarationInstantiation happens
when the generator function is called, and this patch moves it to when
the generator object is first resumed, which is correct. However, this
means that the arguments must be stored until that first time the
generator is resumed. To do this, the `Option<Vm>` field in
`GeneratorState::Suspended` is made into an enum which can store
either a `Vm` or a boxed slice of arguments.
  • Loading branch information
andreubotella committed Aug 25, 2024
1 parent f9aeadf commit b94dc2e
Show file tree
Hide file tree
Showing 13 changed files with 713 additions and 1,349 deletions.
8 changes: 4 additions & 4 deletions nova_vm/src/ecmascript/builtins/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ use crate::{
},
execution::{agent::Agent, ProtoIntrinsics},
types::{
IntoFunction, IntoValue, Number, Object, PropertyDescriptor, PropertyKey,
IntoFunction, IntoValue, Number, Object, PropertyDescriptor, PropertyKey, Value,
BUILTIN_STRING_MEMORY,
},
},
heap::WellKnownSymbolIndexes,
};

use super::{ordinary::ordinary_object_create_with_intrinsics, ArgumentsList};
use super::ordinary::ordinary_object_create_with_intrinsics;

// 10.4.4.1 [[GetOwnProperty]] ( P )

Expand Down Expand Up @@ -122,7 +122,7 @@ use super::{ordinary::ordinary_object_create_with_intrinsics, ArgumentsList};
/// ordinary object.
pub(crate) fn create_unmapped_arguments_object(
agent: &mut Agent,
arguments_list: ArgumentsList,
arguments_list: &[Value],
) -> Object {
// 1. Let len be the number of elements in argumentsList.
let len = arguments_list.len();
Expand Down Expand Up @@ -154,7 +154,7 @@ pub(crate) fn create_unmapped_arguments_object(
.unwrap();
// 5. Let index be 0.
// 6. Repeat, while index < len,
for (index, val) in arguments_list.0.iter().enumerate() {
for (index, val) in arguments_list.iter().enumerate() {
// a. Let val be argumentsList[index].
// b. Perform ! CreateDataPropertyOrThrow(obj, ! ToString(𝔽(index)), val).
debug_assert!(index < u32::MAX as usize);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ impl Generator {

// 7. Set generator.[[GeneratorState]] to executing.
let Some(GeneratorState::Suspended {
vm,
vm_or_args,
executable,
execution_context,
}) = agent[self]
Expand All @@ -76,9 +76,9 @@ impl Generator {
// 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as the
// result of the operation that suspended it. Let result be the value returned by the
// resumed computation.
let execution_result = match vm {
None => Vm::execute(agent, &executable),
Some(vm) => vm.resume(agent, &executable, value),
let execution_result = match vm_or_args {
VmOrArguments::Arguments(args) => Vm::execute(agent, &executable, Some(&args)),
VmOrArguments::Vm(vm) => vm.resume(agent, &executable, value),
};

// GeneratorStart: 4.f. Remove acGenContext from the execution context stack and restore the
Expand Down Expand Up @@ -124,7 +124,7 @@ impl Generator {
// 3. Let generator be the value of the Generator component of genContext.
// 5. Set generator.[[GeneratorState]] to suspended-yield.
agent[self].generator_state = Some(GeneratorState::Suspended {
vm: Some(vm),
vm_or_args: VmOrArguments::Vm(vm),
executable,
execution_context,
});
Expand All @@ -141,7 +141,10 @@ impl Generator {
pub(crate) fn resume_throw(self, agent: &mut Agent, value: Value) -> JsResult<Object> {
// 1. Let state be ? GeneratorValidate(generator, generatorBrand).
match agent[self].generator_state.as_ref().unwrap() {
GeneratorState::Suspended { vm: None, .. } => {
GeneratorState::Suspended {
vm_or_args: VmOrArguments::Arguments(_),
..
} => {
// 2. If state is suspended-start, then
// a. Set generator.[[GeneratorState]] to completed.
// b. NOTE: Once a generator enters the completed state it never leaves it and its
Expand Down Expand Up @@ -172,7 +175,7 @@ impl Generator {

// 8. Set generator.[[GeneratorState]] to executing.
let Some(GeneratorState::Suspended {
vm: Some(vm),
vm_or_args: VmOrArguments::Vm(vm),
executable,
execution_context,
}) = agent[self]
Expand Down Expand Up @@ -215,7 +218,7 @@ impl Generator {
}
ExecutionResult::Yield { vm, yielded_value } => {
agent[self].generator_state = Some(GeneratorState::Suspended {
vm: Some(vm),
vm_or_args: VmOrArguments::Vm(vm),
executable,
execution_context,
});
Expand Down Expand Up @@ -344,11 +347,17 @@ pub struct GeneratorHeapData {
pub(crate) generator_state: Option<GeneratorState>,
}

#[derive(Debug)]
pub(crate) enum VmOrArguments {
Vm(Vm),
Arguments(Box<[Value]>),
}

#[derive(Debug)]
pub(crate) enum GeneratorState {
// SUSPENDED-START has `vm` set to None, SUSPENDED-YIELD has it set to Some.
// SUSPENDED-START has `vm_or_args` set to Arguments, SUSPENDED-YIELD has it set to Vm.
Suspended {
vm: Option<Vm>,
vm_or_args: VmOrArguments,
executable: Executable,
execution_context: ExecutionContext,
},
Expand All @@ -360,12 +369,15 @@ impl HeapMarkAndSweep for GeneratorHeapData {
fn mark_values(&self, queues: &mut WorkQueues) {
self.object_index.mark_values(queues);
if let Some(GeneratorState::Suspended {
vm,
vm_or_args,
executable,
execution_context,
}) = &self.generator_state
{
vm.mark_values(queues);
match vm_or_args {
VmOrArguments::Vm(vm) => vm.mark_values(queues),
VmOrArguments::Arguments(args) => args.as_ref().mark_values(queues),
}
executable.mark_values(queues);
execution_context.mark_values(queues);
}
Expand All @@ -374,12 +386,15 @@ impl HeapMarkAndSweep for GeneratorHeapData {
fn sweep_values(&mut self, compactions: &CompactionLists) {
self.object_index.sweep_values(compactions);
if let Some(GeneratorState::Suspended {
vm,
vm_or_args,
executable,
execution_context,
}) = &mut self.generator_state
{
vm.sweep_values(compactions);
match vm_or_args {
VmOrArguments::Vm(vm) => vm.sweep_values(compactions),
VmOrArguments::Arguments(args) => args.as_ref().sweep_values(compactions),
}
executable.sweep_values(compactions);
execution_context.sweep_values(compactions);
}
Expand Down
Loading

0 comments on commit b94dc2e

Please sign in to comment.