From d2b2a1a78c440baf0f34c1a58206e63f13495ff3 Mon Sep 17 00:00:00 2001 From: Haled Odat <8566042+HalidOdat@users.noreply.github.com> Date: Thu, 27 Jul 2023 18:41:18 +0200 Subject: [PATCH] Rename opcodes --- .../src/builtins/async_generator/mod.rs | 33 --- boa_engine/src/bytecompiler/jump_control.rs | 4 +- boa_engine/src/vm/code_block.rs | 4 +- boa_engine/src/vm/flowgraph/mod.rs | 4 +- .../src/vm/opcode/{await_stm => await}/mod.rs | 68 ++++++ .../{jump/mod.rs => control_flow/jump.rs} | 0 boa_engine/src/vm/opcode/control_flow/mod.rs | 2 + .../src/vm/opcode/control_flow/return.rs | 194 +----------------- boa_engine/src/vm/opcode/generator/mod.rs | 156 +++++++++++++- boa_engine/src/vm/opcode/mod.rs | 19 +- 10 files changed, 240 insertions(+), 244 deletions(-) rename boa_engine/src/vm/opcode/{await_stm => await}/mod.rs (67%) rename boa_engine/src/vm/opcode/{jump/mod.rs => control_flow/jump.rs} (100%) diff --git a/boa_engine/src/builtins/async_generator/mod.rs b/boa_engine/src/builtins/async_generator/mod.rs index b440ac375e0..22cc8f532a6 100644 --- a/boa_engine/src/builtins/async_generator/mod.rs +++ b/boa_engine/src/builtins/async_generator/mod.rs @@ -692,37 +692,4 @@ impl AsyncGenerator { } } } - - // Step 3.e-g in [AsyncGeneratorStart](https://tc39.es/ecma262/#sec-asyncgeneratorstart) - pub(crate) fn close_return(context: &mut Context<'_>) { - let generator_object = context - .vm - .frame() - .async_generator - .clone() - .expect("There should be a object"); - - let mut generator_object_mut = generator_object.borrow_mut(); - let generator = generator_object_mut - .as_async_generator_mut() - .expect("must be async generator"); - - generator.state = AsyncGeneratorState::Completed; - generator.context = None; - - let next = generator - .queue - .pop_front() - .expect("must have item in queue"); - drop(generator_object_mut); - - let return_value = std::mem::take(&mut context.vm.frame_mut().return_value); - - if let Some(error) = context.vm.pending_exception.take() { - Self::complete_step(&next, Err(error), true, None, context); - } else { - Self::complete_step(&next, Ok(return_value), true, None, context); - } - Self::drain_queue(&generator_object, context); - } } diff --git a/boa_engine/src/bytecompiler/jump_control.rs b/boa_engine/src/bytecompiler/jump_control.rs index ff51172f1d7..ff3d862cb61 100644 --- a/boa_engine/src/bytecompiler/jump_control.rs +++ b/boa_engine/src/bytecompiler/jump_control.rs @@ -124,8 +124,8 @@ impl JumpRecord { JumpRecordKind::Continue => compiler.patch_jump_with_target(self.label, start_address), JumpRecordKind::Return => { match (compiler.in_async(), compiler.in_generator()) { - (true, true) => compiler.emit_opcode(Opcode::AsyncGeneratorReturn), - (true, false) => compiler.emit_opcode(Opcode::PromiseCapability), + (true, true) => compiler.emit_opcode(Opcode::AsyncGeneratorClose), + (true, false) => compiler.emit_opcode(Opcode::CompletePromiseCapability), (_, _) => {} } compiler.emit_opcode(Opcode::Return); diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index a9e02c64dbc..80e9e6b9a67 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -570,9 +570,9 @@ impl CodeBlock { | Opcode::This | Opcode::Super | Opcode::Return - | Opcode::AsyncGeneratorReturn - | Opcode::PromiseCapability + | Opcode::AsyncGeneratorClose | Opcode::CreatePromiseCapability + | Opcode::CompletePromiseCapability | Opcode::PopEnvironment | Opcode::IncrementLoopIteration | Opcode::CreateForInIterator diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index 55865d101ea..9036d8b4426 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -523,9 +523,9 @@ impl CodeBlock { | Opcode::PushNewArray | Opcode::GeneratorYield | Opcode::AsyncGeneratorYield - | Opcode::AsyncGeneratorReturn - | Opcode::PromiseCapability + | Opcode::AsyncGeneratorClose | Opcode::CreatePromiseCapability + | Opcode::CompletePromiseCapability | Opcode::GeneratorNext | Opcode::PushClassField | Opcode::SuperCallDerived diff --git a/boa_engine/src/vm/opcode/await_stm/mod.rs b/boa_engine/src/vm/opcode/await/mod.rs similarity index 67% rename from boa_engine/src/vm/opcode/await_stm/mod.rs rename to boa_engine/src/vm/opcode/await/mod.rs index 348627d41d4..f952580c39b 100644 --- a/boa_engine/src/vm/opcode/await_stm/mod.rs +++ b/boa_engine/src/vm/opcode/await/mod.rs @@ -133,3 +133,71 @@ impl Operation for Await { Ok(CompletionType::Yield) } } + +/// `CreatePromiseCapability` implements the Opcode Operation for `Opcode::CreatePromiseCapability` +/// +/// Operation: +/// - Create a promise capacity for an async function, if not already set. +#[derive(Debug, Clone, Copy)] +pub(crate) struct CreatePromiseCapability; + +impl Operation for CreatePromiseCapability { + const NAME: &'static str = "CreatePromiseCapability"; + const INSTRUCTION: &'static str = "INST - CreatePromiseCapability"; + + fn execute(context: &mut Context<'_>) -> JsResult { + if context.vm.frame().promise_capability.is_some() { + return Ok(CompletionType::Normal); + } + + let promise_capability = crate::builtins::promise::PromiseCapability::new( + &context.intrinsics().constructors().promise().constructor(), + context, + ) + .expect("cannot fail per spec"); + + context.vm.frame_mut().promise_capability = Some(promise_capability); + Ok(CompletionType::Normal) + } +} + +/// `CompletePromiseCapability` implements the Opcode Operation for `Opcode::CompletePromiseCapability` +/// +/// Operation: +/// - Resolves or rejects the promise capability, depending if the pending exception is set. +#[derive(Debug, Clone, Copy)] +pub(crate) struct CompletePromiseCapability; + +impl Operation for CompletePromiseCapability { + const NAME: &'static str = "CompletePromiseCapability"; + const INSTRUCTION: &'static str = "INST - CompletePromiseCapability"; + + fn execute(context: &mut Context<'_>) -> JsResult { + // If the current executing function is an async function we have to resolve/reject it's promise at the end. + // The relevant spec section is 3. in [AsyncBlockStart](https://tc39.es/ecma262/#sec-asyncblockstart). + let Some(promise_capability) = context.vm.frame_mut().promise_capability.take() else { + return if context.vm.pending_exception.is_some() { + Ok(CompletionType::Throw) + } else { + Ok(CompletionType::Normal) + }; + }; + + if let Some(error) = context.vm.pending_exception.take() { + promise_capability + .reject() + .call(&JsValue::undefined(), &[error.to_opaque(context)], context) + .expect("cannot fail per spec"); + } else { + let return_value = context.vm.frame().return_value.clone(); + promise_capability + .resolve() + .call(&JsValue::undefined(), &[return_value], context) + .expect("cannot fail per spec"); + }; + + context.vm.frame_mut().return_value = promise_capability.promise().clone().into(); + + Ok(CompletionType::Normal) + } +} diff --git a/boa_engine/src/vm/opcode/jump/mod.rs b/boa_engine/src/vm/opcode/control_flow/jump.rs similarity index 100% rename from boa_engine/src/vm/opcode/jump/mod.rs rename to boa_engine/src/vm/opcode/control_flow/jump.rs diff --git a/boa_engine/src/vm/opcode/control_flow/mod.rs b/boa_engine/src/vm/opcode/control_flow/mod.rs index a3ac8d1488f..7f8ddc3340a 100644 --- a/boa_engine/src/vm/opcode/control_flow/mod.rs +++ b/boa_engine/src/vm/opcode/control_flow/mod.rs @@ -1,5 +1,7 @@ +pub(crate) mod jump; pub(crate) mod r#return; pub(crate) mod throw; +pub(crate) use jump::*; pub(crate) use r#return::*; pub(crate) use throw::*; diff --git a/boa_engine/src/vm/opcode/control_flow/return.rs b/boa_engine/src/vm/opcode/control_flow/return.rs index 7e4b9569627..17e88983906 100644 --- a/boa_engine/src/vm/opcode/control_flow/return.rs +++ b/boa_engine/src/vm/opcode/control_flow/return.rs @@ -1,14 +1,6 @@ -use std::collections::VecDeque; - use crate::{ - builtins::{ - async_generator::{AsyncGenerator, AsyncGeneratorState}, - generator::{GeneratorContext, GeneratorState}, - }, - environments::EnvironmentStack, - object::{ObjectData, PROTOTYPE}, - vm::{opcode::Operation, CallFrame, CompletionType}, - Context, JsObject, JsResult, JsValue, + vm::{opcode::Operation, CompletionType}, + Context, JsResult, }; /// `Return` implements the Opcode Operation for `Opcode::Return` @@ -27,188 +19,6 @@ impl Operation for Return { } } -/// `AsyncGeneratorReturn` implements the Opcode Operation for `Opcode::AsyncGeneratorReturn` -/// -/// Operation: -/// - Return from an async generator function. -#[derive(Debug, Clone, Copy)] -pub(crate) struct AsyncGeneratorReturn; - -impl Operation for AsyncGeneratorReturn { - const NAME: &'static str = "AsyncGeneratorReturn"; - const INSTRUCTION: &'static str = "INST - AsyncGeneratorReturn"; - - fn execute(context: &mut Context<'_>) -> JsResult { - AsyncGenerator::close_return(context); - Ok(CompletionType::Normal) - } -} - -/// `CreatePromiseCapability` implements the Opcode Operation for `Opcode::CreatePromiseCapability` -/// -/// Operation: -/// - Create a promise capacity for an async function. -#[derive(Debug, Clone, Copy)] -pub(crate) struct CreatePromiseCapability; - -impl Operation for CreatePromiseCapability { - const NAME: &'static str = "CreatePromiseCapability"; - const INSTRUCTION: &'static str = "INST - CreatePromiseCapability"; - - fn execute(context: &mut Context<'_>) -> JsResult { - if context.vm.frame().promise_capability.is_some() { - return Ok(CompletionType::Normal); - } - - let promise_capability = crate::builtins::promise::PromiseCapability::new( - &context.intrinsics().constructors().promise().constructor(), - context, - ) - .expect("cannot fail per spec"); - - context.vm.frame_mut().promise_capability = Some(promise_capability); - Ok(CompletionType::Normal) - } -} - -/// `PromiseCapability` implements the Opcode Operation for `Opcode::PromiseCapability` -/// -/// Operation: -/// - Resolves or rejects the promise capability, depending if the pending exception is set. -#[derive(Debug, Clone, Copy)] -pub(crate) struct PromiseCapability; - -impl Operation for PromiseCapability { - const NAME: &'static str = "PromiseCapability"; - const INSTRUCTION: &'static str = "INST - PromiseCapability"; - - fn execute(context: &mut Context<'_>) -> JsResult { - // If the current executing function is an async function we have to resolve/reject it's promise at the end. - // The relevant spec section is 3. in [AsyncBlockStart](https://tc39.es/ecma262/#sec-asyncblockstart). - let Some(promise_capability) = context.vm.frame_mut().promise_capability.take() else { - return if context.vm.pending_exception.is_some() { - Ok(CompletionType::Throw) - } else { - Ok(CompletionType::Normal) - }; - }; - - if let Some(error) = context.vm.pending_exception.take() { - promise_capability - .reject() - .call(&JsValue::undefined(), &[error.to_opaque(context)], context) - .expect("cannot fail per spec"); - } else { - let return_value = context.vm.frame().return_value.clone(); - promise_capability - .resolve() - .call(&JsValue::undefined(), &[return_value], context) - .expect("cannot fail per spec"); - }; - - context.vm.frame_mut().return_value = promise_capability.promise().clone().into(); - - Ok(CompletionType::Normal) - } -} - -/// `Generator` implements the Opcode Operation for `Opcode::Generator` -/// -/// Operation: -/// - TODO: doc -#[derive(Debug, Clone, Copy)] -pub(crate) struct Generator; - -impl Operation for Generator { - const NAME: &'static str = "Generator"; - const INSTRUCTION: &'static str = "INST - Generator"; - - fn execute(context: &mut Context<'_>) -> JsResult { - let r#async = context.vm.read::() != 0; - - let code_block = context.vm.frame().code_block().clone(); - let pc = context.vm.frame().pc; - let mut dummy_call_frame = CallFrame::new(code_block); - dummy_call_frame.pc = pc; - let call_frame = std::mem::replace(context.vm.frame_mut(), dummy_call_frame); - - let this_function_object = context - .vm - .active_function - .clone() - .expect("active functoion should be set to the generator"); - - let proto = this_function_object - .get(PROTOTYPE, context) - .expect("generator must have a prototype property") - .as_object() - .map_or_else( - || { - if r#async { - context.intrinsics().objects().async_generator() - } else { - context.intrinsics().objects().generator() - } - }, - Clone::clone, - ); - - let global_environement = context.vm.environments.global(); - let environments = std::mem::replace( - &mut context.vm.environments, - EnvironmentStack::new(global_environement), - ); - let stack = std::mem::take(&mut context.vm.stack); - - let data = if r#async { - ObjectData::async_generator(AsyncGenerator { - state: AsyncGeneratorState::SuspendedStart, - context: Some(GeneratorContext::new( - environments, - stack, - context.vm.active_function.clone(), - call_frame, - context.realm().clone(), - )), - queue: VecDeque::new(), - }) - } else { - ObjectData::generator(crate::builtins::generator::Generator { - state: GeneratorState::SuspendedStart { - context: GeneratorContext::new( - environments, - stack, - context.vm.active_function.clone(), - call_frame, - context.realm().clone(), - ), - }, - }) - }; - - let generator = - JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, data); - - if r#async { - let gen_clone = generator.clone(); - let mut generator_mut = generator.borrow_mut(); - let gen = generator_mut - .as_async_generator_mut() - .expect("must be object here"); - let gen_context = gen.context.as_mut().expect("must exist"); - // TODO: try to move this to the context itself. - gen_context - .call_frame - .as_mut() - .expect("should have a call frame initialized") - .async_generator = Some(gen_clone); - } - - context.vm.push(generator); - Ok(CompletionType::Yield) - } -} - /// `GetReturnValue` implements the Opcode Operation for `Opcode::GetReturnValue` /// /// Operation: diff --git a/boa_engine/src/vm/opcode/generator/mod.rs b/boa_engine/src/vm/opcode/generator/mod.rs index 6b060c415d1..a744614a3fb 100644 --- a/boa_engine/src/vm/opcode/generator/mod.rs +++ b/boa_engine/src/vm/opcode/generator/mod.rs @@ -1,20 +1,172 @@ pub(crate) mod yield_stm; +use std::collections::VecDeque; + use crate::{ + builtins::{ + async_generator::{AsyncGenerator, AsyncGeneratorState}, + generator::{GeneratorContext, GeneratorState}, + }, + environments::EnvironmentStack, error::JsNativeError, + object::{ObjectData, PROTOTYPE}, string::utf16, vm::{ call_frame::GeneratorResumeKind, opcode::{Operation, ReThrow}, - CompletionType, + CallFrame, CompletionType, }, - Context, JsError, JsResult, + Context, JsError, JsObject, JsResult, }; pub(crate) use yield_stm::*; use super::SetReturnValue; +/// `Generator` implements the Opcode Operation for `Opcode::Generator` +/// +/// Operation: +/// - Creates the generator generator object and yields. +#[derive(Debug, Clone, Copy)] +pub(crate) struct Generator; + +impl Operation for Generator { + const NAME: &'static str = "Generator"; + const INSTRUCTION: &'static str = "INST - Generator"; + + fn execute(context: &mut Context<'_>) -> JsResult { + let r#async = context.vm.read::() != 0; + + let code_block = context.vm.frame().code_block().clone(); + let pc = context.vm.frame().pc; + let mut dummy_call_frame = CallFrame::new(code_block); + dummy_call_frame.pc = pc; + let call_frame = std::mem::replace(context.vm.frame_mut(), dummy_call_frame); + + let this_function_object = context + .vm + .active_function + .clone() + .expect("active functoion should be set to the generator"); + + let proto = this_function_object + .get(PROTOTYPE, context) + .expect("generator must have a prototype property") + .as_object() + .map_or_else( + || { + if r#async { + context.intrinsics().objects().async_generator() + } else { + context.intrinsics().objects().generator() + } + }, + Clone::clone, + ); + + let global_environement = context.vm.environments.global(); + let environments = std::mem::replace( + &mut context.vm.environments, + EnvironmentStack::new(global_environement), + ); + let stack = std::mem::take(&mut context.vm.stack); + + let data = if r#async { + ObjectData::async_generator(AsyncGenerator { + state: AsyncGeneratorState::SuspendedStart, + context: Some(GeneratorContext::new( + environments, + stack, + context.vm.active_function.clone(), + call_frame, + context.realm().clone(), + )), + queue: VecDeque::new(), + }) + } else { + ObjectData::generator(crate::builtins::generator::Generator { + state: GeneratorState::SuspendedStart { + context: GeneratorContext::new( + environments, + stack, + context.vm.active_function.clone(), + call_frame, + context.realm().clone(), + ), + }, + }) + }; + + let generator = + JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, data); + + if r#async { + let gen_clone = generator.clone(); + let mut generator_mut = generator.borrow_mut(); + let gen = generator_mut + .as_async_generator_mut() + .expect("must be object here"); + let gen_context = gen.context.as_mut().expect("must exist"); + // TODO: try to move this to the context itself. + gen_context + .call_frame + .as_mut() + .expect("should have a call frame initialized") + .async_generator = Some(gen_clone); + } + + context.vm.push(generator); + Ok(CompletionType::Yield) + } +} + +/// `AsyncGeneratorClose` implements the Opcode Operation for `Opcode::AsyncGeneratorClose` +/// +/// Operation: +/// - Close an async generator function. +#[derive(Debug, Clone, Copy)] +pub(crate) struct AsyncGeneratorClose; + +impl Operation for AsyncGeneratorClose { + const NAME: &'static str = "AsyncGeneratorClose"; + const INSTRUCTION: &'static str = "INST - AsyncGeneratorClose"; + + fn execute(context: &mut Context<'_>) -> JsResult { + // Step 3.e-g in [AsyncGeneratorStart](https://tc39.es/ecma262/#sec-asyncgeneratorstart) + let generator_object = context + .vm + .frame() + .async_generator + .clone() + .expect("There should be a object"); + + let mut generator_object_mut = generator_object.borrow_mut(); + let generator = generator_object_mut + .as_async_generator_mut() + .expect("must be async generator"); + + generator.state = AsyncGeneratorState::Completed; + generator.context = None; + + let next = generator + .queue + .pop_front() + .expect("must have item in queue"); + drop(generator_object_mut); + + let return_value = std::mem::take(&mut context.vm.frame_mut().return_value); + + if let Some(error) = context.vm.pending_exception.take() { + AsyncGenerator::complete_step(&next, Err(error), true, None, context); + } else { + AsyncGenerator::complete_step(&next, Ok(return_value), true, None, context); + } + AsyncGenerator::drain_queue(&generator_object, context); + + Ok(CompletionType::Normal) + } +} + /// `GeneratorNext` implements the Opcode Operation for `Opcode::GeneratorNext` /// /// Operation: diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index dc2aa3dfd3e..ca5ee3eb0ff 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -2,7 +2,7 @@ use crate::{vm::CompletionType, Context, JsResult}; // Operation modules -mod await_stm; +mod r#await; mod binary_ops; mod call; mod concat; @@ -15,7 +15,6 @@ mod environment; mod generator; mod get; mod iteration; -mod jump; mod meta; mod new; mod nop; @@ -33,8 +32,6 @@ mod value; // Operation structs #[doc(inline)] -pub(crate) use await_stm::*; -#[doc(inline)] pub(crate) use binary_ops::*; #[doc(inline)] pub(crate) use call::*; @@ -59,8 +56,6 @@ pub(crate) use get::*; #[doc(inline)] pub(crate) use iteration::*; #[doc(inline)] -pub(crate) use jump::*; -#[doc(inline)] pub(crate) use meta::*; #[doc(inline)] pub(crate) use new::*; @@ -71,6 +66,8 @@ pub(crate) use pop::*; #[doc(inline)] pub(crate) use push::*; #[doc(inline)] +pub(crate) use r#await::*; +#[doc(inline)] pub(crate) use require::*; #[doc(inline)] pub(crate) use rest_parameter::*; @@ -1326,12 +1323,12 @@ generate_impl! { /// Stack: **=>** Return, - /// Return from an async generator function. + /// Close an async generator function. /// /// Operands: /// /// Stack: **=>** - AsyncGeneratorReturn, + AsyncGeneratorClose, /// Creates the generator generator object and yields. /// @@ -1568,7 +1565,7 @@ generate_impl! { /// Stack: value **=>** received AsyncGeneratorYield, - /// Create a promise capability for an async function. + /// Create a promise capacity for an async function, if not already set. /// /// Operands: /// @@ -1577,12 +1574,12 @@ generate_impl! { /// Resolves or rejects the promise capability of an async function. /// - /// If the pending exception is set reject and rethrow the exception, otherwise resolve. + /// If the pending exception is set, reject and rethrow the exception, otherwise resolve. /// /// Operands: /// /// Stack: **=>** - PromiseCapability, + CompletePromiseCapability, /// Jumps to the specified address if the resume kind is not equal. ///