Skip to content

Commit

Permalink
Add example and remove feature flag
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Apr 28, 2023
1 parent 36621aa commit 8e8a83d
Show file tree
Hide file tree
Showing 11 changed files with 110 additions and 118 deletions.
2 changes: 1 addition & 1 deletion boa_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ repository.workspace = true
rust-version.workspace = true

[dependencies]
boa_engine = { workspace = true, features = ["deser", "flowgraph", "trace", "runtime-limits"] }
boa_engine = { workspace = true, features = ["deser", "flowgraph", "trace"] }
boa_ast = { workspace = true, features = ["serde"] }
boa_parser.workspace = true
boa_gc.workspace = true
Expand Down
3 changes: 0 additions & 3 deletions boa_engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ trace = []
# Enable Boa's additional ECMAScript features for web browsers.
annex-b = ["boa_parser/annex-b"]

# Enable Boa's runtime limits.
runtime-limits = []

[dependencies]
boa_interner.workspace = true
boa_gc = { workspace = true, features = [ "thinvec" ] }
Expand Down
4 changes: 0 additions & 4 deletions boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ use boa_interner::{Interner, Sym};
use boa_parser::{Error as ParseError, Parser};
use boa_profiler::Profiler;

#[cfg(feature = "runtime-limits")]
use crate::vm::RuntimeLimits;

/// ECMAScript context. It is the primary way to interact with the runtime.
Expand Down Expand Up @@ -553,10 +552,7 @@ impl<'host> Context<'host> {
pub fn job_queue(&self) -> MaybeShared<'host, dyn JobQueue> {
self.job_queue.clone()
}
}

