diff --git a/boa_engine/src/script.rs b/boa_engine/src/script.rs index 60c272d379..780ca18917 100644 --- a/boa_engine/src/script.rs +++ b/boa_engine/src/script.rs @@ -146,6 +146,52 @@ impl Script { pub fn evaluate(&self, context: &mut Context<'_>) -> JsResult { let _timer = Profiler::global().start_event("Execution", "Main"); + self.prepare_run(context)?; + let record = context.run(); + + context.vm.pop_frame(); + context.clear_kept_objects(); + + record.consume() + } + + /// Evaluates this script and returns its result, periodically yielding to the executor + /// in order to avoid blocking the current thread. + /// + /// This uses an implementation defined amount of "clock cycles" that need to pass before + /// execution is suspended. See [`Script::evaluate_async_with_budget`] if you want to also + /// customize this parameter. + #[allow(clippy::future_not_send)] + pub async fn evaluate_async(&self, context: &mut Context<'_>) -> JsResult { + self.evaluate_async_with_budget(context, 256).await + } + + /// Evaluates this script and returns its result, yielding to the executor each time `budget` + /// number of "clock cycles" pass. + /// + /// Note that "clock cycle" is in quotation marks because we can't determine exactly how many + /// CPU clock cycles a VM instruction will take, but all instructions have a "cost" associated + /// with them that depends on their individual complexity. We'd recommend benchmarking with + /// different budget sizes in order to find the ideal yielding time for your application. + #[allow(clippy::future_not_send)] + pub async fn evaluate_async_with_budget( + &self, + context: &mut Context<'_>, + budget: u32, + ) -> JsResult { + let _timer = Profiler::global().start_event("Async Execution", "Main"); + + self.prepare_run(context)?; + + let record = context.run_async_with_budget(budget).await; + + context.vm.pop_frame(); + context.clear_kept_objects(); + + record.consume() + } + + fn prepare_run(&self, context: &mut Context<'_>) -> JsResult<()> { let codeblock = self.codeblock(context)?; let env_fp = context.vm.environments.len() as u32; @@ -165,11 +211,7 @@ impl Script { // TODO: Here should be https://tc39.es/ecma262/#sec-globaldeclarationinstantiation self.realm().resize_global_env(); - let record = context.run(); - context.vm.pop_frame(); - context.clear_kept_objects(); - - record.consume() + Ok(()) } } diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index e8df1ece84..369cda869d 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -11,7 +11,7 @@ use crate::{ use boa_gc::{custom_trace, Finalize, Trace}; use boa_profiler::Profiler; -use std::mem::size_of; +use std::{future::Future, mem::size_of, ops::ControlFlow, pin::Pin, task}; #[cfg(feature = "trace")] use boa_interner::ToInternedString; @@ -292,7 +292,10 @@ impl Context<'_> { ); } - fn trace_execute_instruction(&mut self) -> JsResult { + fn trace_execute_instruction(&mut self, f: F) -> JsResult + where + F: FnOnce(Opcode, &mut Context<'_>) -> JsResult, + { let bytecodes = &self.vm.frame().code_block.bytecode; let pc = self.vm.frame().pc as usize; let (_, varying_operand_kind, instruction) = InstructionIterator::with_pc(bytecodes, pc) @@ -322,7 +325,7 @@ impl Context<'_> { } let instant = Instant::now(); - let result = self.execute_instruction(); + let result = self.execute_instruction(f); let duration = instant.elapsed(); let fp = self.vm.frames.last().map(|frame| frame.fp as usize); @@ -370,7 +373,10 @@ impl Context<'_> { } impl Context<'_> { - fn execute_instruction(&mut self) -> JsResult { + fn execute_instruction(&mut self, f: F) -> JsResult + where + F: FnOnce(Opcode, &mut Context<'_>) -> JsResult, + { let opcode: Opcode = { let _timer = Profiler::global().start_event("Opcode retrieval", "vm"); @@ -384,138 +390,178 @@ impl Context<'_> { let _timer = Profiler::global().start_event(opcode.as_instruction_str(), "vm"); - opcode.execute(self) + f(opcode, self) } - pub(crate) fn run(&mut self) -> CompletionRecord { - let _timer = Profiler::global().start_event("run", "vm"); - - #[cfg(feature = "trace")] - if self.vm.trace { - self.trace_call_frame(); + fn execute_one(&mut self, f: F) -> ControlFlow + where + F: FnOnce(Opcode, &mut Context<'_>) -> JsResult, + { + #[cfg(feature = "fuzz")] + { + if self.instructions_remaining == 0 { + return ControlFlow::Break(CompletionRecord::Throw(JsError::from_native( + JsNativeError::no_instructions_remain(), + ))); + } + self.instructions_remaining -= 1; } - 'instruction: loop { - #[cfg(feature = "fuzz")] - { - if self.instructions_remaining == 0 { - return CompletionRecord::Throw(JsError::from_native( - JsNativeError::no_instructions_remain(), - )); - } - self.instructions_remaining -= 1; - } + #[cfg(feature = "trace")] + let result = if self.vm.trace || self.vm.frame().code_block.traceable() { + self.trace_execute_instruction(f) + } else { + self.execute_instruction(f) + }; - #[cfg(feature = "trace")] - let result = if self.vm.trace || self.vm.frame().code_block.traceable() { - self.trace_execute_instruction() - } else { - self.execute_instruction() - }; - - #[cfg(not(feature = "trace"))] - let result = self.execute_instruction(); - - let result = match result { - Ok(result) => result, - Err(err) => { - // If we hit the execution step limit, bubble up the error to the - // (Rust) caller instead of trying to handle as an exception. - if !err.is_catchable() { - let mut fp = self.vm.stack.len(); - let mut env_fp = self.vm.environments.len(); - while let Some(frame) = self.vm.frames.last() { - if frame.exit_early() { - break; - } - - fp = frame.fp as usize; - env_fp = frame.env_fp as usize; - self.vm.pop_frame(); + #[cfg(not(feature = "trace"))] + let result = self.execute_instruction(f); + + let result = match result { + Ok(result) => result, + Err(err) => { + // If we hit the execution step limit, bubble up the error to the + // (Rust) caller instead of trying to handle as an exception. + if !err.is_catchable() { + let mut fp = self.vm.stack.len(); + let mut env_fp = self.vm.environments.len(); + while let Some(frame) = self.vm.frames.last() { + if frame.exit_early() { + break; } - self.vm.environments.truncate(env_fp); - self.vm.stack.truncate(fp); - return CompletionRecord::Throw(err); - } - // Note: -1 because we increment after fetching the opcode. - let pc = self.vm.frame().pc.saturating_sub(1); - if self.vm.handle_exception_at(pc) { - self.vm.pending_exception = Some(err); - continue; + fp = frame.fp as usize; + env_fp = frame.env_fp as usize; + self.vm.pop_frame(); } + self.vm.environments.truncate(env_fp); + self.vm.stack.truncate(fp); + return ControlFlow::Break(CompletionRecord::Throw(err)); + } - // Inject realm before crossing the function boundry - let err = err.inject_realm(self.realm().clone()); - + // Note: -1 because we increment after fetching the opcode. + let pc = self.vm.frame().pc.saturating_sub(1); + if self.vm.handle_exception_at(pc) { self.vm.pending_exception = Some(err); - CompletionType::Throw + return ControlFlow::Continue(()); } - }; - match result { - CompletionType::Normal => {} - CompletionType::Return => { - self.vm.stack.truncate(self.vm.frame().fp as usize); + // Inject realm before crossing the function boundry + let err = err.inject_realm(self.realm().clone()); - let result = self.vm.take_return_value(); - if self.vm.frame().exit_early() { - return CompletionRecord::Normal(result); - } + self.vm.pending_exception = Some(err); + CompletionType::Throw + } + }; - self.vm.push(result); - self.vm.pop_frame(); + match result { + CompletionType::Normal => {} + CompletionType::Return => { + self.vm.stack.truncate(self.vm.frame().fp as usize); + + let result = self.vm.take_return_value(); + if self.vm.frame().exit_early() { + return ControlFlow::Break(CompletionRecord::Normal(result)); + } + + self.vm.push(result); + self.vm.pop_frame(); + } + CompletionType::Throw => { + let mut fp = self.vm.frame().fp; + let mut env_fp = self.vm.frame().env_fp; + if self.vm.frame().exit_early() { + self.vm.environments.truncate(env_fp as usize); + self.vm.stack.truncate(fp as usize); + return ControlFlow::Break(CompletionRecord::Throw( + self.vm + .pending_exception + .take() + .expect("Err must exist for a CompletionType::Throw"), + )); } - CompletionType::Throw => { - let mut fp = self.vm.frame().fp; - let mut env_fp = self.vm.frame().env_fp; - if self.vm.frame().exit_early() { - self.vm.environments.truncate(env_fp as usize); - self.vm.stack.truncate(fp as usize); - return CompletionRecord::Throw( + + self.vm.pop_frame(); + + while let Some(frame) = self.vm.frames.last_mut() { + fp = frame.fp; + env_fp = frame.fp; + let pc = frame.pc; + let exit_early = frame.exit_early(); + + if self.vm.handle_exception_at(pc) { + return ControlFlow::Continue(()); + } + + if exit_early { + return ControlFlow::Break(CompletionRecord::Throw( self.vm .pending_exception .take() .expect("Err must exist for a CompletionType::Throw"), - ); + )); } self.vm.pop_frame(); + } + self.vm.environments.truncate(env_fp as usize); + self.vm.stack.truncate(fp as usize); + } + // Early return immediately. + CompletionType::Yield => { + let result = self.vm.take_return_value(); + if self.vm.frame().exit_early() { + return ControlFlow::Break(CompletionRecord::Return(result)); + } - while let Some(frame) = self.vm.frames.last_mut() { - fp = frame.fp; - env_fp = frame.fp; - let pc = frame.pc; - let exit_early = frame.exit_early(); + self.vm.push(result); + self.vm.pop_frame(); + } + } - if self.vm.handle_exception_at(pc) { - continue 'instruction; - } + ControlFlow::Continue(()) + } - if exit_early { - return CompletionRecord::Throw( - self.vm - .pending_exception - .take() - .expect("Err must exist for a CompletionType::Throw"), - ); - } + /// Runs the current frame to completion, yielding to the caller each time `budget` + /// "clock cycles" have passed. + #[allow(clippy::future_not_send)] + pub(crate) async fn run_async_with_budget(&mut self, budget: u32) -> CompletionRecord { + let _timer = Profiler::global().start_event("run_async_with_budget", "vm"); - self.vm.pop_frame(); - } - self.vm.environments.truncate(env_fp as usize); - self.vm.stack.truncate(fp as usize); - } - // Early return immediately. - CompletionType::Yield => { - let result = self.vm.take_return_value(); - if self.vm.frame().exit_early() { - return CompletionRecord::Return(result); - } + #[cfg(feature = "trace")] + if self.vm.trace { + self.trace_call_frame(); + } - self.vm.push(result); - self.vm.pop_frame(); - } + let mut runtime_budget: u32 = budget; + + loop { + match self.execute_one(|opcode, context| { + opcode.spend_budget_and_execute(context, &mut runtime_budget) + }) { + ControlFlow::Continue(()) => {} + ControlFlow::Break(record) => return record, + } + + if runtime_budget == 0 { + runtime_budget = budget; + yield_now().await; + } + } + } + + pub(crate) fn run(&mut self) -> CompletionRecord { + let _timer = Profiler::global().start_event("run", "vm"); + + #[cfg(feature = "trace")] + if self.vm.trace { + self.trace_call_frame(); + } + + loop { + match self.execute_one(Opcode::execute) { + ControlFlow::Continue(()) => {} + ControlFlow::Break(value) => return value, } } } @@ -538,3 +584,24 @@ impl Context<'_> { Ok(()) } } + +/// Yields once to the executor. +fn yield_now() -> impl Future { + struct YieldNow(bool); + + impl Future for YieldNow { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { + if self.0 { + task::Poll::Ready(()) + } else { + self.0 = true; + cx.waker().wake_by_ref(); + task::Poll::Pending + } + } + } + + YieldNow(false) +} diff --git a/boa_engine/src/vm/opcode/await/mod.rs b/boa_engine/src/vm/opcode/await/mod.rs index 1123f8f9d3..a4e032c455 100644 --- a/boa_engine/src/vm/opcode/await/mod.rs +++ b/boa_engine/src/vm/opcode/await/mod.rs @@ -18,6 +18,7 @@ pub(crate) struct Await; impl Operation for Await { const NAME: &'static str = "Await"; const INSTRUCTION: &'static str = "INST - Await"; + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -150,6 +151,7 @@ pub(crate) struct CreatePromiseCapability; impl Operation for CreatePromiseCapability { const NAME: &'static str = "CreatePromiseCapability"; const INSTRUCTION: &'static str = "INST - CreatePromiseCapability"; + const COST: u8 = 8; fn execute(context: &mut Context<'_>) -> JsResult { if context.vm.frame().promise_capability.is_some() { @@ -177,6 +179,7 @@ pub(crate) struct CompletePromiseCapability; impl Operation for CompletePromiseCapability { const NAME: &'static str = "CompletePromiseCapability"; const INSTRUCTION: &'static str = "INST - CompletePromiseCapability"; + const COST: u8 = 8; 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. diff --git a/boa_engine/src/vm/opcode/binary_ops/logical.rs b/boa_engine/src/vm/opcode/binary_ops/logical.rs index 6b2018a287..161c8936a1 100644 --- a/boa_engine/src/vm/opcode/binary_ops/logical.rs +++ b/boa_engine/src/vm/opcode/binary_ops/logical.rs @@ -13,6 +13,7 @@ pub(crate) struct LogicalAnd; impl Operation for LogicalAnd { const NAME: &'static str = "LogicalAnd"; const INSTRUCTION: &'static str = "INST - LogicalAnd"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let exit = context.vm.read::(); @@ -35,6 +36,7 @@ pub(crate) struct LogicalOr; impl Operation for LogicalOr { const NAME: &'static str = "LogicalOr"; const INSTRUCTION: &'static str = "INST - LogicalOr"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let exit = context.vm.read::(); @@ -57,6 +59,7 @@ pub(crate) struct Coalesce; impl Operation for Coalesce { const NAME: &'static str = "Coalesce"; const INSTRUCTION: &'static str = "INST - Coalesce"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let exit = context.vm.read::(); diff --git a/boa_engine/src/vm/opcode/binary_ops/macro_defined.rs b/boa_engine/src/vm/opcode/binary_ops/macro_defined.rs index dae388ca54..38d6f01dd3 100644 --- a/boa_engine/src/vm/opcode/binary_ops/macro_defined.rs +++ b/boa_engine/src/vm/opcode/binary_ops/macro_defined.rs @@ -15,6 +15,7 @@ macro_rules! implement_bin_ops { impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let rhs = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/binary_ops/mod.rs b/boa_engine/src/vm/opcode/binary_ops/mod.rs index dbaeedbc81..a50f200698 100644 --- a/boa_engine/src/vm/opcode/binary_ops/mod.rs +++ b/boa_engine/src/vm/opcode/binary_ops/mod.rs @@ -20,6 +20,7 @@ pub(crate) struct NotEq; impl Operation for NotEq { const NAME: &'static str = "NotEq"; const INSTRUCTION: &'static str = "INST - NotEq"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let rhs = context.vm.pop(); @@ -40,6 +41,7 @@ pub(crate) struct StrictEq; impl Operation for StrictEq { const NAME: &'static str = "StrictEq"; const INSTRUCTION: &'static str = "INST - StrictEq"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let rhs = context.vm.pop(); @@ -59,6 +61,7 @@ pub(crate) struct StrictNotEq; impl Operation for StrictNotEq { const NAME: &'static str = "StrictNotEq"; const INSTRUCTION: &'static str = "INST - StrictNotEq"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let rhs = context.vm.pop(); @@ -78,6 +81,7 @@ pub(crate) struct In; impl Operation for In { const NAME: &'static str = "In"; const INSTRUCTION: &'static str = "INST - In"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let rhs = context.vm.pop(); @@ -137,6 +141,7 @@ impl InPrivate { impl Operation for InPrivate { const NAME: &'static str = "InPrivate"; const INSTRUCTION: &'static str = "INST - InPrivate"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -164,6 +169,7 @@ pub(crate) struct InstanceOf; impl Operation for InstanceOf { const NAME: &'static str = "InstanceOf"; const INSTRUCTION: &'static str = "INST - InstanceOf"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let target = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/call/mod.rs b/boa_engine/src/vm/opcode/call/mod.rs index d8599e7378..1ea6696e5e 100644 --- a/boa_engine/src/vm/opcode/call/mod.rs +++ b/boa_engine/src/vm/opcode/call/mod.rs @@ -63,6 +63,9 @@ impl CallEval { impl Operation for CallEval { const NAME: &'static str = "CallEval"; const INSTRUCTION: &'static str = "INST - CallEval"; + // TODO: Calls will require a big refactor in order to track + // the cost of the call. + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let argument_count = context.vm.read::(); @@ -90,6 +93,9 @@ pub(crate) struct CallEvalSpread; impl Operation for CallEvalSpread { const NAME: &'static str = "CallEvalSpread"; const INSTRUCTION: &'static str = "INST - CallEvalSpread"; + // TODO: Calls will require a big refactor in order to track + // the cost of the call. + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { // Get the arguments that are stored as an array object on the stack. @@ -176,6 +182,7 @@ impl Call { impl Operation for Call { const NAME: &'static str = "Call"; const INSTRUCTION: &'static str = "INST - Call"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let argument_count = context.vm.read::(); @@ -199,6 +206,7 @@ pub(crate) struct CallSpread; impl Operation for CallSpread { const NAME: &'static str = "CallSpread"; const INSTRUCTION: &'static str = "INST - CallSpread"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { // Get the arguments that are stored as an array object on the stack. @@ -240,6 +248,7 @@ pub(crate) struct ImportCall; impl Operation for ImportCall { const NAME: &'static str = "ImportCall"; const INSTRUCTION: &'static str = "INST - ImportCall"; + const COST: u8 = 15; fn execute(context: &mut Context<'_>) -> JsResult { // Import Calls diff --git a/boa_engine/src/vm/opcode/concat/mod.rs b/boa_engine/src/vm/opcode/concat/mod.rs index 5ebb102f8c..068e4c96e6 100644 --- a/boa_engine/src/vm/opcode/concat/mod.rs +++ b/boa_engine/src/vm/opcode/concat/mod.rs @@ -31,6 +31,7 @@ impl ConcatToString { impl Operation for ConcatToString { const NAME: &'static str = "ConcatToString"; const INSTRUCTION: &'static str = "INST - ConcatToString"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let value_count = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/control_flow/jump.rs b/boa_engine/src/vm/opcode/control_flow/jump.rs index b38fea3bce..9b7b6a46c3 100644 --- a/boa_engine/src/vm/opcode/control_flow/jump.rs +++ b/boa_engine/src/vm/opcode/control_flow/jump.rs @@ -13,6 +13,7 @@ pub(crate) struct Jump; impl Operation for Jump { const NAME: &'static str = "Jump"; const INSTRUCTION: &'static str = "INST - Jump"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let address = context.vm.read::(); @@ -31,6 +32,7 @@ pub(crate) struct JumpIfTrue; impl Operation for JumpIfTrue { const NAME: &'static str = "JumpIfTrue"; const INSTRUCTION: &'static str = "INST - JumpIfTrue"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let address = context.vm.read::(); @@ -51,6 +53,7 @@ pub(crate) struct JumpIfFalse; impl Operation for JumpIfFalse { const NAME: &'static str = "JumpIfFalse"; const INSTRUCTION: &'static str = "INST - JumpIfFalse"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let address = context.vm.read::(); @@ -71,6 +74,7 @@ pub(crate) struct JumpIfNotUndefined; impl Operation for JumpIfNotUndefined { const NAME: &'static str = "JumpIfNotUndefined"; const INSTRUCTION: &'static str = "INST - JumpIfNotUndefined"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let address = context.vm.read::(); @@ -93,6 +97,7 @@ pub(crate) struct JumpIfNullOrUndefined; impl Operation for JumpIfNullOrUndefined { const NAME: &'static str = "JumpIfNullOrUndefined"; const INSTRUCTION: &'static str = "INST - JumpIfNullOrUndefined"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let address = context.vm.read::(); @@ -116,6 +121,7 @@ pub(crate) struct JumpTable; impl Operation for JumpTable { const NAME: &'static str = "JumpTable"; const INSTRUCTION: &'static str = "INST - JumpTable"; + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let default = context.vm.read::(); diff --git a/boa_engine/src/vm/opcode/control_flow/return.rs b/boa_engine/src/vm/opcode/control_flow/return.rs index 747437cb08..4b6ea6b048 100644 --- a/boa_engine/src/vm/opcode/control_flow/return.rs +++ b/boa_engine/src/vm/opcode/control_flow/return.rs @@ -13,6 +13,7 @@ pub(crate) struct Return; impl Operation for Return { const NAME: &'static str = "Return"; const INSTRUCTION: &'static str = "INST - Return"; + const COST: u8 = 4; fn execute(_context: &mut Context<'_>) -> JsResult { Ok(CompletionType::Return) @@ -29,6 +30,7 @@ pub(crate) struct CheckReturn; impl Operation for CheckReturn { const NAME: &'static str = "CheckReturn"; const INSTRUCTION: &'static str = "INST - CheckReturn"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { if !context.vm.frame().construct() { @@ -80,6 +82,7 @@ pub(crate) struct GetReturnValue; impl Operation for GetReturnValue { const NAME: &'static str = "GetReturnValue"; const INSTRUCTION: &'static str = "INST - GetReturnValue"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.get_return_value(); @@ -98,6 +101,7 @@ pub(crate) struct SetReturnValue; impl Operation for SetReturnValue { const NAME: &'static str = "SetReturnValue"; const INSTRUCTION: &'static str = "INST - SetReturnValue"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/control_flow/throw.rs b/boa_engine/src/vm/opcode/control_flow/throw.rs index df09c646e7..c85c6e4ae6 100644 --- a/boa_engine/src/vm/opcode/control_flow/throw.rs +++ b/boa_engine/src/vm/opcode/control_flow/throw.rs @@ -13,6 +13,7 @@ pub(crate) struct Throw; impl Operation for Throw { const NAME: &'static str = "Throw"; const INSTRUCTION: &'static str = "INST - Throw"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let error = JsError::from_opaque(context.vm.pop()); @@ -38,6 +39,7 @@ pub(crate) struct ReThrow; impl Operation for ReThrow { const NAME: &'static str = "ReThrow"; const INSTRUCTION: &'static str = "INST - ReThrow"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { // Note: -1 because we increment after fetching the opcode. @@ -69,6 +71,7 @@ pub(crate) struct Exception; impl Operation for Exception { const NAME: &'static str = "Exception"; const INSTRUCTION: &'static str = "INST - Exception"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { if let Some(error) = context.vm.pending_exception.take() { @@ -96,6 +99,7 @@ pub(crate) struct MaybeException; impl Operation for MaybeException { const NAME: &'static str = "MaybeException"; const INSTRUCTION: &'static str = "INST - MaybeException"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { if let Some(error) = context.vm.pending_exception.take() { @@ -133,6 +137,7 @@ impl ThrowNewTypeError { impl Operation for ThrowNewTypeError { const NAME: &'static str = "ThrowNewTypeError"; const INSTRUCTION: &'static str = "INST - ThrowNewTypeError"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/copy/mod.rs b/boa_engine/src/vm/opcode/copy/mod.rs index d90ebd80c9..1413459c66 100644 --- a/boa_engine/src/vm/opcode/copy/mod.rs +++ b/boa_engine/src/vm/opcode/copy/mod.rs @@ -43,6 +43,7 @@ impl CopyDataProperties { impl Operation for CopyDataProperties { const NAME: &'static str = "CopyDataProperties"; const INSTRUCTION: &'static str = "INST - CopyDataProperties"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let excluded_key_count = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/define/class/getter.rs b/boa_engine/src/vm/opcode/define/class/getter.rs index 8a3e931467..6ebe6f83f5 100644 --- a/boa_engine/src/vm/opcode/define/class/getter.rs +++ b/boa_engine/src/vm/opcode/define/class/getter.rs @@ -51,6 +51,7 @@ impl DefineClassStaticGetterByName { impl Operation for DefineClassStaticGetterByName { const NAME: &'static str = "DefineClassStaticGetterByName"; const INSTRUCTION: &'static str = "INST - DefineClassStaticGetterByName"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -114,6 +115,7 @@ impl DefineClassGetterByName { impl Operation for DefineClassGetterByName { const NAME: &'static str = "DefineClassGetterByName"; const INSTRUCTION: &'static str = "INST - DefineClassGetterByName"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -141,6 +143,7 @@ pub(crate) struct DefineClassStaticGetterByValue; impl Operation for DefineClassStaticGetterByValue { const NAME: &'static str = "DefineClassStaticGetterByValue"; const INSTRUCTION: &'static str = "INST - DefineClassStaticGetterByValue"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); @@ -190,6 +193,7 @@ pub(crate) struct DefineClassGetterByValue; impl Operation for DefineClassGetterByValue { const NAME: &'static str = "DefineClassGetterByValue"; const INSTRUCTION: &'static str = "INST - DefineClassGetterByValue"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/define/class/method.rs b/boa_engine/src/vm/opcode/define/class/method.rs index e1b961668b..a053961710 100644 --- a/boa_engine/src/vm/opcode/define/class/method.rs +++ b/boa_engine/src/vm/opcode/define/class/method.rs @@ -47,6 +47,7 @@ impl DefineClassStaticMethodByName { impl Operation for DefineClassStaticMethodByName { const NAME: &'static str = "DefineClassStaticMethodByName"; const INSTRUCTION: &'static str = "INST - DefineClassStaticMethodByName"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -106,6 +107,7 @@ impl DefineClassMethodByName { impl Operation for DefineClassMethodByName { const NAME: &'static str = "DefineClassMethodByName"; const INSTRUCTION: &'static str = "INST - DefineClassMethodByName"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -133,6 +135,7 @@ pub(crate) struct DefineClassStaticMethodByValue; impl Operation for DefineClassStaticMethodByValue { const NAME: &'static str = "DefineClassStaticMethodByValue"; const INSTRUCTION: &'static str = "INST - DefineClassStaticMethodByValue"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); @@ -178,6 +181,7 @@ pub(crate) struct DefineClassMethodByValue; impl Operation for DefineClassMethodByValue { const NAME: &'static str = "DefineClassMethodByValue"; const INSTRUCTION: &'static str = "INST - DefineClassMethodByValue"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/define/class/setter.rs b/boa_engine/src/vm/opcode/define/class/setter.rs index efce60c417..165c85d80c 100644 --- a/boa_engine/src/vm/opcode/define/class/setter.rs +++ b/boa_engine/src/vm/opcode/define/class/setter.rs @@ -52,6 +52,7 @@ impl DefineClassStaticSetterByName { impl Operation for DefineClassStaticSetterByName { const NAME: &'static str = "DefineClassStaticSetterByName"; const INSTRUCTION: &'static str = "INST - DefineClassStaticSetterByName"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -117,6 +118,7 @@ impl DefineClassSetterByName { impl Operation for DefineClassSetterByName { const NAME: &'static str = "DefineClassSetterByName"; const INSTRUCTION: &'static str = "INST - DefineClassSetterByName"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -144,6 +146,7 @@ pub(crate) struct DefineClassStaticSetterByValue; impl Operation for DefineClassStaticSetterByValue { const NAME: &'static str = "DefineClassStaticSetterByValue"; const INSTRUCTION: &'static str = "INST - DefineClassStaticSetterByValue"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); @@ -195,6 +198,7 @@ pub(crate) struct DefineClassSetterByValue; impl Operation for DefineClassSetterByValue { const NAME: &'static str = "DefineClassSetterByValue"; const INSTRUCTION: &'static str = "INST - DefineClassSetterByValue"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/define/mod.rs b/boa_engine/src/vm/opcode/define/mod.rs index 1544ed82d1..e555f545ea 100644 --- a/boa_engine/src/vm/opcode/define/mod.rs +++ b/boa_engine/src/vm/opcode/define/mod.rs @@ -38,6 +38,7 @@ impl DefVar { impl Operation for DefVar { const NAME: &'static str = "DefVar"; const INSTRUCTION: &'static str = "INST - DefVar"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -80,6 +81,7 @@ impl DefInitVar { impl Operation for DefInitVar { const NAME: &'static str = "DefInitVar"; const INSTRUCTION: &'static str = "INST - DefInitVar"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -122,6 +124,7 @@ impl PutLexicalValue { impl Operation for PutLexicalValue { const NAME: &'static str = "PutLexicalValue"; const INSTRUCTION: &'static str = "INST - PutLexicalValue"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); diff --git a/boa_engine/src/vm/opcode/define/own_property.rs b/boa_engine/src/vm/opcode/define/own_property.rs index 91daaf92b4..0ecbe226da 100644 --- a/boa_engine/src/vm/opcode/define/own_property.rs +++ b/boa_engine/src/vm/opcode/define/own_property.rs @@ -38,6 +38,7 @@ impl DefineOwnPropertyByName { impl Operation for DefineOwnPropertyByName { const NAME: &'static str = "DefineOwnPropertyByName"; const INSTRUCTION: &'static str = "INST - DefineOwnPropertyByName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -65,6 +66,7 @@ pub(crate) struct DefineOwnPropertyByValue; impl Operation for DefineOwnPropertyByValue { const NAME: &'static str = "DefineOwnPropertyByValue"; const INSTRUCTION: &'static str = "INST - DefineOwnPropertyByValue"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/delete/mod.rs b/boa_engine/src/vm/opcode/delete/mod.rs index ddeb0ec1d6..0f50cf5020 100644 --- a/boa_engine/src/vm/opcode/delete/mod.rs +++ b/boa_engine/src/vm/opcode/delete/mod.rs @@ -30,6 +30,7 @@ impl DeletePropertyByName { impl Operation for DeletePropertyByName { const NAME: &'static str = "DeletePropertyByName"; const INSTRUCTION: &'static str = "INST - DeletePropertyByName"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -57,6 +58,7 @@ pub(crate) struct DeletePropertyByValue; impl Operation for DeletePropertyByValue { const NAME: &'static str = "DeletePropertyByValue"; const INSTRUCTION: &'static str = "INST - DeletePropertyByValue"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let key_value = context.vm.pop(); @@ -97,6 +99,7 @@ impl DeleteName { impl Operation for DeleteName { const NAME: &'static str = "DeleteName"; const INSTRUCTION: &'static str = "INST - DeleteName"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -124,6 +127,7 @@ pub(crate) struct DeleteSuperThrow; impl Operation for DeleteSuperThrow { const NAME: &'static str = "DeleteSuperThrow"; const INSTRUCTION: &'static str = "INST - DeleteSuperThrow"; + const COST: u8 = 2; fn execute(_: &mut Context<'_>) -> JsResult { Err(JsNativeError::reference() diff --git a/boa_engine/src/vm/opcode/dup/mod.rs b/boa_engine/src/vm/opcode/dup/mod.rs index 422a2e8a55..963d063a3b 100644 --- a/boa_engine/src/vm/opcode/dup/mod.rs +++ b/boa_engine/src/vm/opcode/dup/mod.rs @@ -13,6 +13,7 @@ pub(crate) struct Dup; impl Operation for Dup { const NAME: &'static str = "Dup"; const INSTRUCTION: &'static str = "INST - Dup"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/environment/mod.rs b/boa_engine/src/vm/opcode/environment/mod.rs index 1aecd2e03d..d08c658df8 100644 --- a/boa_engine/src/vm/opcode/environment/mod.rs +++ b/boa_engine/src/vm/opcode/environment/mod.rs @@ -14,6 +14,7 @@ pub(crate) struct This; impl Operation for This { const NAME: &'static str = "This"; const INSTRUCTION: &'static str = "INST - This"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let this = context.vm.environments.get_this_binding()?; @@ -32,6 +33,7 @@ pub(crate) struct Super; impl Operation for Super { const NAME: &'static str = "Super"; const INSTRUCTION: &'static str = "INST - Super"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let home_object = { @@ -72,6 +74,7 @@ pub(crate) struct SuperCallPrepare; impl Operation for SuperCallPrepare { const NAME: &'static str = "SuperCallPrepare"; const INSTRUCTION: &'static str = "INST - SuperCallPrepare"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let this_env = context @@ -134,6 +137,7 @@ impl SuperCall { impl Operation for SuperCall { const NAME: &'static str = "SuperCall"; const INSTRUCTION: &'static str = "INST - SuperCall"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value_count = context.vm.read::() as usize; @@ -161,6 +165,7 @@ pub(crate) struct SuperCallSpread; impl Operation for SuperCallSpread { const NAME: &'static str = "SuperCallSpread"; const INSTRUCTION: &'static str = "INST - SuperCallSpread"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { // Get the arguments that are stored as an array object on the stack. @@ -219,6 +224,7 @@ pub(crate) struct SuperCallDerived; impl Operation for SuperCallDerived { const NAME: &'static str = "SuperCallDerived"; const INSTRUCTION: &'static str = "INST - SuperCallDerived"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let argument_count = context.vm.frame().argument_count as usize; @@ -271,6 +277,7 @@ pub(crate) struct BindThisValue; impl Operation for BindThisValue { const NAME: &'static str = "BindThisValue"; const INSTRUCTION: &'static str = "INST - BindThisValue"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { // Taken from `SuperCall : super Arguments` steps 7-12. diff --git a/boa_engine/src/vm/opcode/generator/mod.rs b/boa_engine/src/vm/opcode/generator/mod.rs index 32408f2407..08418bd68e 100644 --- a/boa_engine/src/vm/opcode/generator/mod.rs +++ b/boa_engine/src/vm/opcode/generator/mod.rs @@ -32,6 +32,7 @@ pub(crate) struct Generator; impl Operation for Generator { const NAME: &'static str = "Generator"; const INSTRUCTION: &'static str = "INST - Generator"; + const COST: u8 = 8; fn execute(context: &mut Context<'_>) -> JsResult { let r#async = context.vm.read::() != 0; @@ -128,6 +129,7 @@ pub(crate) struct AsyncGeneratorClose; impl Operation for AsyncGeneratorClose { const NAME: &'static str = "AsyncGeneratorClose"; const INSTRUCTION: &'static str = "INST - AsyncGeneratorClose"; + const COST: u8 = 8; fn execute(context: &mut Context<'_>) -> JsResult { // Step 3.e-g in [AsyncGeneratorStart](https://tc39.es/ecma262/#sec-asyncgeneratorstart) @@ -176,6 +178,7 @@ pub(crate) struct GeneratorNext; impl Operation for GeneratorNext { const NAME: &'static str = "GeneratorNext"; const INSTRUCTION: &'static str = "INST - GeneratorNext"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let generator_resume_kind = context.vm.pop().to_generator_resume_kind(); @@ -202,6 +205,7 @@ pub(crate) struct JumpIfNotResumeKind; impl Operation for JumpIfNotResumeKind { const NAME: &'static str = "JumpIfNotResumeKind"; const INSTRUCTION: &'static str = "INST - JumpIfNotResumeKind"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let exit = context.vm.read::(); @@ -228,6 +232,7 @@ pub(crate) struct GeneratorDelegateNext; impl Operation for GeneratorDelegateNext { const NAME: &'static str = "GeneratorDelegateNext"; const INSTRUCTION: &'static str = "INST - GeneratorDelegateNext"; + const COST: u8 = 18; fn execute(context: &mut Context<'_>) -> JsResult { let throw_method_undefined = context.vm.read::(); @@ -317,6 +322,7 @@ pub(crate) struct GeneratorDelegateResume; impl Operation for GeneratorDelegateResume { const NAME: &'static str = "GeneratorDelegateResume"; const INSTRUCTION: &'static str = "INST - GeneratorDelegateResume"; + const COST: u8 = 7; fn execute(context: &mut Context<'_>) -> JsResult { let return_gen = context.vm.read::(); diff --git a/boa_engine/src/vm/opcode/generator/yield_stm.rs b/boa_engine/src/vm/opcode/generator/yield_stm.rs index 477a5cf642..13c5be3f6c 100644 --- a/boa_engine/src/vm/opcode/generator/yield_stm.rs +++ b/boa_engine/src/vm/opcode/generator/yield_stm.rs @@ -14,6 +14,7 @@ pub(crate) struct GeneratorYield; impl Operation for GeneratorYield { const NAME: &'static str = "GeneratorYield"; const INSTRUCTION: &'static str = "INST - GeneratorYield"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -32,6 +33,7 @@ pub(crate) struct AsyncGeneratorYield; impl Operation for AsyncGeneratorYield { const NAME: &'static str = "AsyncGeneratorYield"; const INSTRUCTION: &'static str = "INST - AsyncGeneratorYield"; + const COST: u8 = 8; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/get/argument.rs b/boa_engine/src/vm/opcode/get/argument.rs index fc0e507a54..a27f23dab6 100644 --- a/boa_engine/src/vm/opcode/get/argument.rs +++ b/boa_engine/src/vm/opcode/get/argument.rs @@ -29,6 +29,7 @@ impl GetArgument { impl Operation for GetArgument { const NAME: &'static str = "GetArgument"; const INSTRUCTION: &'static str = "INST - GetArgument"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/get/function.rs b/boa_engine/src/vm/opcode/get/function.rs index 0c403bbc1f..2f96412d52 100644 --- a/boa_engine/src/vm/opcode/get/function.rs +++ b/boa_engine/src/vm/opcode/get/function.rs @@ -23,6 +23,7 @@ impl GetArrowFunction { impl Operation for GetArrowFunction { const NAME: &'static str = "GetArrowFunction"; const INSTRUCTION: &'static str = "INST - GetArrowFunction"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -60,6 +61,7 @@ impl GetAsyncArrowFunction { impl Operation for GetAsyncArrowFunction { const NAME: &'static str = "GetAsyncArrowFunction"; const INSTRUCTION: &'static str = "INST - GetAsyncArrowFunction"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -101,6 +103,7 @@ impl GetFunction { impl Operation for GetFunction { const NAME: &'static str = "GetFunction"; const INSTRUCTION: &'static str = "INST - GetFunction"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -145,6 +148,7 @@ impl GetFunctionAsync { impl Operation for GetFunctionAsync { const NAME: &'static str = "GetFunctionAsync"; const INSTRUCTION: &'static str = "INST - GetFunctionAsync"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/get/generator.rs b/boa_engine/src/vm/opcode/get/generator.rs index 83b99347d7..ab7689ff6b 100644 --- a/boa_engine/src/vm/opcode/get/generator.rs +++ b/boa_engine/src/vm/opcode/get/generator.rs @@ -23,6 +23,7 @@ impl GetGenerator { impl Operation for GetGenerator { const NAME: &'static str = "GetGenerator"; const INSTRUCTION: &'static str = "INST - GetGenerator"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -60,6 +61,7 @@ impl GetGeneratorAsync { impl Operation for GetGeneratorAsync { const NAME: &'static str = "GetGeneratorAsync"; const INSTRUCTION: &'static str = "INST - GetGeneratorAsync"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/get/name.rs b/boa_engine/src/vm/opcode/get/name.rs index 3181293970..f2b3ce87c9 100644 --- a/boa_engine/src/vm/opcode/get/name.rs +++ b/boa_engine/src/vm/opcode/get/name.rs @@ -31,6 +31,7 @@ impl GetName { impl Operation for GetName { const NAME: &'static str = "GetName"; const INSTRUCTION: &'static str = "INST - GetName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -69,6 +70,7 @@ impl GetLocator { impl Operation for GetLocator { const NAME: &'static str = "GetLocator"; const INSTRUCTION: &'static str = "INST - GetLocator"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -115,6 +117,7 @@ impl GetNameAndLocator { impl Operation for GetNameAndLocator { const NAME: &'static str = "GetNameAndLocator"; const INSTRUCTION: &'static str = "INST - GetNameAndLocator"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -169,6 +172,7 @@ impl GetNameOrUndefined { impl Operation for GetNameOrUndefined { const NAME: &'static str = "GetNameOrUndefined"; const INSTRUCTION: &'static str = "INST - GetNameOrUndefined"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); diff --git a/boa_engine/src/vm/opcode/get/private.rs b/boa_engine/src/vm/opcode/get/private.rs index 1ba7518d38..eb260f33b5 100644 --- a/boa_engine/src/vm/opcode/get/private.rs +++ b/boa_engine/src/vm/opcode/get/private.rs @@ -31,6 +31,7 @@ impl GetPrivateField { impl Operation for GetPrivateField { const NAME: &'static str = "GetPrivateField"; const INSTRUCTION: &'static str = "INST - GetPrivateField"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/get/property.rs b/boa_engine/src/vm/opcode/get/property.rs index fcc4deadc3..bf425b2967 100644 --- a/boa_engine/src/vm/opcode/get/property.rs +++ b/boa_engine/src/vm/opcode/get/property.rs @@ -32,6 +32,7 @@ impl GetPropertyByName { impl Operation for GetPropertyByName { const NAME: &'static str = "GetPropertyByName"; const INSTRUCTION: &'static str = "INST - GetPropertyByName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -59,6 +60,7 @@ pub(crate) struct GetPropertyByValue; impl Operation for GetPropertyByValue { const NAME: &'static str = "GetPropertyByValue"; const INSTRUCTION: &'static str = "INST - GetPropertyByValue"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let key = context.vm.pop(); @@ -105,6 +107,7 @@ pub(crate) struct GetPropertyByValuePush; impl Operation for GetPropertyByValuePush { const NAME: &'static str = "GetPropertyByValuePush"; const INSTRUCTION: &'static str = "INST - GetPropertyByValuePush"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let key = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/iteration/for_in.rs b/boa_engine/src/vm/opcode/iteration/for_in.rs index 6f26cd0ddb..ea56e37720 100644 --- a/boa_engine/src/vm/opcode/iteration/for_in.rs +++ b/boa_engine/src/vm/opcode/iteration/for_in.rs @@ -15,6 +15,7 @@ pub(crate) struct CreateForInIterator; impl Operation for CreateForInIterator { const NAME: &'static str = "CreateForInIterator"; const INSTRUCTION: &'static str = "INST - CreateForInIterator"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let object = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/iteration/get.rs b/boa_engine/src/vm/opcode/iteration/get.rs index 649a8e609e..2df6ffe5dd 100644 --- a/boa_engine/src/vm/opcode/iteration/get.rs +++ b/boa_engine/src/vm/opcode/iteration/get.rs @@ -14,6 +14,7 @@ pub(crate) struct GetIterator; impl Operation for GetIterator { const NAME: &'static str = "GetIterator"; const INSTRUCTION: &'static str = "INST - GetIterator"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let object = context.vm.pop(); @@ -33,6 +34,7 @@ pub(crate) struct GetAsyncIterator; impl Operation for GetAsyncIterator { const NAME: &'static str = "GetAsyncIterator"; const INSTRUCTION: &'static str = "INST - GetAsyncIterator"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let object = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/iteration/iterator.rs b/boa_engine/src/vm/opcode/iteration/iterator.rs index f0083ec710..b670911bb0 100644 --- a/boa_engine/src/vm/opcode/iteration/iterator.rs +++ b/boa_engine/src/vm/opcode/iteration/iterator.rs @@ -17,6 +17,7 @@ pub(crate) struct IteratorNext; impl Operation for IteratorNext { const NAME: &'static str = "IteratorNext"; const INSTRUCTION: &'static str = "INST - IteratorNext"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let mut iterator = context @@ -44,6 +45,7 @@ pub(crate) struct IteratorNextWithoutPop; impl Operation for IteratorNextWithoutPop { const NAME: &'static str = "IteratorNextWithoutPop"; const INSTRUCTION: &'static str = "INST - IteratorNextWithoutPop"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let mut iterator = context @@ -74,6 +76,7 @@ pub(crate) struct IteratorFinishAsyncNext; impl Operation for IteratorFinishAsyncNext { const NAME: &'static str = "IteratorFinishAsyncNext"; const INSTRUCTION: &'static str = "INST - IteratorFinishAsyncNext"; + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let mut iterator = context @@ -115,6 +118,7 @@ pub(crate) struct IteratorResult; impl Operation for IteratorResult { const NAME: &'static str = "IteratorResult"; const INSTRUCTION: &'static str = "INST - IteratorResult"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let last_result = context @@ -143,6 +147,7 @@ pub(crate) struct IteratorValue; impl Operation for IteratorValue { const NAME: &'static str = "IteratorValue"; const INSTRUCTION: &'static str = "INST - IteratorValue"; + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let mut iterator = context @@ -171,6 +176,7 @@ pub(crate) struct IteratorValueWithoutPop; impl Operation for IteratorValueWithoutPop { const NAME: &'static str = "IteratorValueWithoutPop"; const INSTRUCTION: &'static str = "INST - IteratorValueWithoutPop"; + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let mut iterator = context @@ -199,6 +205,7 @@ pub(crate) struct IteratorDone; impl Operation for IteratorDone { const NAME: &'static str = "IteratorDone"; const INSTRUCTION: &'static str = "INST - IteratorDone"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let done = context @@ -225,6 +232,7 @@ pub(crate) struct IteratorReturn; impl Operation for IteratorReturn { const NAME: &'static str = "IteratorReturn"; const INSTRUCTION: &'static str = "INST - IteratorReturn"; + const COST: u8 = 8; fn execute(context: &mut Context<'_>) -> JsResult { let record = context @@ -261,6 +269,7 @@ pub(crate) struct IteratorToArray; impl Operation for IteratorToArray { const NAME: &'static str = "IteratorToArray"; const INSTRUCTION: &'static str = "INST - IteratorToArray"; + const COST: u8 = 8; fn execute(context: &mut Context<'_>) -> JsResult { let mut iterator = context @@ -314,6 +323,7 @@ pub(crate) struct IteratorPop; impl Operation for IteratorPop { const NAME: &'static str = "IteratorPop"; const INSTRUCTION: &'static str = "INST - IteratorPop"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { context.vm.frame_mut().iterators.pop(); @@ -331,6 +341,7 @@ pub(crate) struct IteratorStackEmpty; impl Operation for IteratorStackEmpty { const NAME: &'static str = "IteratorStackEmpty"; const INSTRUCTION: &'static str = "INST - IteratorStackEmpty"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let is_empty = context.vm.frame().iterators.is_empty(); @@ -349,6 +360,7 @@ pub(crate) struct CreateIteratorResult; impl Operation for CreateIteratorResult { const NAME: &'static str = "CreateIteratorResult"; const INSTRUCTION: &'static str = "INST - CreateIteratorResult"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/iteration/loop_ops.rs b/boa_engine/src/vm/opcode/iteration/loop_ops.rs index 94fd85b0da..df1488a4c3 100644 --- a/boa_engine/src/vm/opcode/iteration/loop_ops.rs +++ b/boa_engine/src/vm/opcode/iteration/loop_ops.rs @@ -14,6 +14,7 @@ pub(crate) struct IncrementLoopIteration; impl Operation for IncrementLoopIteration { const NAME: &'static str = "IncrementLoopIteration"; const INSTRUCTION: &'static str = "INST - IncrementLoopIteration"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let previous_iteration_count = context.vm.frame_mut().loop_iteration_count; diff --git a/boa_engine/src/vm/opcode/meta/mod.rs b/boa_engine/src/vm/opcode/meta/mod.rs index 9fefa5d0d7..464c5645e1 100644 --- a/boa_engine/src/vm/opcode/meta/mod.rs +++ b/boa_engine/src/vm/opcode/meta/mod.rs @@ -16,6 +16,7 @@ pub(crate) struct NewTarget; impl Operation for NewTarget { const NAME: &'static str = "NewTarget"; const INSTRUCTION: &'static str = "INST - NewTarget"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let new_target = if let Some(new_target) = context @@ -44,6 +45,7 @@ pub(crate) struct ImportMeta; impl Operation for ImportMeta { const NAME: &'static str = "ImportMeta"; const INSTRUCTION: &'static str = "INST - ImportMeta"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { // Meta Properties diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index bc7d73cbfc..12c567553e 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -396,11 +396,11 @@ macro_rules! generate_opcodes { } impl Opcode { - const MAX: usize = 2usize.pow(8) * 3; + const MAX: usize = 2usize.pow(8); // TODO: see if this can be exposed on all features. #[allow(unused)] - const NAMES: [&'static str; Self::MAX] = [ + const NAMES: [&'static str; Self::MAX * 3] = [ $( $mapping)?)>::NAME),*, $( $mapping)?)>::NAME),*, $( $mapping)?)>::NAME),* @@ -414,7 +414,7 @@ macro_rules! generate_opcodes { Self::NAMES[self as usize] } - const INSTRUCTIONS: [&'static str; Self::MAX] = [ + const INSTRUCTIONS: [&'static str; Self::MAX * 3] = [ $( $mapping)?)>::INSTRUCTION),*, $( $mapping)?)>::INSTRUCTION),*, $( $mapping)?)>::INSTRUCTION),* @@ -426,7 +426,29 @@ macro_rules! generate_opcodes { Self::INSTRUCTIONS[self as usize] } - const EXECUTE_FNS: [fn(&mut Context<'_>) -> JsResult; Self::MAX] = [ + const SPEND_FNS: [fn(&mut Context<'_>, &mut u32) -> JsResult; Self::MAX] = [ + $( $mapping)?)>::spend_budget_and_execute),*, + ]; + + /// Spends the cost of this opcode into the provided budget and executes it. + pub(super) fn spend_budget_and_execute( + self, + context: &mut Context<'_>, + budget: &mut u32 + ) -> JsResult { + Self::SPEND_FNS[self as usize](context, budget) + } + + const COSTS: [u8; Self::MAX] = [ + $( $mapping)?)>::COST),*, + ]; + + /// Return the cost of this opcode. + pub(super) const fn cost(self) -> u8 { + Self::COSTS[self as usize] + } + + const EXECUTE_FNS: [fn(&mut Context<'_>) -> JsResult; Self::MAX * 3] = [ $( $mapping)?)>::execute),*, $( $mapping)?)>::execute_with_u16_operands),*, $( $mapping)?)>::execute_with_u32_operands),* @@ -440,7 +462,7 @@ macro_rules! generate_opcodes { /// This represents a VM instruction, it contains both opcode and operands. /// // TODO: An instruction should be a representation of a valid executable instruction (opcode + operands), - // so variants like `ResevedN`, or operand width prefix modifiers, idealy shouldn't + // so variants like `ReservedN`, or operand width prefix modifiers, idealy shouldn't // be a part of `Instruction`. #[derive(Debug, Clone, PartialEq)] #[repr(u8)] @@ -530,6 +552,7 @@ macro_rules! generate_opcodes { pub(crate) trait Operation { const NAME: &'static str; const INSTRUCTION: &'static str; + const COST: u8; /// Execute opcode with [`VaryingOperandKind::U8`] sized [`VaryingOperand`]s. fn execute(context: &mut Context<'_>) -> JsResult; @@ -547,6 +570,15 @@ pub(crate) trait Operation { fn execute_with_u32_operands(context: &mut Context<'_>) -> JsResult { Reserved::execute_with_u32_operands(context) } + + /// Spends the cost of this operation into `budget` and runs `execute`. + fn spend_budget_and_execute( + context: &mut Context<'_>, + budget: &mut u32, + ) -> JsResult { + *budget = budget.saturating_sub(u32::from(Self::COST)); + Self::execute(context) + } } generate_opcodes! { diff --git a/boa_engine/src/vm/opcode/modifier.rs b/boa_engine/src/vm/opcode/modifier.rs index 96999ea40c..dbfa590f68 100644 --- a/boa_engine/src/vm/opcode/modifier.rs +++ b/boa_engine/src/vm/opcode/modifier.rs @@ -12,11 +12,22 @@ pub(crate) struct U16Operands; impl Operation for U16Operands { const NAME: &'static str = "U16Operands"; const INSTRUCTION: &'static str = "INST - U16Operands"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let opcode = context.vm.read::() as usize; - Opcode::EXECUTE_FNS[256 + opcode](context) + Opcode::EXECUTE_FNS[Opcode::MAX + opcode](context) + } + + fn spend_budget_and_execute( + context: &mut Context<'_>, + budget: &mut u32, + ) -> JsResult { + let opcode: Opcode = context.vm.read::().into(); + + *budget = budget.saturating_sub(u32::from(opcode.cost()) + u32::from(Self::COST)); + Opcode::EXECUTE_FNS[Opcode::MAX + opcode as usize](context) } } @@ -30,10 +41,21 @@ pub(crate) struct U32Operands; impl Operation for U32Operands { const NAME: &'static str = "U32Operands"; const INSTRUCTION: &'static str = "INST - U32Operands"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let opcode = context.vm.read::() as usize; - Opcode::EXECUTE_FNS[256 * 2 + opcode](context) + Opcode::EXECUTE_FNS[Opcode::MAX * 2 + opcode](context) + } + + fn spend_budget_and_execute( + context: &mut Context<'_>, + budget: &mut u32, + ) -> JsResult { + let opcode: Opcode = context.vm.read::().into(); + + *budget = budget.saturating_sub(u32::from(opcode.cost()) + u32::from(Self::COST)); + Opcode::EXECUTE_FNS[Opcode::MAX * 2 + opcode as usize](context) } } diff --git a/boa_engine/src/vm/opcode/new/mod.rs b/boa_engine/src/vm/opcode/new/mod.rs index c1dca0965d..9ca33ee2ba 100644 --- a/boa_engine/src/vm/opcode/new/mod.rs +++ b/boa_engine/src/vm/opcode/new/mod.rs @@ -31,6 +31,7 @@ impl New { impl Operation for New { const NAME: &'static str = "New"; const INSTRUCTION: &'static str = "INST - New"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let argument_count = context.vm.read::() as usize; @@ -58,6 +59,7 @@ pub(crate) struct NewSpread; impl Operation for NewSpread { const NAME: &'static str = "NewSpread"; const INSTRUCTION: &'static str = "INST - NewSpread"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { // Get the arguments that are stored as an array object on the stack. diff --git a/boa_engine/src/vm/opcode/nop/mod.rs b/boa_engine/src/vm/opcode/nop/mod.rs index 8fd9529725..290173c66a 100644 --- a/boa_engine/src/vm/opcode/nop/mod.rs +++ b/boa_engine/src/vm/opcode/nop/mod.rs @@ -13,6 +13,7 @@ pub(crate) struct Nop; impl Operation for Nop { const NAME: &'static str = "Nop"; const INSTRUCTION: &'static str = "INST - Nop"; + const COST: u8 = 1; fn execute(_: &mut Context<'_>) -> JsResult { Ok(CompletionType::Normal) @@ -29,6 +30,7 @@ pub(crate) struct Reserved; impl Operation for Reserved { const NAME: &'static str = "Reserved"; const INSTRUCTION: &'static str = "INST - Reserved"; + const COST: u8 = 0; fn execute(_: &mut Context<'_>) -> JsResult { unreachable!("Reserved opcodes are unreachable!") diff --git a/boa_engine/src/vm/opcode/pop/mod.rs b/boa_engine/src/vm/opcode/pop/mod.rs index 875bfed748..84c21b9b7b 100644 --- a/boa_engine/src/vm/opcode/pop/mod.rs +++ b/boa_engine/src/vm/opcode/pop/mod.rs @@ -13,6 +13,7 @@ pub(crate) struct Pop; impl Operation for Pop { const NAME: &'static str = "Pop"; const INSTRUCTION: &'static str = "INST - Pop"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let _val = context.vm.pop(); @@ -30,6 +31,7 @@ pub(crate) struct PopEnvironment; impl Operation for PopEnvironment { const NAME: &'static str = "PopEnvironment"; const INSTRUCTION: &'static str = "INST - PopEnvironment"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { context.vm.environments.pop(); diff --git a/boa_engine/src/vm/opcode/push/array.rs b/boa_engine/src/vm/opcode/push/array.rs index 3783a10c6c..748adba8ff 100644 --- a/boa_engine/src/vm/opcode/push/array.rs +++ b/boa_engine/src/vm/opcode/push/array.rs @@ -16,6 +16,7 @@ pub(crate) struct PushNewArray; impl Operation for PushNewArray { const NAME: &'static str = "PushNewArray"; const INSTRUCTION: &'static str = "INST - PushNewArray"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let array = context @@ -38,6 +39,7 @@ pub(crate) struct PushValueToArray; impl Operation for PushValueToArray { const NAME: &'static str = "PushValueToArray"; const INSTRUCTION: &'static str = "INST - PushValueToArray"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -63,6 +65,7 @@ pub(crate) struct PushElisionToArray; impl Operation for PushElisionToArray { const NAME: &'static str = "PushElisionToArray"; const INSTRUCTION: &'static str = "INST - PushElisionToArray"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let array = context.vm.pop(); @@ -88,6 +91,7 @@ pub(crate) struct PushIteratorToArray; impl Operation for PushIteratorToArray { const NAME: &'static str = "PushIteratorToArray"; const INSTRUCTION: &'static str = "INST - PushIteratorToArray"; + const COST: u8 = 8; fn execute(context: &mut Context<'_>) -> JsResult { let mut iterator = context diff --git a/boa_engine/src/vm/opcode/push/class/field.rs b/boa_engine/src/vm/opcode/push/class/field.rs index 5b24802437..3db65fd9b8 100644 --- a/boa_engine/src/vm/opcode/push/class/field.rs +++ b/boa_engine/src/vm/opcode/push/class/field.rs @@ -14,6 +14,7 @@ pub(crate) struct PushClassField; impl Operation for PushClassField { const NAME: &'static str = "PushClassField"; const INSTRUCTION: &'static str = "INST - PushClassField"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let field_function_value = context.vm.pop(); @@ -85,6 +86,7 @@ impl PushClassFieldPrivate { impl Operation for PushClassFieldPrivate { const NAME: &'static str = "PushClassFieldPrivate"; const INSTRUCTION: &'static str = "INST - PushClassFieldPrivate"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/push/class/mod.rs b/boa_engine/src/vm/opcode/push/class/mod.rs index 28e7c183b0..a7c59e8b48 100644 --- a/boa_engine/src/vm/opcode/push/class/mod.rs +++ b/boa_engine/src/vm/opcode/push/class/mod.rs @@ -22,6 +22,7 @@ pub(crate) struct PushClassPrototype; impl Operation for PushClassPrototype { const NAME: &'static str = "PushClassPrototype"; const INSTRUCTION: &'static str = "INST - PushClassPrototype"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let superclass = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/push/class/private.rs b/boa_engine/src/vm/opcode/push/class/private.rs index 0cfe63d255..cb41a2c109 100644 --- a/boa_engine/src/vm/opcode/push/class/private.rs +++ b/boa_engine/src/vm/opcode/push/class/private.rs @@ -51,6 +51,7 @@ impl PushClassPrivateMethod { impl Operation for PushClassPrivateMethod { const NAME: &'static str = "PushClassPrivateMethod"; const INSTRUCTION: &'static str = "INST - PushClassPrivateMethod"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -103,6 +104,7 @@ impl PushClassPrivateGetter { impl Operation for PushClassPrivateGetter { const NAME: &'static str = "PushClassPrivateGetter"; const INSTRUCTION: &'static str = "INST - PushClassPrivateGetter"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -155,6 +157,7 @@ impl PushClassPrivateSetter { impl Operation for PushClassPrivateSetter { const NAME: &'static str = "PushClassPrivateSetter"; const INSTRUCTION: &'static str = "INST - PushClassPrivateSetter"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/push/environment.rs b/boa_engine/src/vm/opcode/push/environment.rs index 0f80166fd6..e903372400 100644 --- a/boa_engine/src/vm/opcode/push/environment.rs +++ b/boa_engine/src/vm/opcode/push/environment.rs @@ -28,6 +28,7 @@ impl PushDeclarativeEnvironment { impl Operation for PushDeclarativeEnvironment { const NAME: &'static str = "PushDeclarativeEnvironment"; const INSTRUCTION: &'static str = "INST - PushDeclarativeEnvironment"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let compile_environments_index = context.vm.read::() as usize; @@ -55,6 +56,7 @@ pub(crate) struct PushObjectEnvironment; impl Operation for PushObjectEnvironment { const NAME: &'static str = "PushObjectEnvironment"; const INSTRUCTION: &'static str = "INST - PushObjectEnvironment"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let object = context.vm.pop(); @@ -75,6 +77,7 @@ pub(crate) struct PushPrivateEnvironment; impl Operation for PushPrivateEnvironment { const NAME: &'static str = "PushPrivateEnvironment"; const INSTRUCTION: &'static str = "INST - PushPrivateEnvironment"; + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let class_value = context.vm.pop(); @@ -114,6 +117,7 @@ pub(crate) struct PopPrivateEnvironment; impl Operation for PopPrivateEnvironment { const NAME: &'static str = "PopPrivateEnvironment"; const INSTRUCTION: &'static str = "INST - PopPrivateEnvironment"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { context.vm.environments.pop_private(); diff --git a/boa_engine/src/vm/opcode/push/literal.rs b/boa_engine/src/vm/opcode/push/literal.rs index 2ffd2b0d44..f684c7223d 100644 --- a/boa_engine/src/vm/opcode/push/literal.rs +++ b/boa_engine/src/vm/opcode/push/literal.rs @@ -23,6 +23,7 @@ impl PushLiteral { impl Operation for PushLiteral { const NAME: &'static str = "PushLiteral"; const INSTRUCTION: &'static str = "INST - PushLiteral"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -65,6 +66,7 @@ impl PushRegExp { impl Operation for PushRegExp { const NAME: &'static str = "PushRegExp"; const INSTRUCTION: &'static str = "INST - PushRegExp"; + const COST: u8 = 5; fn execute(context: &mut Context<'_>) -> JsResult { let pattern_index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/push/mod.rs b/boa_engine/src/vm/opcode/push/mod.rs index 32451aad00..bf52183995 100644 --- a/boa_engine/src/vm/opcode/push/mod.rs +++ b/boa_engine/src/vm/opcode/push/mod.rs @@ -29,6 +29,7 @@ macro_rules! implement_push_generics { impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { context.vm.push($push_value); diff --git a/boa_engine/src/vm/opcode/push/numbers.rs b/boa_engine/src/vm/opcode/push/numbers.rs index fec5825e47..3e42ffe3c2 100644 --- a/boa_engine/src/vm/opcode/push/numbers.rs +++ b/boa_engine/src/vm/opcode/push/numbers.rs @@ -15,6 +15,7 @@ macro_rules! implement_push_numbers_with_conversion { impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.read::<$num_type>(); @@ -37,6 +38,7 @@ macro_rules! implement_push_numbers_no_conversion { impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.read::<$num_type>(); diff --git a/boa_engine/src/vm/opcode/push/object.rs b/boa_engine/src/vm/opcode/push/object.rs index 46089038ed..2e6f97f15d 100644 --- a/boa_engine/src/vm/opcode/push/object.rs +++ b/boa_engine/src/vm/opcode/push/object.rs @@ -14,6 +14,7 @@ pub(crate) struct PushEmptyObject; impl Operation for PushEmptyObject { const NAME: &'static str = "PushEmptyObject"; const INSTRUCTION: &'static str = "INST - PushEmptyObject"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let o = context diff --git a/boa_engine/src/vm/opcode/require/mod.rs b/boa_engine/src/vm/opcode/require/mod.rs index a3adb2ccab..7fa2f5e497 100644 --- a/boa_engine/src/vm/opcode/require/mod.rs +++ b/boa_engine/src/vm/opcode/require/mod.rs @@ -13,6 +13,7 @@ pub(crate) struct RequireObjectCoercible; impl Operation for RequireObjectCoercible { const NAME: &'static str = "RequireObjectCoercible"; const INSTRUCTION: &'static str = "INST - RequireObjectCoercible"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/rest_parameter/mod.rs b/boa_engine/src/vm/opcode/rest_parameter/mod.rs index 2855b52a68..052e336d30 100644 --- a/boa_engine/src/vm/opcode/rest_parameter/mod.rs +++ b/boa_engine/src/vm/opcode/rest_parameter/mod.rs @@ -14,6 +14,7 @@ pub(crate) struct RestParameterInit; impl Operation for RestParameterInit { const NAME: &'static str = "RestParameterInit"; const INSTRUCTION: &'static str = "INST - RestParameterInit"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let arg_count = context.vm.frame().argument_count as usize; diff --git a/boa_engine/src/vm/opcode/set/class_prototype.rs b/boa_engine/src/vm/opcode/set/class_prototype.rs index 7c4b316a35..32f24920ec 100644 --- a/boa_engine/src/vm/opcode/set/class_prototype.rs +++ b/boa_engine/src/vm/opcode/set/class_prototype.rs @@ -15,6 +15,7 @@ pub(crate) struct SetClassPrototype; impl Operation for SetClassPrototype { const NAME: &'static str = "SetClassPrototype"; const INSTRUCTION: &'static str = "INST - SetClassPrototype"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let prototype_value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/set/home_object.rs b/boa_engine/src/vm/opcode/set/home_object.rs index 474d43a6f8..cde9c65fbb 100644 --- a/boa_engine/src/vm/opcode/set/home_object.rs +++ b/boa_engine/src/vm/opcode/set/home_object.rs @@ -13,6 +13,7 @@ pub(crate) struct SetHomeObject; impl Operation for SetHomeObject { const NAME: &'static str = "SetHomeObject"; const INSTRUCTION: &'static str = "INST - SetHomeObject"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let function = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/set/name.rs b/boa_engine/src/vm/opcode/set/name.rs index d292d56bbd..fe027bc997 100644 --- a/boa_engine/src/vm/opcode/set/name.rs +++ b/boa_engine/src/vm/opcode/set/name.rs @@ -27,6 +27,7 @@ impl ThrowMutateImmutable { impl Operation for ThrowMutateImmutable { const NAME: &'static str = "ThrowMutateImmutable"; const INSTRUCTION: &'static str = "INST - ThrowMutateImmutable"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -73,6 +74,7 @@ impl SetName { impl Operation for SetName { const NAME: &'static str = "SetName"; const INSTRUCTION: &'static str = "INST - SetName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -100,6 +102,7 @@ pub(crate) struct SetNameByLocator; impl Operation for SetNameByLocator { const NAME: &'static str = "SetNameByLocator"; const INSTRUCTION: &'static str = "INST - SetNameByLocator"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let binding_locator = context diff --git a/boa_engine/src/vm/opcode/set/private.rs b/boa_engine/src/vm/opcode/set/private.rs index b2fd5151b5..3916bc67f3 100644 --- a/boa_engine/src/vm/opcode/set/private.rs +++ b/boa_engine/src/vm/opcode/set/private.rs @@ -36,6 +36,7 @@ impl SetPrivateField { impl Operation for SetPrivateField { const NAME: &'static str = "SetPrivateField"; const INSTRUCTION: &'static str = "INST - SetPrivateField"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -81,6 +82,7 @@ impl DefinePrivateField { impl Operation for DefinePrivateField { const NAME: &'static str = "DefinePrivateField"; const INSTRUCTION: &'static str = "INST - DefinePrivateField"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -140,6 +142,7 @@ impl SetPrivateMethod { impl Operation for SetPrivateMethod { const NAME: &'static str = "SetPrivateMethod"; const INSTRUCTION: &'static str = "INST - SetPrivateMethod"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -190,6 +193,7 @@ impl SetPrivateSetter { impl Operation for SetPrivateSetter { const NAME: &'static str = "SetPrivateSetter"; const INSTRUCTION: &'static str = "INST - SetPrivateSetter"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -240,6 +244,7 @@ impl SetPrivateGetter { impl Operation for SetPrivateGetter { const NAME: &'static str = "SetPrivateGetter"; const INSTRUCTION: &'static str = "INST - SetPrivateGetter"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/set/property.rs b/boa_engine/src/vm/opcode/set/property.rs index cd044840ca..93b8a8c6d4 100644 --- a/boa_engine/src/vm/opcode/set/property.rs +++ b/boa_engine/src/vm/opcode/set/property.rs @@ -41,6 +41,7 @@ impl SetPropertyByName { impl Operation for SetPropertyByName { const NAME: &'static str = "SetPropertyByName"; const INSTRUCTION: &'static str = "INST - SetPropertyByName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::(); @@ -68,6 +69,7 @@ pub(crate) struct SetPropertyByValue; impl Operation for SetPropertyByValue { const NAME: &'static str = "SetPropertyByValue"; const INSTRUCTION: &'static str = "INST - SetPropertyByValue"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -197,6 +199,7 @@ impl SetPropertyGetterByName { impl Operation for SetPropertyGetterByName { const NAME: &'static str = "SetPropertyGetterByName"; const INSTRUCTION: &'static str = "INST - SetPropertyGetterByName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -224,6 +227,7 @@ pub(crate) struct SetPropertyGetterByValue; impl Operation for SetPropertyGetterByValue { const NAME: &'static str = "SetPropertyGetterByValue"; const INSTRUCTION: &'static str = "INST - SetPropertyGetterByValue"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -285,6 +289,7 @@ impl SetPropertySetterByName { impl Operation for SetPropertySetterByName { const NAME: &'static str = "SetPropertySetterByName"; const INSTRUCTION: &'static str = "INST - SetPropertySetterByName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let index = context.vm.read::() as usize; @@ -312,6 +317,7 @@ pub(crate) struct SetPropertySetterByValue; impl Operation for SetPropertySetterByValue { const NAME: &'static str = "SetPropertySetterByValue"; const INSTRUCTION: &'static str = "INST - SetPropertySetterByValue"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -348,6 +354,7 @@ pub(crate) struct SetFunctionName; impl Operation for SetFunctionName { const NAME: &'static str = "SetFunctionName"; const INSTRUCTION: &'static str = "INST - SetFunctionName"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let prefix = context.vm.read::(); diff --git a/boa_engine/src/vm/opcode/set/prototype.rs b/boa_engine/src/vm/opcode/set/prototype.rs index 733410e9e3..77ef64eefc 100644 --- a/boa_engine/src/vm/opcode/set/prototype.rs +++ b/boa_engine/src/vm/opcode/set/prototype.rs @@ -13,6 +13,7 @@ pub(crate) struct SetPrototype; impl Operation for SetPrototype { const NAME: &'static str = "SetPrototype"; const INSTRUCTION: &'static str = "INST - SetPrototype"; + const COST: u8 = 4; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/swap/mod.rs b/boa_engine/src/vm/opcode/swap/mod.rs index 9d2ba8b52f..c3e0118959 100644 --- a/boa_engine/src/vm/opcode/swap/mod.rs +++ b/boa_engine/src/vm/opcode/swap/mod.rs @@ -13,6 +13,7 @@ pub(crate) struct Swap; impl Operation for Swap { const NAME: &'static str = "Swap"; const INSTRUCTION: &'static str = "INST - Swap"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let len = context.vm.stack.len(); @@ -32,6 +33,7 @@ pub(crate) struct RotateLeft; impl Operation for RotateLeft { const NAME: &'static str = "RotateLeft"; const INSTRUCTION: &'static str = "INST - RotateLeft"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let n = context.vm.read::() as usize; @@ -51,6 +53,7 @@ pub(crate) struct RotateRight; impl Operation for RotateRight { const NAME: &'static str = "RotateRight"; const INSTRUCTION: &'static str = "INST - RotateRight"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let n = context.vm.read::() as usize; diff --git a/boa_engine/src/vm/opcode/switch/mod.rs b/boa_engine/src/vm/opcode/switch/mod.rs index 9633df4fbd..d65124794f 100644 --- a/boa_engine/src/vm/opcode/switch/mod.rs +++ b/boa_engine/src/vm/opcode/switch/mod.rs @@ -14,6 +14,7 @@ pub(crate) struct Case; impl Operation for Case { const NAME: &'static str = "Case"; const INSTRUCTION: &'static str = "INST - Case"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let address = context.vm.read::(); @@ -39,6 +40,7 @@ pub(crate) struct Default; impl Operation for Default { const NAME: &'static str = "Default"; const INSTRUCTION: &'static str = "INST - Default"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let exit = context.vm.read::(); diff --git a/boa_engine/src/vm/opcode/templates/mod.rs b/boa_engine/src/vm/opcode/templates/mod.rs index ebb58fa764..397563d386 100644 --- a/boa_engine/src/vm/opcode/templates/mod.rs +++ b/boa_engine/src/vm/opcode/templates/mod.rs @@ -17,6 +17,7 @@ pub(crate) struct TemplateLookup; impl Operation for TemplateLookup { const NAME: &'static str = "TemplateLookup"; const INSTRUCTION: &'static str = "INST - TemplateLookup"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let jump = context.vm.read::(); @@ -101,6 +102,7 @@ impl TemplateCreate { impl Operation for TemplateCreate { const NAME: &'static str = "TemplateCreate"; const INSTRUCTION: &'static str = "INST - TemplateCreate"; + const COST: u8 = 6; fn execute(context: &mut Context<'_>) -> JsResult { let count = u32::from(context.vm.read::()); diff --git a/boa_engine/src/vm/opcode/to/mod.rs b/boa_engine/src/vm/opcode/to/mod.rs index 80f278edb3..38c8ffe564 100644 --- a/boa_engine/src/vm/opcode/to/mod.rs +++ b/boa_engine/src/vm/opcode/to/mod.rs @@ -13,6 +13,7 @@ pub(crate) struct ToBoolean; impl Operation for ToBoolean { const NAME: &'static str = "ToBoolean"; const INSTRUCTION: &'static str = "INST - ToBoolean"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -31,6 +32,7 @@ pub(crate) struct ToPropertyKey; impl Operation for ToPropertyKey { const NAME: &'static str = "ToPropertyKey"; const INSTRUCTION: &'static str = "INST - ToPropertyKey"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/unary_ops/decrement.rs b/boa_engine/src/vm/opcode/unary_ops/decrement.rs index 3e1512d541..41ef62e3f2 100644 --- a/boa_engine/src/vm/opcode/unary_ops/decrement.rs +++ b/boa_engine/src/vm/opcode/unary_ops/decrement.rs @@ -14,6 +14,7 @@ pub(crate) struct Dec; impl Operation for Dec { const NAME: &'static str = "Dec"; const INSTRUCTION: &'static str = "INST - Dec"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -42,6 +43,7 @@ pub(crate) struct DecPost; impl Operation for DecPost { const NAME: &'static str = "DecPost"; const INSTRUCTION: &'static str = "INST - DecPost"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/unary_ops/increment.rs b/boa_engine/src/vm/opcode/unary_ops/increment.rs index 1967c399eb..c18d0e5e15 100644 --- a/boa_engine/src/vm/opcode/unary_ops/increment.rs +++ b/boa_engine/src/vm/opcode/unary_ops/increment.rs @@ -14,6 +14,7 @@ pub(crate) struct Inc; impl Operation for Inc { const NAME: &'static str = "Inc"; const INSTRUCTION: &'static str = "INST - Inc"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -42,6 +43,7 @@ pub(crate) struct IncPost; impl Operation for IncPost { const NAME: &'static str = "IncPost"; const INSTRUCTION: &'static str = "INST - IncPost"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/unary_ops/logical.rs b/boa_engine/src/vm/opcode/unary_ops/logical.rs index b39e25a2f7..0c7fef5465 100644 --- a/boa_engine/src/vm/opcode/unary_ops/logical.rs +++ b/boa_engine/src/vm/opcode/unary_ops/logical.rs @@ -13,6 +13,7 @@ pub(crate) struct LogicalNot; impl Operation for LogicalNot { const NAME: &'static str = "LogicalNot"; const INSTRUCTION: &'static str = "INST - LogicalNot"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/unary_ops/mod.rs b/boa_engine/src/vm/opcode/unary_ops/mod.rs index a0b5a88fbb..95680981b3 100644 --- a/boa_engine/src/vm/opcode/unary_ops/mod.rs +++ b/boa_engine/src/vm/opcode/unary_ops/mod.rs @@ -26,6 +26,7 @@ pub(crate) struct TypeOf; impl Operation for TypeOf { const NAME: &'static str = "TypeOf"; const INSTRUCTION: &'static str = "INST - TypeOf"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -44,6 +45,7 @@ pub(crate) struct Pos; impl Operation for Pos { const NAME: &'static str = "Pos"; const INSTRUCTION: &'static str = "INST - Pos"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -63,6 +65,7 @@ pub(crate) struct Neg; impl Operation for Neg { const NAME: &'static str = "Neg"; const INSTRUCTION: &'static str = "INST - Neg"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -84,6 +87,7 @@ pub(crate) struct BitNot; impl Operation for BitNot { const NAME: &'static str = "BitNot"; const INSTRUCTION: &'static str = "INST - BitNot"; + const COST: u8 = 3; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/unary_ops/void.rs b/boa_engine/src/vm/opcode/unary_ops/void.rs index b7a3b3642e..6cde2ed195 100644 --- a/boa_engine/src/vm/opcode/unary_ops/void.rs +++ b/boa_engine/src/vm/opcode/unary_ops/void.rs @@ -13,6 +13,7 @@ pub(crate) struct Void; impl Operation for Void { const NAME: &'static str = "Void"; const INSTRUCTION: &'static str = "INST - Void"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let _old = context.vm.pop(); diff --git a/boa_engine/src/vm/opcode/value/mod.rs b/boa_engine/src/vm/opcode/value/mod.rs index ce2e87eeb3..70890007f2 100644 --- a/boa_engine/src/vm/opcode/value/mod.rs +++ b/boa_engine/src/vm/opcode/value/mod.rs @@ -14,6 +14,7 @@ pub(crate) struct ValueNotNullOrUndefined; impl Operation for ValueNotNullOrUndefined { const NAME: &'static str = "ValueNotNullOrUndefined"; const INSTRUCTION: &'static str = "INST - ValueNotNullOrUndefined"; + const COST: u8 = 2; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop(); @@ -42,6 +43,7 @@ pub(crate) struct IsObject; impl Operation for IsObject { const NAME: &'static str = "IsObject"; const INSTRUCTION: &'static str = "INST - IsObject"; + const COST: u8 = 1; fn execute(context: &mut Context<'_>) -> JsResult { let value = context.vm.pop();