mirror of https://github.com/boa-dev/boa.git
Haled Odat
2 years ago
committed by
GitHub
37 changed files with 620 additions and 214 deletions
@ -0,0 +1,35 @@ |
|||||||
|
use boa_engine::{ |
||||||
|
object::{FunctionObjectBuilder, ObjectInitializer}, |
||||||
|
property::Attribute, |
||||||
|
Context, JsArgs, JsObject, JsResult, JsValue, NativeFunction, |
||||||
|
}; |
||||||
|
|
||||||
|
fn get_loop(_: &JsValue, _: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let max = context.runtime_limits().loop_iteration_limit(); |
||||||
|
Ok(JsValue::from(max)) |
||||||
|
} |
||||||
|
|
||||||
|
fn set_loop(_: &JsValue, args: &[JsValue], context: &mut Context<'_>) -> JsResult<JsValue> { |
||||||
|
let value = args.get_or_undefined(0).to_length(context)?; |
||||||
|
context.runtime_limits_mut().set_loop_iteration_limit(value); |
||||||
|
Ok(JsValue::undefined()) |
||||||
|
} |
||||||
|
|
||||||
|
pub(super) fn create_object(context: &mut Context<'_>) -> JsObject { |
||||||
|
let get_loop = FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(get_loop)) |
||||||
|
.name("get loop") |
||||||
|
.length(0) |
||||||
|
.build(); |
||||||
|
let set_loop = FunctionObjectBuilder::new(context, NativeFunction::from_fn_ptr(set_loop)) |
||||||
|
.name("set loop") |
||||||
|
.length(1) |
||||||
|
.build(); |
||||||
|
ObjectInitializer::new(context) |
||||||
|
.accessor( |
||||||
|
"loop", |
||||||
|
Some(get_loop), |
||||||
|
Some(set_loop), |
||||||
|
Attribute::WRITABLE | Attribute::CONFIGURABLE | Attribute::NON_ENUMERABLE, |
||||||
|
) |
||||||
|
.build() |
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
/// Represents the limits of different runtime operations.
|
||||||
|
#[derive(Debug, Clone, Copy)] |
||||||
|
pub struct RuntimeLimits { |
||||||
|
/// Max stack size before an error is thrown.
|
||||||
|
stack_size_limit: usize, |
||||||
|
|
||||||
|
/// Max loop iterations before an error is thrown.
|
||||||
|
loop_iteration_limit: u64, |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for RuntimeLimits { |
||||||
|
#[inline] |
||||||
|
fn default() -> Self { |
||||||
|
Self { |
||||||
|
loop_iteration_limit: u64::MAX, |
||||||
|
stack_size_limit: 1024, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl RuntimeLimits { |
||||||
|
/// Return the loop iteration limit.
|
||||||
|
///
|
||||||
|
/// If the limit is exceeded in a loop it will throw and errror.
|
||||||
|
///
|
||||||
|
/// The limit value [`u64::MAX`] means that there is no limit.
|
||||||
|
#[inline] |
||||||
|
#[must_use] |
||||||
|
pub const fn loop_iteration_limit(&self) -> u64 { |
||||||
|
self.loop_iteration_limit |
||||||
|
} |
||||||
|
|
||||||
|
/// Set the loop iteration limit.
|
||||||
|
///
|
||||||
|
/// If the limit is exceeded in a loop it will throw and errror.
|
||||||
|
///
|
||||||
|
/// Setting the limit to [`u64::MAX`] means that there is no limit.
|
||||||
|
#[inline] |
||||||
|
pub fn set_loop_iteration_limit(&mut self, value: u64) { |
||||||
|
self.loop_iteration_limit = value; |
||||||
|
} |
||||||
|
|
||||||
|
/// Disable loop iteration limit.
|
||||||
|
#[inline] |
||||||
|
pub fn disable_loop_iteration_limit(&mut self) { |
||||||
|
self.loop_iteration_limit = u64::MAX; |
||||||
|
} |
||||||
|
|
||||||
|
/// Get max stack size.
|
||||||
|
#[inline] |
||||||
|
#[must_use] |
||||||
|
pub const fn stack_size_limit(&self) -> usize { |
||||||
|
self.stack_size_limit |
||||||
|
} |
||||||
|
|
||||||
|
/// Set max stack size before an error is thrown.
|
||||||
|
#[inline] |
||||||
|
pub fn set_stack_size_limit(&mut self, value: usize) { |
||||||
|
self.stack_size_limit = value; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
use boa_engine::{Context, Source}; |
||||||
|
|
||||||
|
fn main() { |
||||||
|
// Create the JavaScript context.
|
||||||
|
let mut context = Context::default(); |
||||||
|
|
||||||
|
// Set the context's runtime limit on loops to 10 iterations.
|
||||||
|
context.runtime_limits_mut().set_loop_iteration_limit(10); |
||||||
|
|
||||||
|
// The code below iterates 5 times, so no error is thrown.
|
||||||
|
let result = context.eval_script(Source::from_bytes( |
||||||
|
r" |
||||||
|
for (let i = 0; i < 5; ++i) { } |
||||||
|
", |
||||||
|
)); |
||||||
|
result.expect("no error should be thrown"); |
||||||
|
|
||||||
|
// Here we exceed the limit by 1 iteration and a `RuntimeLimit` error is thrown.
|
||||||
|
//
|
||||||
|
// This error cannot be caught in JavaScript it propagates to rust caller.
|
||||||
|
let result = context.eval_script(Source::from_bytes( |
||||||
|
r" |
||||||
|
try { |
||||||
|
for (let i = 0; i < 11; ++i) { } |
||||||
|
} catch (e) { |
||||||
|
|
||||||
|
} |
||||||
|
", |
||||||
|
)); |
||||||
|
result.expect_err("should have throw an error"); |
||||||
|
|
||||||
|
// Preventing an infinity loops
|
||||||
|
let result = context.eval_script(Source::from_bytes( |
||||||
|
r" |
||||||
|
while (true) { } |
||||||
|
", |
||||||
|
)); |
||||||
|
result.expect_err("should have throw an error"); |
||||||
|
|
||||||
|
// The limit applies to all types of loops.
|
||||||
|
let result = context.eval_script(Source::from_bytes( |
||||||
|
r" |
||||||
|
for (let e of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) { } |
||||||
|
", |
||||||
|
)); |
||||||
|
result.expect_err("should have throw an error"); |
||||||
|
} |
Loading…
Reference in new issue