Skip to content

Commit

Permalink
Implement runtime limits for loops (#2857)
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat authored May 5, 2023
1 parent e3d2056 commit 802d796
Show file tree
Hide file tree
Showing 37 changed files with 620 additions and 214 deletions.
35 changes: 35 additions & 0 deletions boa_cli/src/debug/limits.rs
Original file line number Diff line number Diff line change
@@ -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()
}
7 changes: 7 additions & 0 deletions boa_cli/src/debug/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use boa_engine::{object::ObjectInitializer, property::Attribute, Context, JsObje

mod function;
mod gc;
mod limits;
mod object;
mod optimizer;
mod realm;
Expand All @@ -17,6 +18,7 @@ fn create_boa_object(context: &mut Context<'_>) -> JsObject {
let optimizer_module = optimizer::create_object(context);
let gc_module = gc::create_object(context);
let realm_module = realm::create_object(context);
let limits_module = limits::create_object(context);

ObjectInitializer::new(context)
.property(
Expand Down Expand Up @@ -49,6 +51,11 @@ fn create_boa_object(context: &mut Context<'_>) -> JsObject {
realm_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
"limits",
limits_module,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.build()
}

Expand Down
21 changes: 9 additions & 12 deletions boa_engine/src/builtins/array/tests.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use super::Array;
use crate::{
builtins::{error::ErrorKind, Number},
run_test_actions, Context, JsValue, TestAction,
};
use crate::{builtins::Number, run_test_actions, Context, JsNativeErrorKind, JsValue, TestAction};
use indoc::indoc;

#[test]
Expand Down Expand Up @@ -191,7 +188,7 @@ fn flat_map_not_callable() {
var array = [1,2,3];
array.flatMap("not a function");
"#},
ErrorKind::Type,
JsNativeErrorKind::Type,
"flatMap mapper function is not callable",
)]);
}
Expand Down Expand Up @@ -639,7 +636,7 @@ fn reduce() {
// Empty array
TestAction::assert_native_error(
"[].reduce((acc, x) => acc + x);",
ErrorKind::Type,
JsNativeErrorKind::Type,
"Array.prototype.reduce: called on an empty array and with no initial value",
),
// Array with no defined elements
Expand All @@ -650,7 +647,7 @@ fn reduce() {
delete deleteArr[1];
deleteArr.reduce((acc, x) => acc + x);
"#},
ErrorKind::Type,
JsNativeErrorKind::Type,
"Array.prototype.reduce: called on an empty array and with no initial value",
),
// No callback
Expand All @@ -659,7 +656,7 @@ fn reduce() {
var someArr = [0, 1];
someArr.reduce('');
"#},
ErrorKind::Type,
JsNativeErrorKind::Type,
"Array.prototype.reduce: callback function is not callable",
),
]);
Expand Down Expand Up @@ -720,7 +717,7 @@ fn reduce_right() {
// Empty array
TestAction::assert_native_error(
"[].reduceRight((acc, x) => acc + x);",
ErrorKind::Type,
JsNativeErrorKind::Type,
"Array.prototype.reduceRight: called on an empty array and with no initial value",
),
// Array with no defined elements
Expand All @@ -731,7 +728,7 @@ fn reduce_right() {
delete deleteArr[1];
deleteArr.reduceRight((acc, x) => acc + x);
"#},
ErrorKind::Type,
JsNativeErrorKind::Type,
"Array.prototype.reduceRight: called on an empty array and with no initial value",
),
// No callback
Expand All @@ -740,7 +737,7 @@ fn reduce_right() {
var otherArr = [0, 1];
otherArr.reduceRight("");
"#},
ErrorKind::Type,
JsNativeErrorKind::Type,
"Array.prototype.reduceRight: callback function is not callable",
),
]);
Expand Down Expand Up @@ -865,7 +862,7 @@ fn array_spread_arrays() {
fn array_spread_non_iterable() {
run_test_actions([TestAction::assert_native_error(
"const array2 = [...5];",
ErrorKind::Type,
JsNativeErrorKind::Type,
"value with type `number` is not iterable",
)]);
}
Expand Down
48 changes: 28 additions & 20 deletions boa_engine/src/builtins/bigint/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{builtins::error::ErrorKind, run_test_actions, JsBigInt, TestAction};
use crate::{run_test_actions, JsBigInt, JsNativeErrorKind, TestAction};

#[test]
fn equality() {
Expand Down Expand Up @@ -62,17 +62,17 @@ fn bigint_function_throws() {
run_test_actions([
TestAction::assert_native_error(
"BigInt(0.1)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"cannot convert 0.1 to a BigInt",
),
TestAction::assert_native_error(
"BigInt(null)",
ErrorKind::Type,
JsNativeErrorKind::Type,
"cannot convert null to a BigInt",
),
TestAction::assert_native_error(
"BigInt(undefined)",
ErrorKind::Type,
JsNativeErrorKind::Type,
"cannot convert undefined to a BigInt",
),
]);
Expand Down Expand Up @@ -108,29 +108,37 @@ fn operations() {
),
TestAction::assert_eq("15000n / 50n", JsBigInt::from(300)),
TestAction::assert_eq("15001n / 50n", JsBigInt::from(300)),
TestAction::assert_native_error("1n/0n", ErrorKind::Range, "BigInt division by zero"),
TestAction::assert_native_error(
"1n/0n",
JsNativeErrorKind::Range,
"BigInt division by zero",
),
TestAction::assert_eq("15007n % 10n", JsBigInt::from(7)),
TestAction::assert_native_error("1n % 0n", ErrorKind::Range, "BigInt division by zero"),
TestAction::assert_native_error(
"1n % 0n",
JsNativeErrorKind::Range,
"BigInt division by zero",
),
TestAction::assert_eq(
"100n ** 10n",
JsBigInt::from_string("100000000000000000000").unwrap(),
),
TestAction::assert_native_error(
"10n ** (-10n)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"BigInt negative exponent",
),
TestAction::assert_eq("8n << 2n", JsBigInt::from(32)),
TestAction::assert_native_error(
"1000n << 1000000000000000n",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Maximum BigInt size exceeded",
),
TestAction::assert_eq("8n >> 2n", JsBigInt::from(2)),
// TODO: this should return 0n instead of throwing
TestAction::assert_native_error(
"1000n >> 1000000000000000n",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Maximum BigInt size exceeded",
),
]);
Expand All @@ -151,17 +159,17 @@ fn to_string_invalid_radix() {
run_test_actions([
TestAction::assert_native_error(
"10n.toString(null)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"radix must be an integer at least 2 and no greater than 36",
),
TestAction::assert_native_error(
"10n.toString(-1)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"radix must be an integer at least 2 and no greater than 36",
),
TestAction::assert_native_error(
"10n.toString(37)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"radix must be an integer at least 2 and no greater than 36",
),
]);
Expand Down Expand Up @@ -219,22 +227,22 @@ fn as_int_n_errors() {
run_test_actions([
TestAction::assert_native_error(
"BigInt.asIntN(-1, 0n)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Index must be between 0 and 2^53 - 1",
),
TestAction::assert_native_error(
"BigInt.asIntN(-2.5, 0n)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Index must be between 0 and 2^53 - 1",
),
TestAction::assert_native_error(
"BigInt.asIntN(9007199254740992, 0n)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Index must be between 0 and 2^53 - 1",
),
TestAction::assert_native_error(
"BigInt.asIntN(0n, 0n)",
ErrorKind::Type,
JsNativeErrorKind::Type,
"argument must not be a bigint",
),
]);
Expand Down Expand Up @@ -292,22 +300,22 @@ fn as_uint_n_errors() {
run_test_actions([
TestAction::assert_native_error(
"BigInt.asUintN(-1, 0n)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Index must be between 0 and 2^53 - 1",
),
TestAction::assert_native_error(
"BigInt.asUintN(-2.5, 0n)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Index must be between 0 and 2^53 - 1",
),
TestAction::assert_native_error(
"BigInt.asUintN(9007199254740992, 0n)",
ErrorKind::Range,
JsNativeErrorKind::Range,
"Index must be between 0 and 2^53 - 1",
),
TestAction::assert_native_error(
"BigInt.asUintN(0n, 0n)",
ErrorKind::Type,
JsNativeErrorKind::Type,
"argument must not be a bigint",
),
]);
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/builtins/date/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{builtins::error::ErrorKind, run_test_actions, TestAction};
use crate::{run_test_actions, JsNativeErrorKind, TestAction};
use chrono::{Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone};
use indoc::indoc;

Expand Down Expand Up @@ -47,7 +47,7 @@ fn timestamp_from_utc(
fn date_this_time_value() {
run_test_actions([TestAction::assert_native_error(
"({toString: Date.prototype.toString}).toString()",
ErrorKind::Type,
JsNativeErrorKind::Type,
"'this' is not a Date",
)]);
}
Expand Down
11 changes: 5 additions & 6 deletions boa_engine/src/builtins/function/tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::{
builtins::error::ErrorKind,
error::JsNativeError,
js_string,
native_function::NativeFunction,
object::{FunctionObjectBuilder, JsObject},
property::{Attribute, PropertyDescriptor},
run_test_actions, JsValue, TestAction,
run_test_actions, JsNativeErrorKind, JsValue, TestAction,
};
use indoc::indoc;

Expand Down Expand Up @@ -60,7 +59,7 @@ fn function_prototype() {
),
TestAction::assert_native_error(
"new Function.prototype()",
ErrorKind::Type,
JsNativeErrorKind::Type,
"not a constructor",
),
]);
Expand All @@ -81,7 +80,7 @@ fn function_prototype_call_throw() {
let call = Function.prototype.call;
call(call)
"#},
ErrorKind::Type,
JsNativeErrorKind::Type,
"undefined is not a function",
)]);
}
Expand Down Expand Up @@ -181,12 +180,12 @@ fn function_constructor_early_errors_super() {
run_test_actions([
TestAction::assert_native_error(
"Function('super()')()",
ErrorKind::Syntax,
JsNativeErrorKind::Syntax,
"invalid `super` call",
),
TestAction::assert_native_error(
"Function('super.a')()",
ErrorKind::Syntax,
JsNativeErrorKind::Syntax,
"invalid `super` reference",
),
]);
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/builtins/json/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use indoc::indoc;

use crate::{builtins::error::ErrorKind, run_test_actions, JsValue, TestAction};
use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction};

#[test]
fn json_sanity() {
Expand Down Expand Up @@ -307,7 +307,7 @@ fn json_fields_should_be_enumerable() {
fn json_parse_with_no_args_throws_syntax_error() {
run_test_actions([TestAction::assert_native_error(
"JSON.parse();",
ErrorKind::Syntax,
JsNativeErrorKind::Syntax,
"expected value at line 1 column 1",
)]);
}
4 changes: 2 additions & 2 deletions boa_engine/src/builtins/map/tests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{builtins::error::ErrorKind, run_test_actions, JsValue, TestAction};
use crate::{run_test_actions, JsNativeErrorKind, JsValue, TestAction};
use indoc::indoc;

#[test]
Expand Down Expand Up @@ -242,7 +242,7 @@ fn recursive_display() {
fn not_a_function() {
run_test_actions([TestAction::assert_native_error(
"let map = Map()",
ErrorKind::Type,
JsNativeErrorKind::Type,
"calling a builtin Map constructor without new is forbidden",
)]);
}
Expand Down
Loading

0 comments on commit 802d796

Please sign in to comment.