#[cfg(feature = "runtime-limits")]
impl Context<'_> {
/// Get the [`RuntimeLimits`].
pub fn runtime_limits(&mut self) -> RuntimeLimits {
self.vm.runtime_limits
Expand Down
7 changes: 1 addition & 6 deletions boa_engine/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,14 +645,12 @@ impl JsNativeError {
/// Creates a new `JsNativeError` that indicates that the context exceeded the runtime limits.
#[must_use]
#[inline]
#[cfg(feature = "runtime-limits")]
pub fn runtime_limit() -> Self {
Self::new(JsNativeErrorKind::RuntimeLimit, Box::default(), None)
}

/// Check if it's a [`JsNativeErrorKind::RuntimeLimit`].
#[inline]
#[cfg(feature = "runtime-limits")]
pub const fn is_runtime_limit(&self) -> bool {
matches!(self.kind, JsNativeErrorKind::RuntimeLimit)
}
Expand Down Expand Up @@ -794,7 +792,6 @@ impl JsNativeError {
"The NoInstructionsRemain native error cannot be converted to an opaque type."
)
}
#[cfg(feature = "runtime-limits")]
JsNativeErrorKind::RuntimeLimit => {
panic!("The RuntimeLimit native error cannot be converted to an opaque type.")
}
Expand Down Expand Up @@ -943,7 +940,6 @@ pub enum JsNativeErrorKind {
NoInstructionsRemain,

/// Error thrown when a runtime limit is exceeded. It's not a valid JS error variant.
#[cfg(feature = "runtime-limits")]
RuntimeLimit,
}

Expand Down Expand Up @@ -974,10 +970,9 @@ impl std::fmt::Display for JsNativeErrorKind {
Self::Syntax => "SyntaxError",
Self::Type => "TypeError",
Self::Uri => "UriError",
Self::RuntimeLimit => "RuntimeLimit",
#[cfg(feature = "fuzz")]
Self::NoInstructionsRemain => "NoInstructionsRemain",
#[cfg(feature = "runtime-limits")]
Self::RuntimeLimit => "RuntimeLimit",
}
.fmt(f)
}
Expand Down
31 changes: 2 additions & 29 deletions boa_engine/src/vm/call_frame/env_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ pub(crate) enum EnvEntryKind {
Global,
Loop {
/// This is used to keep track of how many iterations a loop has done.
#[cfg(feature = "runtime-limits")]
iteration_count: u64,
},
Try,
Expand Down Expand Up @@ -54,26 +53,8 @@ impl EnvStackEntry {

/// Returns calling `EnvStackEntry` with `kind` field of `Loop`.
/// And the loop iteration set to zero.
#[cfg(feature = "runtime-limits")]
pub(crate) const fn with_loop_flag(mut self) -> Self {
self.kind = EnvEntryKind::Loop { iteration_count: 0 };
self
}

/// Returns calling `EnvStackEntry` with `kind` field of `Loop`.
/// And the loop iteration set to the given value.
#[cfg(feature = "runtime-limits")]
pub(crate) const fn with_loop_flag_and_iteration_count(mut self, value: u64) -> Self {
self.kind = EnvEntryKind::Loop {
iteration_count: value,
};
self
}

/// Returns calling `EnvStackEntry` with `kind` field of `Loop`.
#[cfg(not(feature = "runtime-limits"))]
pub(crate) const fn with_loop_flag(mut self) -> Self {
self.kind = EnvEntryKind::Loop {};
pub(crate) const fn with_loop_flag(mut self, iteration_count: u64) -> Self {
self.kind = EnvEntryKind::Loop { iteration_count };
self
}

Expand Down Expand Up @@ -118,25 +99,17 @@ impl EnvStackEntry {
}

/// Returns true if an `EnvStackEntry` is a loop
#[cfg(feature = "runtime-limits")]
pub(crate) const fn is_loop_env(&self) -> bool {
matches!(self.kind, EnvEntryKind::Loop { .. })
}

#[cfg(feature = "runtime-limits")]
pub(crate) const fn as_loop_iteration_count(self) -> Option<u64> {
if let EnvEntryKind::Loop { iteration_count } = self.kind {
return Some(iteration_count);
}
None
}

/// Returns true if an `EnvStackEntry` is a loop
#[cfg(not(feature = "runtime-limits"))]
pub(crate) fn is_loop_env(&self) -> bool {
self.kind == EnvEntryKind::Loop {}
}

/// Returns true if an `EnvStackEntry` is a try block
pub(crate) fn is_try_env(&self) -> bool {
self.kind == EnvEntryKind::Try
Expand Down
22 changes: 7 additions & 15 deletions boa_engine/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,13 @@ mod code_block;
mod completion_record;
mod opcode;

#[cfg(feature = "runtime-limits")]
mod runtime_limits;

#[cfg(feature = "flowgraph")]
pub mod flowgraph;

pub use {call_frame::CallFrame, code_block::CodeBlock, opcode::Opcode};

#[cfg(feature = "runtime-limits")]
pub use runtime_limits::RuntimeLimits;
pub use {call_frame::CallFrame, code_block::CodeBlock, opcode::Opcode};

pub(crate) use {
call_frame::GeneratorResumeKind,
Expand All @@ -56,7 +53,6 @@ pub struct Vm {
pub(crate) environments: DeclarativeEnvironmentStack,
#[cfg(feature = "trace")]
pub(crate) trace: bool,
#[cfg(feature = "runtime-limits")]
pub(crate) runtime_limits: RuntimeLimits,
pub(crate) active_function: Option<JsObject>,
}
Expand All @@ -71,7 +67,6 @@ impl Vm {
err: None,
#[cfg(feature = "trace")]
trace: false,
#[cfg(feature = "runtime-limits")]
runtime_limits: RuntimeLimits::default(),
active_function: None,
}
Expand Down Expand Up @@ -281,15 +276,12 @@ impl Context<'_> {
}
}

#[cfg(feature = "runtime-limits")]
{
if let Some(native_error) = err.as_native() {
// If we hit the execution step limit, bubble up the error to the
// (Rust) caller instead of trying to handle as an exception.
if native_error.is_runtime_limit() {
self.vm.err = Some(err);
break CompletionType::Throw;
}
if let Some(native_error) = err.as_native() {
// If we hit the execution step limit, bubble up the error to the
// (Rust) caller instead of trying to handle as an exception.
if native_error.is_runtime_limit() {
self.vm.err = Some(err);
break CompletionType::Throw;
}
}

Expand Down
12 changes: 4 additions & 8 deletions boa_engine/src/vm/opcode/call/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ impl Operation for CallEval {
const INSTRUCTION: &'static str = "INST - CallEval";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
#[cfg(feature = "runtime-limits")]
if context.vm.runtime_limits.stack_size_limit() <= context.vm.stack.len() {
return Err(JsNativeError::range()
return Err(JsNativeError::runtime_limit()
.with_message("Maximum call stack size exceeded")
.into());
}
Expand Down Expand Up @@ -78,9 +77,8 @@ impl Operation for CallEvalSpread {
const INSTRUCTION: &'static str = "INST - CallEvalSpread";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
#[cfg(feature = "runtime-limits")]
if context.vm.runtime_limits.stack_size_limit() <= context.vm.stack.len() {
return Err(JsNativeError::range()
return Err(JsNativeError::runtime_limit()
.with_message("Maximum call stack size exceeded")
.into());
}
Expand Down Expand Up @@ -145,9 +143,8 @@ impl Operation for Call {
const INSTRUCTION: &'static str = "INST - Call";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
#[cfg(feature = "runtime-limits")]
if context.vm.runtime_limits.stack_size_limit() <= context.vm.stack.len() {
return Err(JsNativeError::range()
return Err(JsNativeError::runtime_limit()
.with_message("Maximum call stack size exceeded")
.into());
}
Expand Down Expand Up @@ -185,9 +182,8 @@ impl Operation for CallSpread {
const INSTRUCTION: &'static str = "INST - CallSpread";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
#[cfg(feature = "runtime-limits")]
if context.vm.runtime_limits.stack_size_limit() <= context.vm.stack.len() {
return Err(JsNativeError::range()
return Err(JsNativeError::runtime_limit()
.with_message("Maximum call stack size exceeded")
.into());
}
Expand Down
77 changes: 31 additions & 46 deletions boa_engine/src/vm/opcode/iteration/loop_ops.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::JsNativeError;
use crate::{
vm::{call_frame::EnvStackEntry, opcode::Operation, CompletionType},
Context, JsResult,
};

#[cfg(feature = "runtime-limits")]
use crate::JsNativeError;

/// `LoopStart` implements the Opcode Operation for `Opcode::LoopStart`
///
/// Operation:
Expand All @@ -22,12 +20,26 @@ impl Operation for LoopStart {
let exit = context.vm.read::<u32>();

// Create and push loop evironment entry.
let entry = EnvStackEntry::new(start, exit).with_loop_flag();
let entry = EnvStackEntry::new(start, exit).with_loop_flag(1);
context.vm.frame_mut().env_stack.push(entry);
Ok(CompletionType::Normal)
}
}

fn cleanup_loop_environment(context: &mut Context<'_>) {
let mut envs_to_pop = 0_usize;
while let Some(env_entry) = context.vm.frame_mut().env_stack.pop() {
envs_to_pop += env_entry.env_num();

if env_entry.is_loop_env() {
break;
}
}

let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.vm.environments.truncate(env_truncation_len);
}

/// `LoopContinue` implements the Opcode Operation for `Opcode::LoopContinue`.
///
/// Operation:
Expand All @@ -43,8 +55,7 @@ impl Operation for LoopContinue {
let start = context.vm.read::<u32>();
let exit = context.vm.read::<u32>();

#[cfg(feature = "runtime-limits")]
let mut iteration_count: u64 = 0;
let mut iteration_count = 0;

// 1. Clean up the previous environment.
if let Some(entry) = context
Expand All @@ -61,41 +72,26 @@ impl Operation for LoopContinue {
.saturating_sub(entry.env_num());
context.vm.environments.truncate(env_truncation_len);

#[cfg(feature = "runtime-limits")]
// Pop loop environment and get it's iteration count.
let previous_entry = context.vm.frame_mut().env_stack.pop();
if let Some(previous_iteration_count) =
previous_entry.and_then(EnvStackEntry::as_loop_iteration_count)
{
// Pop loop environment and get it's iteration count.
let previous_entry = context.vm.frame_mut().env_stack.pop();
let previous_iteration_count = previous_entry
.and_then(EnvStackEntry::as_loop_iteration_count)
.expect("there should be a loop environment");

// Increment loop iteration count.
iteration_count = previous_iteration_count + 1;
}
iteration_count = previous_iteration_count.wrapping_add(1);

// Pop loop environment.
#[cfg(not(feature = "runtime-limits"))]
context.vm.frame_mut().env_stack.pop();
}
let max = context.vm.runtime_limits.loop_iteration_limit();
if previous_iteration_count > max {
cleanup_loop_environment(context);

#[cfg(feature = "runtime-limits")]
{
let max = context.vm.runtime_limits.loop_iteration_limit();
if iteration_count >= max {
return Err(JsNativeError::runtime_limit()
.with_message(format!("max loop iteration limit {max} exceeded"))
.into());
return Err(JsNativeError::runtime_limit()
.with_message(format!("max loop iteration limit {max} exceeded"))
.into());
}
}
}

// 2. Push a new clean EnvStack.
let entry = EnvStackEntry::new(start, exit);

#[cfg(feature = "runtime-limits")]
let entry = entry.with_loop_flag_and_iteration_count(iteration_count);

#[cfg(not(feature = "runtime-limits"))]
let entry = entry.with_loop_flag();
let entry = EnvStackEntry::new(start, exit).with_loop_flag(iteration_count);

context.vm.frame_mut().env_stack.push(entry);

Expand All @@ -115,18 +111,7 @@ impl Operation for LoopEnd {
const INSTRUCTION: &'static str = "INST - LoopEnd";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
let mut envs_to_pop = 0_usize;
while let Some(env_entry) = context.vm.frame_mut().env_stack.pop() {
envs_to_pop += env_entry.env_num();

if env_entry.is_loop_env() {
break;
}
}

let env_truncation_len = context.vm.environments.len().saturating_sub(envs_to_pop);
context.vm.environments.truncate(env_truncation_len);

cleanup_loop_environment(context);
Ok(CompletionType::Normal)
}
}
6 changes: 2 additions & 4 deletions boa_engine/src/vm/opcode/new/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ impl Operation for New {
const INSTRUCTION: &'static str = "INST - New";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
#[cfg(feature = "runtime-limits")]
if context.vm.runtime_limits.stack_size_limit() <= context.vm.stack.len() {
return Err(JsNativeError::range()
return Err(JsNativeError::runtime_limit()
.with_message("Maximum call stack size exceeded")
.into());
}
Expand Down Expand Up @@ -56,9 +55,8 @@ impl Operation for NewSpread {
const INSTRUCTION: &'static str = "INST - NewSpread";

fn execute(context: &mut Context<'_>) -> JsResult<CompletionType> {
#[cfg(feature = "runtime-limits")]
if context.vm.runtime_limits.stack_size_limit() <= context.vm.stack.len() {
return Err(JsNativeError::range()
return Err(JsNativeError::runtime_limit()
.with_message("Maximum call stack size exceeded")
.into());
}
Expand Down
Loading

0 comments on commit 8e8a83d

Please sign in to comment.