diff --git a/Cargo.lock b/Cargo.lock index 84a4dc13ebf..c9d4334e301 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "arbitrary" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arrayvec" version = "0.4.12" @@ -83,6 +92,7 @@ dependencies = [ name = "boa_engine" version = "0.14.0" dependencies = [ + "arbitrary", "bitflags", "boa_gc", "boa_interner", @@ -127,10 +137,22 @@ dependencies = [ "measureme", ] +[[package]] +name = "boa_inputgen" +version = "0.14.0" +dependencies = [ + "arbitrary", + "boa_engine", + "boa_interner", + "spin", + "string-interner", +] + [[package]] name = "boa_interner" version = "0.14.0" dependencies = [ + "arbitrary", "gc", "serde", "string-interner", @@ -382,6 +404,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "derive_arbitrary" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98e23c06c035dac87bd802d98f368df73a7f2cb05a66ffbd1f377e821fac4af9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -850,6 +883,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ + "arbitrary", "autocfg", "num-integer", "num-traits", @@ -1375,6 +1409,15 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + [[package]] name = "str-buf" version = "1.0.5" diff --git a/Cargo.toml b/Cargo.toml index d3517716906..c16692cce21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "boa_cli", "boa_engine", "boa_gc", + "boa_inputgen", "boa_interner", "boa_profiler", "boa_tester", diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 8e7ad1aaeea..2295443c38b 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -18,7 +18,10 @@ deser = ["boa_interner/serde"] # Enable Boa's WHATWG console object implementation. console = [] +fuzzer = ["arbitrary", "boa_interner/fuzzer", "num-bigint/arbitrary"] + [dependencies] +arbitrary = { version = "1", features = ["derive"], optional = true } boa_unicode = { path = "../boa_unicode", version = "0.14.0" } boa_interner = { path = "../boa_interner", version = "0.14.0" } boa_gc = { path = "../boa_gc", version = "0.14.0" } diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 4abde1b8550..8cdc6dcd22d 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -85,6 +85,10 @@ pub struct Context { /// Whether or not global strict mode is active. strict: bool, + /// The maximum number of instructions which will be executed in this context. + #[cfg(feature = "fuzzer")] + pub(crate) max_insns: usize, + pub(crate) vm: Vm, } @@ -97,6 +101,8 @@ impl Default for Context { console: Console::default(), intrinsics: Intrinsics::default(), strict: false, + #[cfg(feature = "fuzzer")] + max_insns: 1 << 20, vm: Vm { frame: None, stack: Vec::with_capacity(1024), @@ -723,4 +729,10 @@ impl Context { pub fn set_trace(&mut self, trace: bool) { self.vm.trace = trace; } + + /// Set the maximum number of instructions for this context + #[cfg(feature = "fuzzer")] + pub fn set_max_insns(&mut self, max_insns: usize) { + self.max_insns = max_insns; + } } diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index 9db8165bf50..605fce2f5a7 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -11,6 +11,8 @@ html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" )] #![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![cfg_attr(feature = "fuzzer", allow(clippy::use_self))] // false-positive on derive(Arbitrary) +#![cfg_attr(not(feature = "fuzzer"), deny(clippy::use_self))] #![warn( clippy::perf, clippy::single_match_else, @@ -26,7 +28,6 @@ clippy::all, clippy::cast_lossless, clippy::redundant_closure_for_method_calls, - clippy::use_self, clippy::unnested_or_patterns, clippy::trivially_copy_pass_by_ref, clippy::needless_pass_by_value, diff --git a/boa_engine/src/syntax/ast/constant.rs b/boa_engine/src/syntax/ast/constant.rs index 4fc1f3d20aa..1874eae2167 100644 --- a/boa_engine/src/syntax/ast/constant.rs +++ b/boa_engine/src/syntax/ast/constant.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Finalize, PartialEq)] pub enum Const { /// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks. diff --git a/boa_engine/src/syntax/ast/node/array/mod.rs b/boa_engine/src/syntax/ast/node/array/mod.rs index 697913c594f..75b5203b738 100644 --- a/boa_engine/src/syntax/ast/node/array/mod.rs +++ b/boa_engine/src/syntax/ast/node/array/mod.rs @@ -27,6 +27,7 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct ArrayDecl { #[cfg_attr(feature = "deser", serde(flatten))] @@ -59,6 +60,13 @@ impl AsRef<[Node]> for ArrayDecl { } } +#[cfg(feature = "fuzzer")] +impl AsMut<[Node]> for ArrayDecl { + fn as_mut(&mut self) -> &mut [Node] { + &mut self.arr + } +} + impl From for ArrayDecl where T: Into>, diff --git a/boa_engine/src/syntax/ast/node/await_expr/mod.rs b/boa_engine/src/syntax/ast/node/await_expr/mod.rs index f5a97030945..981ac185654 100644 --- a/boa_engine/src/syntax/ast/node/await_expr/mod.rs +++ b/boa_engine/src/syntax/ast/node/await_expr/mod.rs @@ -20,11 +20,19 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct AwaitExpr { expr: Box, } +impl AwaitExpr { + #[cfg(feature = "fuzzer")] + pub fn expr_mut(&mut self) -> &mut Node { + &mut self.expr + } +} + impl From for AwaitExpr where T: Into>, diff --git a/boa_engine/src/syntax/ast/node/block/mod.rs b/boa_engine/src/syntax/ast/node/block/mod.rs index 5f8d4118096..7b261ba0f8c 100644 --- a/boa_engine/src/syntax/ast/node/block/mod.rs +++ b/boa_engine/src/syntax/ast/node/block/mod.rs @@ -28,6 +28,7 @@ mod tests; /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[cfg_attr(feature = "deser", serde(transparent))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Block { #[cfg_attr(feature = "deser", serde(flatten))] @@ -40,6 +41,11 @@ impl Block { self.statements.items() } + #[cfg(feature = "fuzzer")] + pub fn items_mut(&mut self) -> &mut [Node] { + self.statements.items_mut() + } + pub(crate) fn lexically_declared_names(&self, interner: &Interner) -> FxHashSet { self.statements.lexically_declared_names(interner) } diff --git a/boa_engine/src/syntax/ast/node/call/mod.rs b/boa_engine/src/syntax/ast/node/call/mod.rs index 72b8580cf1b..36898aa8247 100644 --- a/boa_engine/src/syntax/ast/node/call/mod.rs +++ b/boa_engine/src/syntax/ast/node/call/mod.rs @@ -23,6 +23,7 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-CallExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Call { expr: Box, @@ -51,6 +52,16 @@ impl Call { pub fn args(&self) -> &[Node] { &self.args } + + #[cfg(feature = "fuzzer")] + pub fn expr_mut(&mut self) -> &mut Node { + &mut self.expr + } + + #[cfg(feature = "fuzzer")] + pub fn args_mut(&mut self) -> &mut [Node] { + &mut self.args + } } impl ToInternedString for Call { diff --git a/boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs b/boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs index 16766e842a8..a3a1296e6b8 100644 --- a/boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs +++ b/boa_engine/src/syntax/ast/node/conditional/conditional_op/mod.rs @@ -20,6 +20,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-ConditionalExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct ConditionalOp { condition: Box, @@ -40,6 +41,21 @@ impl ConditionalOp { &self.if_false } + #[cfg(feature = "fuzzer")] + pub fn cond_mut(&mut self) -> &mut Node { + &mut self.condition + } + + #[cfg(feature = "fuzzer")] + pub fn if_true_mut(&mut self) -> &mut Node { + &mut self.if_true + } + + #[cfg(feature = "fuzzer")] + pub fn if_false_mut(&mut self) -> &mut Node { + &mut self.if_false + } + /// Creates a `ConditionalOp` AST node. pub fn new(condition: C, if_true: T, if_false: F) -> Self where diff --git a/boa_engine/src/syntax/ast/node/conditional/if_node/mod.rs b/boa_engine/src/syntax/ast/node/conditional/if_node/mod.rs index a41ed183a09..7f01ad21945 100644 --- a/boa_engine/src/syntax/ast/node/conditional/if_node/mod.rs +++ b/boa_engine/src/syntax/ast/node/conditional/if_node/mod.rs @@ -22,6 +22,7 @@ use serde::{Deserialize, Serialize}; /// [falsy]: https://developer.mozilla.org/en-US/docs/Glossary/falsy /// [expression]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct If { cond: Box, @@ -42,6 +43,21 @@ impl If { self.else_node.as_ref().map(Box::as_ref) } + #[cfg(feature = "fuzzer")] + pub fn cond_mut(&mut self) -> &mut Node { + &mut self.cond + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut Node { + &mut self.body + } + + #[cfg(feature = "fuzzer")] + pub fn else_node_mut(&mut self) -> Option<&mut Node> { + self.else_node.as_deref_mut() + } + /// Creates an `If` AST node. pub fn new(condition: C, body: B, else_node: OE) -> Self where diff --git a/boa_engine/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs index 5514b3b5cfe..88f9e6ffec1 100644 --- a/boa_engine/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs @@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct ArrowFunctionDecl { name: Option, @@ -61,6 +62,21 @@ impl ArrowFunctionDecl { &self.body } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> Option<&mut Sym> { + self.name.as_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn params_mut(&mut self) -> &mut FormalParameterList { + &mut self.params + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut StatementList { + &mut self.body + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs index 27e59f29f01..43b874d66a2 100644 --- a/boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/async_function_decl/mod.rs @@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#sec-async-function-prototype-properties /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct AsyncFunctionDecl { name: Sym, @@ -52,6 +53,21 @@ impl AsyncFunctionDecl { self.body.items() } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> &mut Sym { + &mut self.name + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut StatementList { + &mut self.body + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/async_function_expr/mod.rs b/boa_engine/src/syntax/ast/node/declaration/async_function_expr/mod.rs index 38012856fc3..2cb4c057142 100644 --- a/boa_engine/src/syntax/ast/node/declaration/async_function_expr/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/async_function_expr/mod.rs @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct AsyncFunctionExpr { name: Option, @@ -54,6 +55,21 @@ impl AsyncFunctionExpr { &self.body } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> Option<&mut Sym> { + self.name.as_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut [Node] { + self.body.items_mut() + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs index cb127f7d830..fa57243b66d 100644 --- a/boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/async_generator_decl/mod.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; /// /// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct AsyncGeneratorDecl { name: Sym, @@ -50,6 +51,21 @@ impl AsyncGeneratorDecl { self.body.items() } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> &mut Sym { + &mut self.name + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut [Node] { + self.body.items_mut() + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/async_generator_expr/mod.rs b/boa_engine/src/syntax/ast/node/declaration/async_generator_expr/mod.rs index f41aa6b7574..4953030b7f9 100644 --- a/boa_engine/src/syntax/ast/node/declaration/async_generator_expr/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/async_generator_expr/mod.rs @@ -16,6 +16,7 @@ use super::block_to_string; /// /// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct AsyncGeneratorExpr { name: Option, @@ -53,6 +54,21 @@ impl AsyncGeneratorExpr { &self.body } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> Option<&mut Sym> { + self.name.as_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut [Node] { + self.body.items_mut() + } + pub(in crate::syntax::ast::node) fn to_indented_string( &self, interner: &Interner, diff --git a/boa_engine/src/syntax/ast/node/declaration/function_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/function_decl/mod.rs index 41d3d2baa89..4b57e112624 100644 --- a/boa_engine/src/syntax/ast/node/declaration/function_decl/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/function_decl/mod.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function /// [func_expr]: ../enum.Node.html#variant.FunctionExpr #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct FunctionDecl { name: Sym, @@ -60,6 +61,21 @@ impl FunctionDecl { &self.body } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> &mut Sym { + &mut self.name + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut StatementList { + &mut self.body + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs b/boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs index 73e65a983a8..4a965cbb24c 100644 --- a/boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/function_expr/mod.rs @@ -24,6 +24,7 @@ use super::block_to_string; /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-function /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct FunctionExpr { name: Option, @@ -61,6 +62,21 @@ impl FunctionExpr { &self.body } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> Option<&mut Sym> { + self.name.as_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut StatementList { + &mut self.body + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs b/boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs index 6dbd0d66417..a4c61b9fc0c 100644 --- a/boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/generator_decl/mod.rs @@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-GeneratorDeclaration /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function* #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct GeneratorDecl { name: Sym, @@ -51,6 +52,21 @@ impl GeneratorDecl { &self.body } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> &mut Sym { + &mut self.name + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut [Node] { + self.body.items_mut() + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/generator_expr/mod.rs b/boa_engine/src/syntax/ast/node/declaration/generator_expr/mod.rs index 28783b72c85..d5aa1834e50 100644 --- a/boa_engine/src/syntax/ast/node/declaration/generator_expr/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/generator_expr/mod.rs @@ -16,6 +16,7 @@ use super::block_to_string; /// [spec]: https://tc39.es/ecma262/#prod-GeneratorExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function* #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct GeneratorExpr { name: Option, @@ -53,6 +54,21 @@ impl GeneratorExpr { &self.body } + #[cfg(feature = "fuzzer")] + pub fn name_mut(&mut self) -> Option<&mut Sym> { + self.name.as_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn parameters_mut(&mut self) -> &mut FormalParameterList { + &mut self.parameters + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut StatementList { + &mut self.body + } + /// Converts the generator expresion node to a string with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/declaration/mod.rs b/boa_engine/src/syntax/ast/node/declaration/mod.rs index bb26ef93ec1..dcf90459d8b 100644 --- a/boa_engine/src/syntax/ast/node/declaration/mod.rs +++ b/boa_engine/src/syntax/ast/node/declaration/mod.rs @@ -32,6 +32,7 @@ pub use self::{ mod tests; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum DeclarationList { /// The `const` statements are block-scoped, much like variables defined using the `let` @@ -152,6 +153,7 @@ impl From for Box<[Declaration]> { /// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration /// [spec3]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum Declaration { Identifier { @@ -239,6 +241,7 @@ impl Declaration { /// /// [spec1]: https://tc39.es/ecma262/#prod-BindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum DeclarationPattern { Object(DeclarationPatternObject), @@ -285,6 +288,7 @@ impl DeclarationPattern { /// /// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct DeclarationPatternObject { bindings: Vec, @@ -334,6 +338,18 @@ impl DeclarationPatternObject { &self.bindings } + #[cfg(feature = "fuzzer")] + #[inline] + pub fn init_mut(&mut self) -> Option<&mut Node> { + self.init.as_mut() + } + + #[cfg(feature = "fuzzer")] + #[inline] + pub fn bindings_mut(&mut self) -> &mut Vec { + &mut self.bindings + } + /// Gets the list of identifiers declared by the object binding pattern. #[inline] pub(crate) fn idents(&self) -> Vec { @@ -384,6 +400,7 @@ impl DeclarationPatternObject { /// /// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct DeclarationPatternArray { bindings: Vec, @@ -435,6 +452,18 @@ impl DeclarationPatternArray { &self.bindings } + #[cfg(feature = "fuzzer")] + #[inline] + pub fn init_mut(&mut self) -> Option<&mut Node> { + self.init.as_mut() + } + + #[cfg(feature = "fuzzer")] + #[inline] + pub fn bindings_mut(&mut self) -> &mut Vec { + &mut self.bindings + } + /// Gets the list of identifiers declared by the array binding pattern. #[inline] pub(crate) fn idents(&self) -> Vec { @@ -478,6 +507,7 @@ impl DeclarationPatternArray { /// /// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum BindingPatternTypeObject { /// Empty represents an empty object binding pattern e.g. `{ }`. @@ -601,6 +631,7 @@ impl ToInternedString for BindingPatternTypeObject { /// /// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum BindingPatternTypeArray { /// Empty represents an empty array binding pattern e.g. `[ ]`. diff --git a/boa_engine/src/syntax/ast/node/field/get_const_field/mod.rs b/boa_engine/src/syntax/ast/node/field/get_const_field/mod.rs index 00f7a75c5be..7390fad47aa 100644 --- a/boa_engine/src/syntax/ast/node/field/get_const_field/mod.rs +++ b/boa_engine/src/syntax/ast/node/field/get_const_field/mod.rs @@ -27,6 +27,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#sec-property-accessors /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Dot_notation #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct GetConstField { obj: Box, @@ -54,6 +55,16 @@ impl GetConstField { pub fn field(&self) -> Sym { self.field } + + #[cfg(feature = "fuzzer")] + pub fn obj_mut(&mut self) -> &mut Node { + &mut self.obj + } + + #[cfg(feature = "fuzzer")] + pub fn field_mut(&mut self) -> &mut Sym { + &mut self.field + } } impl ToInternedString for GetConstField { diff --git a/boa_engine/src/syntax/ast/node/field/get_field/mod.rs b/boa_engine/src/syntax/ast/node/field/get_field/mod.rs index 744a40c00be..dab078d0a3b 100644 --- a/boa_engine/src/syntax/ast/node/field/get_field/mod.rs +++ b/boa_engine/src/syntax/ast/node/field/get_field/mod.rs @@ -28,6 +28,7 @@ use serde::{Deserialize, Serialize}; /// [symbol]: https://developer.mozilla.org/en-US/docs/Glossary/Symbol /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_accessors#Bracket_notation #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct GetField { obj: Box, @@ -43,6 +44,16 @@ impl GetField { &self.field } + #[cfg(feature = "fuzzer")] + pub fn obj_mut(&mut self) -> &mut Node { + &mut self.obj + } + + #[cfg(feature = "fuzzer")] + pub fn field_mut(&mut self) -> &mut Node { + &mut self.field + } + /// Creates a `GetField` AST node. pub fn new(value: V, field: F) -> Self where diff --git a/boa_engine/src/syntax/ast/node/identifier/mod.rs b/boa_engine/src/syntax/ast/node/identifier/mod.rs index 7bec5fc53c8..db776024e6b 100644 --- a/boa_engine/src/syntax/ast/node/identifier/mod.rs +++ b/boa_engine/src/syntax/ast/node/identifier/mod.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[cfg_attr(feature = "deser", serde(transparent))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Debug, Clone, Copy, Finalize, PartialEq)] pub struct Identifier { ident: Sym, diff --git a/boa_engine/src/syntax/ast/node/iteration/break_node/mod.rs b/boa_engine/src/syntax/ast/node/iteration/break_node/mod.rs index aa6afc331eb..e78ce28a458 100644 --- a/boa_engine/src/syntax/ast/node/iteration/break_node/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/break_node/mod.rs @@ -23,6 +23,7 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-BreakStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Debug, Clone, Copy, Finalize, PartialEq)] pub struct Break { label: Option, @@ -43,6 +44,11 @@ impl Break { pub fn label(&self) -> Option { self.label } + + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } } unsafe impl Trace for Break { diff --git a/boa_engine/src/syntax/ast/node/iteration/continue_node/mod.rs b/boa_engine/src/syntax/ast/node/iteration/continue_node/mod.rs index 7933a0bebcc..d2881ebad5a 100644 --- a/boa_engine/src/syntax/ast/node/iteration/continue_node/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/continue_node/mod.rs @@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Continue { label: Option, @@ -38,6 +39,11 @@ impl Continue { pub fn label(&self) -> Option { self.label } + + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } } impl ToInternedString for Continue { diff --git a/boa_engine/src/syntax/ast/node/iteration/do_while_loop/mod.rs b/boa_engine/src/syntax/ast/node/iteration/do_while_loop/mod.rs index 6750d946e27..a4aaa5d00b4 100644 --- a/boa_engine/src/syntax/ast/node/iteration/do_while_loop/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/do_while_loop/mod.rs @@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#sec-do-while-statement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct DoWhileLoop { body: Box, @@ -42,6 +43,21 @@ impl DoWhileLoop { self.label = Some(label); } + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut Node { + &mut self.body + } + + #[cfg(feature = "fuzzer")] + pub fn cond_mut(&mut self) -> &mut Node { + &mut self.cond + } + + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } + /// Creates a `DoWhileLoop` AST node. pub fn new(body: B, condition: C) -> Self where diff --git a/boa_engine/src/syntax/ast/node/iteration/for_in_loop/mod.rs b/boa_engine/src/syntax/ast/node/iteration/for_in_loop/mod.rs index 9d8b3a242df..79a46e2b69c 100644 --- a/boa_engine/src/syntax/ast/node/iteration/for_in_loop/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/for_in_loop/mod.rs @@ -6,6 +6,7 @@ use boa_interner::{Interner, Sym, ToInternedString}; use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct ForInLoop { init: Box, @@ -48,6 +49,26 @@ impl ForInLoop { self.label = Some(label); } + #[cfg(feature = "fuzzer")] + pub fn init_mut(&mut self) -> &mut IterableLoopInitializer { + &mut self.init + } + + #[cfg(feature = "fuzzer")] + pub fn expr_mut(&mut self) -> &mut Node { + &mut self.expr + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut Node { + &mut self.body + } + + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } + /// Converts the "for in" loop to a string with the given indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs b/boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs index e18cb709713..d6eb38f1310 100644 --- a/boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/for_loop/mod.rs @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct ForLoop { #[cfg_attr(feature = "deser", serde(flatten))] @@ -59,6 +60,26 @@ impl ForLoop { self.inner.body() } + #[cfg(feature = "fuzzer")] + pub fn init_mut(&mut self) -> Option<&mut Node> { + self.inner.init_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn condition_mut(&mut self) -> Option<&mut Node> { + self.inner.condition_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn final_expr_mut(&mut self) -> Option<&mut Node> { + self.inner.final_expr_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut Node { + self.inner.body_mut() + } + /// Converts the for loop to a string with the given indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, @@ -97,6 +118,11 @@ impl ForLoop { pub fn set_label(&mut self, label: Sym) { self.label = Some(label); } + + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } } impl ToInternedString for ForLoop { @@ -113,6 +139,7 @@ impl From for Node { /// Inner structure to avoid multiple indirections in the heap. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] struct InnerForLoop { init: Option, @@ -157,4 +184,24 @@ impl InnerForLoop { fn body(&self) -> &Node { &self.body } + + #[cfg(feature = "fuzzer")] + fn init_mut(&mut self) -> Option<&mut Node> { + self.init.as_mut() + } + + #[cfg(feature = "fuzzer")] + fn condition_mut(&mut self) -> Option<&mut Node> { + self.condition.as_mut() + } + + #[cfg(feature = "fuzzer")] + fn final_expr_mut(&mut self) -> Option<&mut Node> { + self.final_expr.as_mut() + } + + #[cfg(feature = "fuzzer")] + fn body_mut(&mut self) -> &mut Node { + &mut self.body + } } diff --git a/boa_engine/src/syntax/ast/node/iteration/for_of_loop/mod.rs b/boa_engine/src/syntax/ast/node/iteration/for_of_loop/mod.rs index 2542baf1d79..73541ffb9bc 100644 --- a/boa_engine/src/syntax/ast/node/iteration/for_of_loop/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/for_of_loop/mod.rs @@ -6,6 +6,7 @@ use boa_interner::{Interner, Sym, ToInternedString}; use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct ForOfLoop { init: Box, @@ -49,6 +50,26 @@ impl ForOfLoop { self.label = Some(label); } + #[cfg(feature = "fuzzer")] + pub fn init_mut(&mut self) -> &mut IterableLoopInitializer { + &mut self.init + } + + #[cfg(feature = "fuzzer")] + pub fn iterable_mut(&mut self) -> &mut Node { + &mut self.iterable + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut Node { + &mut self.body + } + + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } + /// Converts the "for of" loop to a string with the given indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/iteration/mod.rs b/boa_engine/src/syntax/ast/node/iteration/mod.rs index 78f67a7adad..9e974f7c6a4 100644 --- a/boa_engine/src/syntax/ast/node/iteration/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/mod.rs @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize}; mod tests; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum IterableLoopInitializer { Identifier(Identifier), diff --git a/boa_engine/src/syntax/ast/node/iteration/while_loop/mod.rs b/boa_engine/src/syntax/ast/node/iteration/while_loop/mod.rs index eec09515cd1..31d5bac7408 100644 --- a/boa_engine/src/syntax/ast/node/iteration/while_loop/mod.rs +++ b/boa_engine/src/syntax/ast/node/iteration/while_loop/mod.rs @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-grammar-notation-WhileStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/while #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct WhileLoop { cond: Box, @@ -41,6 +42,21 @@ impl WhileLoop { self.label = Some(label); } + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut Node { + &mut self.body + } + + #[cfg(feature = "fuzzer")] + pub fn cond_mut(&mut self) -> &mut Node { + &mut self.cond + } + + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } + /// Creates a `WhileLoop` AST node. pub fn new(condition: C, body: B) -> Self where diff --git a/boa_engine/src/syntax/ast/node/mod.rs b/boa_engine/src/syntax/ast/node/mod.rs index 8d36bd5d8d4..4a63458b102 100644 --- a/boa_engine/src/syntax/ast/node/mod.rs +++ b/boa_engine/src/syntax/ast/node/mod.rs @@ -64,6 +64,7 @@ use serde::{Deserialize, Serialize}; // TODO: This should be split into Expression and Statement. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum Node { /// Array declaration node. [More information](./array/struct.ArrayDecl.html). diff --git a/boa_engine/src/syntax/ast/node/new/mod.rs b/boa_engine/src/syntax/ast/node/new/mod.rs index 028e939f3db..69a757c149a 100644 --- a/boa_engine/src/syntax/ast/node/new/mod.rs +++ b/boa_engine/src/syntax/ast/node/new/mod.rs @@ -24,6 +24,7 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-NewExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct New { call: Call, @@ -40,6 +41,16 @@ impl New { self.call.args() } + #[cfg(feature = "fuzzer")] + pub fn expr_mut(&mut self) -> &mut Node { + self.call.expr_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn args_mut(&mut self) -> &mut [Node] { + self.call.args_mut() + } + /// Returns the inner call pub(crate) fn call(&self) -> &Call { &self.call diff --git a/boa_engine/src/syntax/ast/node/object/mod.rs b/boa_engine/src/syntax/ast/node/object/mod.rs index f5dc0a30225..4802efb43ed 100644 --- a/boa_engine/src/syntax/ast/node/object/mod.rs +++ b/boa_engine/src/syntax/ast/node/object/mod.rs @@ -34,6 +34,7 @@ mod tests; /// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[cfg_attr(feature = "deser", serde(transparent))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Object { properties: Box<[PropertyDefinition]>, @@ -44,6 +45,11 @@ impl Object { &self.properties } + #[cfg(feature = "fuzzer")] + pub fn properties_mut(&mut self) -> &mut [PropertyDefinition] { + &mut self.properties + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, @@ -155,6 +161,7 @@ impl From for Node { /// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript // TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq, Trace, Finalize)] pub enum PropertyDefinition { /// Puts a variable into an object. @@ -245,6 +252,7 @@ impl PropertyDefinition { /// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq, Finalize, Trace)] pub enum MethodDefinition { /// The `get` syntax binds an object property to a function that will be called when that property is looked up. @@ -326,6 +334,7 @@ pub enum MethodDefinition { /// /// [spec]: https://tc39.es/ecma262/#prod-PropertyName #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq, Finalize)] pub enum PropertyName { /// A `Literal` property name can be either an identifier, a string or a numeric literal. diff --git a/boa_engine/src/syntax/ast/node/operator/assign/mod.rs b/boa_engine/src/syntax/ast/node/operator/assign/mod.rs index 5ff427f18f2..140dcbe7e13 100644 --- a/boa_engine/src/syntax/ast/node/operator/assign/mod.rs +++ b/boa_engine/src/syntax/ast/node/operator/assign/mod.rs @@ -24,6 +24,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Assignment_Operators #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Assign { lhs: Box, @@ -52,6 +53,16 @@ impl Assign { pub fn rhs(&self) -> &Node { &self.rhs } + + #[cfg(feature = "fuzzer")] + pub fn lhs_mut(&mut self) -> &mut AssignTarget { + &mut self.lhs + } + + #[cfg(feature = "fuzzer")] + pub fn rhs_mut(&mut self) -> &mut Node { + &mut self.rhs + } } impl ToInternedString for Assign { @@ -77,6 +88,7 @@ impl From for Node { /// /// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum AssignTarget { Identifier(Identifier), diff --git a/boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs b/boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs index 40cdb16339d..c68b5119f50 100644 --- a/boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs +++ b/boa_engine/src/syntax/ast/node/operator/bin_op/mod.rs @@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize}; /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Operators #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct BinOp { op: op::BinOp, @@ -48,6 +49,16 @@ impl BinOp { pub fn rhs(&self) -> &Node { &self.rhs } + + #[cfg(feature = "fuzzer")] + pub fn lhs_mut(&mut self) -> &mut Node { + &mut self.lhs + } + + #[cfg(feature = "fuzzer")] + pub fn rhs_mut(&mut self) -> &mut Node { + &mut self.rhs + } } impl ToInternedString for BinOp { diff --git a/boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs b/boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs index 42a9a99e055..919a544dbaa 100644 --- a/boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs +++ b/boa_engine/src/syntax/ast/node/operator/unary_op/mod.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary_operators #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct UnaryOp { op: op::UnaryOp, @@ -41,6 +42,11 @@ impl UnaryOp { pub fn target(&self) -> &Node { self.target.as_ref() } + + #[cfg(feature = "fuzzer")] + pub fn target_mut(&mut self) -> &mut Node { + self.target.as_mut() + } } impl ToInternedString for UnaryOp { diff --git a/boa_engine/src/syntax/ast/node/parameters.rs b/boa_engine/src/syntax/ast/node/parameters.rs index 40fb50a41b5..66725938469 100644 --- a/boa_engine/src/syntax/ast/node/parameters.rs +++ b/boa_engine/src/syntax/ast/node/parameters.rs @@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize}; /// /// [spec]: https://tc39.es/ecma262/#prod-FormalParameterList #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Default, PartialEq, Trace, Finalize)] pub struct FormalParameterList { pub(crate) parameters: Box<[FormalParameter]>, @@ -53,11 +54,17 @@ impl FormalParameterList { pub(crate) fn has_arguments(&self) -> bool { self.flags.contains(FormalParameterListFlags::HAS_ARGUMENTS) } + + #[cfg(feature = "fuzzer")] + pub fn items_mut(&mut self) -> &mut [FormalParameter] { + &mut self.parameters + } } bitflags! { /// Flags for a [`FormalParameterList`]. #[allow(clippy::unsafe_derive_deserialize)] + #[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] pub(crate) struct FormalParameterListFlags: u8 { const IS_SIMPLE = 0b0000_0001; @@ -90,6 +97,7 @@ impl Default for FormalParameterListFlags { /// [spec]: https://tc39.es/ecma262/#prod-FormalParameter /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Missing_formal_parameter #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, PartialEq, Trace, Finalize)] pub struct FormalParameter { declaration: Declaration, @@ -125,6 +133,11 @@ impl FormalParameter { &self.declaration } + #[cfg(feature = "fuzzer")] + pub fn declaration_mut(&mut self) -> &mut Declaration { + &mut self.declaration + } + /// Gets the initialization node of the formal parameter, if any. pub fn init(&self) -> Option<&Node> { self.declaration.init() diff --git a/boa_engine/src/syntax/ast/node/return_smt/mod.rs b/boa_engine/src/syntax/ast/node/return_smt/mod.rs index 1e34cf7dc9b..20b0d6e06b4 100644 --- a/boa_engine/src/syntax/ast/node/return_smt/mod.rs +++ b/boa_engine/src/syntax/ast/node/return_smt/mod.rs @@ -27,6 +27,7 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Return { expr: Option>, @@ -42,6 +43,16 @@ impl Return { self.expr.as_ref().map(Box::as_ref) } + #[cfg(feature = "fuzzer")] + pub fn label_mut(&mut self) -> Option<&mut Sym> { + self.label.as_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn expr_mut(&mut self) -> Option<&mut Node> { + self.expr.as_deref_mut() + } + /// Creates a `Return` AST node. pub fn new(expr: OE, label: L) -> Self where diff --git a/boa_engine/src/syntax/ast/node/spread/mod.rs b/boa_engine/src/syntax/ast/node/spread/mod.rs index cb645e4de61..127c4a69039 100644 --- a/boa_engine/src/syntax/ast/node/spread/mod.rs +++ b/boa_engine/src/syntax/ast/node/spread/mod.rs @@ -26,6 +26,7 @@ mod tests; /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[cfg_attr(feature = "deser", serde(transparent))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Spread { val: Box, @@ -36,6 +37,11 @@ impl Spread { &self.val } + #[cfg(feature = "fuzzer")] + pub fn val_mut(&mut self) -> &mut Node { + &mut self.val + } + /// Creates a `Spread` AST node. pub fn new(val: V) -> Self where diff --git a/boa_engine/src/syntax/ast/node/statement_list/mod.rs b/boa_engine/src/syntax/ast/node/statement_list/mod.rs index 0239b4548c4..1588ef1bae2 100644 --- a/boa_engine/src/syntax/ast/node/statement_list/mod.rs +++ b/boa_engine/src/syntax/ast/node/statement_list/mod.rs @@ -1,6 +1,8 @@ //! Statement list node. use crate::syntax::ast::node::{Declaration, Node}; +#[cfg(feature = "fuzzer")] +use arbitrary::{size_hint, Arbitrary, Unstructured}; use boa_gc::{unsafe_empty_trace, Finalize, Trace}; use boa_interner::{Interner, Sym, ToInternedString}; use std::{ops::Deref, rc::Rc}; @@ -35,6 +37,12 @@ impl StatementList { &self.items } + #[cfg(feature = "fuzzer")] + #[inline] + pub fn items_mut(&mut self) -> &mut [Node] { + &mut self.items + } + /// Get the strict mode. #[inline] pub fn strict(&self) -> bool { @@ -172,3 +180,17 @@ impl From for RcStatementList { unsafe impl Trace for RcStatementList { unsafe_empty_trace!(); } + +#[cfg(feature = "fuzzer")] +impl<'a> Arbitrary<'a> for StatementList { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + Ok(Self { + items: Vec::arbitrary(u)?.into_boxed_slice(), + strict: bool::arbitrary(u)?, + }) + } + + fn size_hint(depth: usize) -> (usize, Option) { + size_hint::and(Vec::::size_hint(depth), bool::size_hint(depth)) + } +} diff --git a/boa_engine/src/syntax/ast/node/switch/mod.rs b/boa_engine/src/syntax/ast/node/switch/mod.rs index 1ae2d40a5cb..75cfff6e595 100644 --- a/boa_engine/src/syntax/ast/node/switch/mod.rs +++ b/boa_engine/src/syntax/ast/node/switch/mod.rs @@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize}; mod tests; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Case { condition: Node, @@ -41,6 +42,16 @@ impl Case { pub fn body(&self) -> &StatementList { &self.body } + + #[cfg(feature = "fuzzer")] + pub fn condition_mut(&mut self) -> &mut Node { + &mut self.condition + } + + #[cfg(feature = "fuzzer")] + pub fn body_mut(&mut self) -> &mut StatementList { + &mut self.body + } } /// The `switch` statement evaluates an expression, matching the expression's value to a case @@ -60,6 +71,7 @@ impl Case { /// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Switch { val: Box, @@ -97,6 +109,21 @@ impl Switch { self.default.as_ref().map(StatementList::items) } + #[cfg(feature = "fuzzer")] + pub fn val_mut(&mut self) -> &mut Node { + &mut self.val + } + + #[cfg(feature = "fuzzer")] + pub fn cases_mut(&mut self) -> &mut [Case] { + &mut self.cases + } + + #[cfg(feature = "fuzzer")] + pub fn default_mut(&mut self) -> Option<&mut [Node]> { + self.default.as_mut().map(StatementList::items_mut) + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, diff --git a/boa_engine/src/syntax/ast/node/template/mod.rs b/boa_engine/src/syntax/ast/node/template/mod.rs index 41f88ccce6d..c499723c248 100644 --- a/boa_engine/src/syntax/ast/node/template/mod.rs +++ b/boa_engine/src/syntax/ast/node/template/mod.rs @@ -19,6 +19,7 @@ mod tests; /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals /// [spec]: https://tc39.es/ecma262/#sec-template-literals #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct TemplateLit { elements: Box<[TemplateElement]>, @@ -37,6 +38,11 @@ impl TemplateLit { pub(crate) fn elements(&self) -> &[TemplateElement] { &self.elements } + + #[cfg(feature = "fuzzer")] + pub fn elements_mut(&mut self) -> &mut [TemplateElement] { + &mut self.elements + } } impl ToInternedString for TemplateLit { @@ -57,6 +63,7 @@ impl ToInternedString for TemplateLit { } } #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct TaggedTemplate { tag: Box, @@ -97,6 +104,26 @@ impl TaggedTemplate { pub(crate) fn exprs(&self) -> &[Node] { &self.exprs } + + #[cfg(feature = "fuzzer")] + pub fn tag_mut(&mut self) -> &mut Node { + &mut self.tag + } + + #[cfg(feature = "fuzzer")] + pub fn raws_mut(&mut self) -> &mut [Sym] { + &mut self.raws + } + + #[cfg(feature = "fuzzer")] + pub fn cookeds_mut(&mut self) -> &mut [Option] { + &mut self.cookeds + } + + #[cfg(feature = "fuzzer")] + pub fn exprs_mut(&mut self) -> &mut [Node] { + &mut self.exprs + } } impl ToInternedString for TaggedTemplate { @@ -122,6 +149,7 @@ impl From for Node { } #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub enum TemplateElement { String(Sym), diff --git a/boa_engine/src/syntax/ast/node/throw/mod.rs b/boa_engine/src/syntax/ast/node/throw/mod.rs index 607a2ab093a..6b49e265c29 100644 --- a/boa_engine/src/syntax/ast/node/throw/mod.rs +++ b/boa_engine/src/syntax/ast/node/throw/mod.rs @@ -23,6 +23,7 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Throw { expr: Box, @@ -33,6 +34,11 @@ impl Throw { &self.expr } + #[cfg(feature = "fuzzer")] + pub fn expr_mut(&mut self) -> &mut Node { + &mut self.expr + } + /// Creates a `Throw` AST node. pub fn new(val: V) -> Self where diff --git a/boa_engine/src/syntax/ast/node/try_node/mod.rs b/boa_engine/src/syntax/ast/node/try_node/mod.rs index 7fd0f57f416..19e3481b6ea 100644 --- a/boa_engine/src/syntax/ast/node/try_node/mod.rs +++ b/boa_engine/src/syntax/ast/node/try_node/mod.rs @@ -22,6 +22,7 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#prod-TryStatement /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Try { block: Block, @@ -66,6 +67,21 @@ impl Try { self.finally.as_ref().map(Finally::block) } + #[cfg(feature = "fuzzer")] + pub fn block_mut(&mut self) -> &mut Block { + &mut self.block + } + + #[cfg(feature = "fuzzer")] + pub fn catch_mut(&mut self) -> Option<&mut Catch> { + self.catch.as_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn finally_mut(&mut self) -> Option<&mut Block> { + self.finally.as_mut().map(Finally::block_mut) + } + /// Implements the display formatting with indentation. pub(in crate::syntax::ast::node) fn to_indented_string( &self, @@ -103,6 +119,7 @@ impl From for Node { /// Catch block. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Catch { parameter: Option>, @@ -133,6 +150,16 @@ impl Catch { &self.block } + #[cfg(feature = "fuzzer")] + pub fn parameter_mut(&mut self) -> Option<&mut Declaration> { + self.parameter.as_deref_mut() + } + + #[cfg(feature = "fuzzer")] + pub fn block_mut(&mut self) -> &mut Block { + &mut self.block + } + /// Implements the display formatting with indentation. pub(super) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { let mut buf = " catch".to_owned(); @@ -156,6 +183,7 @@ impl ToInternedString for Catch { /// Finally block. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Finally { block: Block, @@ -167,6 +195,11 @@ impl Finally { &self.block } + #[cfg(feature = "fuzzer")] + pub fn block_mut(&mut self) -> &mut Block { + &mut self.block + } + /// Implements the display formatting with indentation. pub(super) fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String { format!( diff --git a/boa_engine/src/syntax/ast/node/yield/mod.rs b/boa_engine/src/syntax/ast/node/yield/mod.rs index 0e9e602ff92..d7749d2719c 100644 --- a/boa_engine/src/syntax/ast/node/yield/mod.rs +++ b/boa_engine/src/syntax/ast/node/yield/mod.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; /// [spec]: https://tc39.es/ecma262/#prod-YieldExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct Yield { expr: Option>, @@ -25,6 +26,11 @@ impl Yield { self.expr.as_ref().map(Box::as_ref) } + #[cfg(feature = "fuzzer")] + pub fn expr_mut(&mut self) -> Option<&mut Node> { + self.expr.as_deref_mut() + } + pub fn delegate(&self) -> bool { self.delegate } diff --git a/boa_engine/src/syntax/ast/op.rs b/boa_engine/src/syntax/ast/op.rs index 859fb7b0274..8f0f2477e0c 100644 --- a/boa_engine/src/syntax/ast/op.rs +++ b/boa_engine/src/syntax/ast/op.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, Finalize, PartialEq)] pub enum NumOp { /// The addition operator produces the sum of numeric operands or string concatenation. @@ -132,6 +133,7 @@ unsafe impl Trace for NumOp { /// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, Finalize, PartialEq)] pub enum UnaryOp { /// The increment operator increments (adds one to) its operand and returns a value. @@ -346,6 +348,7 @@ unsafe impl Trace for UnaryOp { /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, Finalize, PartialEq)] pub enum BitOp { /// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1. @@ -472,6 +475,7 @@ unsafe impl Trace for BitOp { /// [spec]: tc39.es/ecma262/#sec-testing-and-comparison-operations /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Comparison #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, Finalize, PartialEq)] pub enum CompOp { /// The equality operator converts the operands if they are not of the same type, then applies @@ -668,6 +672,7 @@ unsafe impl Trace for CompOp { /// [spec]: https://tc39.es/ecma262/#sec-binary-logical-operators /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Logical #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, Finalize, PartialEq)] pub enum LogOp { /// The logical AND operator returns the value of the first operand if it can be coerced into `false`; @@ -733,6 +738,7 @@ unsafe impl Trace for LogOp { /// This represents a binary operation between two values. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, Finalize, PartialEq)] pub enum BinOp { /// Numeric operation. @@ -832,6 +838,7 @@ unsafe impl Trace for BinOp { /// [spec]: https://tc39.es/ecma262/#prod-AssignmentOperator /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[derive(Clone, Copy, Debug, Finalize, PartialEq)] pub enum AssignOp { /// The addition assignment operator adds the value of the right operand to a variable and assigns the result to the variable. diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index bd8b6b1ff62..3c6670d9f86 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -1466,9 +1466,21 @@ impl Context { ); } + #[cfg(feature = "fuzzer")] + let mut insns_executed = 0; let start_stack_size = self.vm.stack.len(); while self.vm.frame().pc < self.vm.frame().code.code.len() { + #[cfg(feature = "fuzzer")] + { + // update and check how many VM instructions have been executed to make sure we + // don't introduce a spurious timeout in the fuzzer from a source which loops + // infinitely + insns_executed += 1; + if insns_executed > self.max_insns { + return Err("instruction max exceeded".into()); + } + } let result = if self.vm.trace { let mut pc = self.vm.frame().pc; let opcode: Opcode = self diff --git a/boa_inputgen/Cargo.toml b/boa_inputgen/Cargo.toml new file mode 100644 index 00000000000..36eeb4f9c5a --- /dev/null +++ b/boa_inputgen/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "boa_inputgen" +version = "0.14.0" +edition = "2021" +rust-version = "1.58" +authors = ["Addison Crump "] +description = "Arbitrary JavaScript input generator used to fuzz Boa." +repository = "https://github.com/boa-dev/boa" +keywords = ["javascript", "js", "fuzzing", "testing"] +categories = ["compilers"] +license = "Unlicense/MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +spin = "0.9" +string-interner = "*" + +[dependencies.arbitrary] +version = "1" +features = ["derive"] + +[dependencies.boa_engine] +path = "../boa_engine" +features = ["fuzzer"] + +[dependencies.boa_interner] +path = "../boa_interner" +features = ["fuzzer"] diff --git a/boa_inputgen/src/data.rs b/boa_inputgen/src/data.rs new file mode 100644 index 00000000000..94c17f180c9 --- /dev/null +++ b/boa_inputgen/src/data.rs @@ -0,0 +1,109 @@ +//! Fuzz data generation. + +use std::collections::HashSet; + +use arbitrary::{size_hint, Arbitrary, Unstructured}; +use spin::lazy::Lazy; + +use boa_engine::syntax::ast::node::StatementList; +use boa_interner::{Interner, ToInternedString}; + +use crate::replace_syms; + +static ALPHA: Lazy> = Lazy::new(|| { + let mut all = Vec::new(); + all.extend(b'A'..b'Z'); + all.extend(b'a'..b'z'); + all +}); + +static ALPHANUM: Lazy> = Lazy::new(|| { + let mut all = Vec::new(); + all.extend(b'0'..b'9'); + all.extend(b'A'..b'Z'); + all.extend(b'a'..b'z'); + all +}); + +/// A valid name for use as an identifier. +#[derive(Debug, PartialEq, Eq, Hash)] +struct Name { + name: String, +} + +impl Arbitrary<'_> for Name { + fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result { + // generate a valid identifier; starts with at least one alphabetic character + let mut chars = match Vec::<_>::arbitrary(u)? { + v if v.is_empty() => return Err(arbitrary::Error::NotEnoughData), + v => v, + }; + + let (first, rest) = chars + .split_first_mut() + .expect("Ensured above that the vec is not empty"); + + *first = ALPHA[(*first as usize) % ALPHA.len()]; + + // remaining characters are alphanumeric + for c in rest { + *c = ALPHANUM[(*c as usize) % ALPHANUM.len()]; + } + + Ok(Self { + name: String::from_utf8(chars).expect("Only valid characters used."), + }) + } + + // size is at least one u8 and a vec of u8s for the rest + fn size_hint(depth: usize) -> (usize, Option) { + size_hint::and(u8::size_hint(depth), Vec::::size_hint(depth)) + } +} + +/// Fuzz data which can be arbitrarily generated and used to test boa's parser, compiler, and vm. +#[derive(Debug, Clone)] +pub struct FuzzData { + source: String, +} + +impl<'a> Arbitrary<'a> for FuzzData { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + // we need at least one name or we'll mod by zero when trying to get a name + let first_name = Name::arbitrary(u)?; + // generate the rest + let mut vars = HashSet::::arbitrary(u)?; + vars.insert(first_name); + + // generate a javascript sample + let mut sample = StatementList::arbitrary(u)?; + + // notify the interner of the symbols we're using + let mut interner = Interner::with_capacity(vars.len()); + let syms = vars + .into_iter() + .map(|var| interner.get_or_intern(var.name)) + .collect::>(); + + // walk the AST and ensure that all identifiers are valid + replace_syms(&syms, &mut sample); + Ok(Self { + source: sample.to_interned_string(&interner), + }) + } + + fn size_hint(depth: usize) -> (usize, Option) { + size_hint::and_all(&[ + Name::size_hint(depth), + HashSet::::size_hint(depth), + StatementList::size_hint(depth), + ]) + } +} + +impl FuzzData { + /// Get the source represented by this fuzz data + pub fn get_source(&self) -> &str { + &self.source + } +} diff --git a/boa_inputgen/src/ident_walk.rs b/boa_inputgen/src/ident_walk.rs new file mode 100644 index 00000000000..de72b76d1ec --- /dev/null +++ b/boa_inputgen/src/ident_walk.rs @@ -0,0 +1,576 @@ +//! Identifier and symbol walker for ensuring that generated inputs do not fail with "string +//! disappeared". The AST is walked by level-order traversal and Sym instances are replaced with +//! valid ones generated by arbitrary. + +use boa_engine::syntax::ast::{ + node::{ + declaration::{BindingPatternTypeArray, BindingPatternTypeObject}, + iteration::IterableLoopInitializer, + object::{MethodDefinition, PropertyDefinition, PropertyName}, + operator::assign::AssignTarget, + template::TemplateElement, + AsyncFunctionExpr, AsyncGeneratorExpr, Block, Declaration, DeclarationList, + DeclarationPattern, FormalParameter, FunctionExpr, GeneratorExpr, Identifier, + StatementList, + }, + Const, Node, +}; +use boa_interner::Sym; + +/// Given this list of syms, walk the AST provided and replace any syms with a matching sym from +/// the list. +pub(crate) fn replace_syms(syms: &[Sym], sample: &mut StatementList) { + replace_inner( + syms, + sample + .items_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }) + .collect(), + ); +} + +/// Extend the lifetime of an arbitrary reference temporarily. Only safe in this context because +/// we only hold these references during the walk, so they are *relatively* static. Holding +/// references to the AST while continuing to walk makes a double-mutable-reference scenario which +/// the borrow checker denies, but it's entirely safe since we only modify one Sym at a time and the +/// modification is idempotent. +unsafe fn extend_lifetime<'a, T>(node: &mut T) -> &'a mut T { + &mut *(node as *mut T) +} + +/// Primary mechanism by which symbols are replaced: we just change the value to one from the list +/// via modulo of the raw sym ID. +fn map_sym(syms: &[Sym], sym: &mut Sym) { + *sym = syms[sym.as_raw().get() % syms.len()]; +} + +fn replace_block<'a>(nodes: &mut Vec<&'a mut Node>, block: &mut Block) { + nodes.extend( + block + .items_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); +} + +fn replace_ident(syms: &[Sym], ident: &mut Identifier) { + let mut sym = ident.sym(); + map_sym(syms, &mut sym); + *ident = Identifier::new(sym); +} + +fn replace_decl<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, decl: &mut Declaration) { + match decl { + Declaration::Identifier { ident, init } => { + replace_ident(syms, ident); + if let Some(n) = init.as_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + Declaration::Pattern(declpattern) => { + replace_declpattern(syms, nodes, unsafe { extend_lifetime(declpattern) }); + } + } +} + +fn replace_decllist<'a>( + syms: &[Sym], + nodes: &mut Vec<&'a mut Node>, + decllist: &mut DeclarationList, +) { + match decllist { + DeclarationList::Const(list) | DeclarationList::Let(list) | DeclarationList::Var(list) => { + list.iter_mut() + .for_each(|decl| replace_decl(syms, nodes, decl)); + } + } +} + +fn replace_declpattern<'a>( + syms: &[Sym], + nodes: &mut Vec<&'a mut Node>, + declpattern: &mut DeclarationPattern, +) { + let mut stack = vec![declpattern]; + while let Some(declpattern) = stack.pop() { + match declpattern { + DeclarationPattern::Object(o) => { + o.bindings_mut().iter_mut().for_each(|bpto| match bpto { + BindingPatternTypeObject::Empty => {} + BindingPatternTypeObject::SingleName { + ident, + property_name, + default_init, + } => { + map_sym(syms, ident); + map_sym(syms, property_name); + if let Some(n) = default_init.as_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + BindingPatternTypeObject::RestProperty { + ident, + excluded_keys, + } => { + map_sym(syms, ident); + excluded_keys.iter_mut().for_each(|sym| map_sym(syms, sym)); + } + BindingPatternTypeObject::BindingPattern { + ident, + pattern, + default_init, + } => { + map_sym(syms, ident); + stack.push(unsafe { extend_lifetime(pattern) }); + if let Some(n) = default_init.as_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + BindingPatternTypeObject::RestGetConstField { + get_const_field, + excluded_keys, + } => { + nodes.push(unsafe { extend_lifetime(get_const_field.obj_mut()) }); + map_sym(syms, get_const_field.field_mut()); + excluded_keys.iter_mut().for_each(|s| map_sym(syms, s)); + } + }); + if let Some(n) = o.init_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + DeclarationPattern::Array(a) => { + a.bindings_mut().iter_mut().for_each(|bpta| match bpta { + BindingPatternTypeArray::Empty | BindingPatternTypeArray::Elision => {} + BindingPatternTypeArray::SingleName { + ident, + default_init, + } => { + map_sym(syms, ident); + if let Some(n) = default_init.as_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + BindingPatternTypeArray::BindingPattern { pattern } + | BindingPatternTypeArray::BindingPatternRest { pattern } => { + stack.push(unsafe { extend_lifetime(pattern) }); + } + BindingPatternTypeArray::SingleNameRest { ident } => { + map_sym(syms, ident); + } + BindingPatternTypeArray::GetField { get_field } + | BindingPatternTypeArray::GetFieldRest { get_field } => { + nodes.push(unsafe { extend_lifetime(get_field.obj_mut()) }); + nodes.push(unsafe { extend_lifetime(get_field.field_mut()) }); + } + BindingPatternTypeArray::GetConstField { get_const_field } + | BindingPatternTypeArray::GetConstFieldRest { get_const_field } => { + nodes.push(unsafe { extend_lifetime(get_const_field.obj_mut()) }); + map_sym(syms, get_const_field.field_mut()); + } + }); + if let Some(n) = a.init_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + } + } +} + +fn replace_afe<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, afe: &mut AsyncFunctionExpr) { + if let Some(sym) = afe.name_mut() { + map_sym(syms, sym); + } + afe.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, nodes, fp)); + nodes.extend( + afe.body_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); +} + +fn replace_age<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, age: &mut AsyncGeneratorExpr) { + if let Some(sym) = age.name_mut() { + map_sym(syms, sym); + } + age.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, nodes, fp)); + nodes.extend( + age.body_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); +} + +fn replace_fe<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, fe: &mut FunctionExpr) { + if let Some(sym) = fe.name_mut() { + map_sym(syms, sym); + } + fe.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, nodes, fp)); + nodes.extend( + fe.body_mut() + .items_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); +} + +fn replace_ge<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, ge: &mut GeneratorExpr) { + if let Some(sym) = ge.name_mut() { + map_sym(syms, sym); + } + ge.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, nodes, fp)); + nodes.extend( + ge.body_mut() + .items_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); +} + +fn replace_fp<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, fp: &mut FormalParameter) { + replace_decl(syms, nodes, fp.declaration_mut()); +} + +fn replace_ili<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, ili: &mut IterableLoopInitializer) { + match ili { + IterableLoopInitializer::Identifier(i) => replace_ident(syms, i), + IterableLoopInitializer::Var(d) + | IterableLoopInitializer::Let(d) + | IterableLoopInitializer::Const(d) => replace_decl(syms, nodes, d), + IterableLoopInitializer::DeclarationPattern(declpattern) => { + replace_declpattern(syms, nodes, declpattern); + } + } +} + +fn replace_methdef<'a>( + syms: &[Sym], + nodes: &mut Vec<&'a mut Node>, + methdef: &mut MethodDefinition, +) { + match methdef { + MethodDefinition::Get(fe) | MethodDefinition::Set(fe) | MethodDefinition::Ordinary(fe) => { + replace_fe(syms, nodes, fe) + } + MethodDefinition::Generator(ge) => replace_ge(syms, nodes, ge), + MethodDefinition::AsyncGenerator(age) => replace_age(syms, nodes, age), + MethodDefinition::Async(afe) => replace_afe(syms, nodes, afe), + } +} + +fn replace_propdef<'a>( + syms: &[Sym], + nodes: &mut Vec<&'a mut Node>, + propdef: &mut PropertyDefinition, +) { + match propdef { + PropertyDefinition::IdentifierReference(ir) => map_sym(syms, ir), + PropertyDefinition::Property(pn, n) => { + replace_propname(syms, nodes, pn); + nodes.push(unsafe { extend_lifetime(n) }); + } + PropertyDefinition::MethodDefinition(md, pn) => { + replace_methdef(syms, nodes, md); + replace_propname(syms, nodes, pn); + } + PropertyDefinition::SpreadObject(n) => { + nodes.push(unsafe { extend_lifetime(n) }); + } + } +} + +fn replace_propname<'a>(syms: &[Sym], nodes: &mut Vec<&'a mut Node>, propname: &mut PropertyName) { + match propname { + PropertyName::Literal(l) => map_sym(syms, l), + PropertyName::Computed(c) => nodes.push(unsafe { extend_lifetime(c) }), + } +} + +/// Perform the AST walk. Method used here is a level-order traversal of the AST by using `nodes` as +/// a queue of nodes we still need to walk. +fn replace_inner(syms: &[Sym], mut nodes: Vec<&mut Node>) { + while let Some(node) = nodes.pop() { + match node { + Node::ArrayDecl(orig) => nodes.extend( + orig.as_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ), + Node::ArrowFunctionDecl(orig) => { + if let Some(sym) = orig.name_mut() { + map_sym(syms, sym); + } + orig.params_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, &mut nodes, fp)); + nodes.extend( + orig.body_mut() + .items_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); + } + Node::Assign(orig) => { + match orig.lhs_mut() { + AssignTarget::Identifier(ident) => replace_ident(syms, ident), + AssignTarget::GetConstField(get_const_field) => { + nodes.push(unsafe { extend_lifetime(get_const_field.obj_mut()) }); + map_sym(syms, get_const_field.field_mut()); + } + AssignTarget::GetField(get_field) => { + nodes.push(unsafe { extend_lifetime(get_field.obj_mut()) }); + nodes.push(unsafe { extend_lifetime(get_field.field_mut()) }); + } + AssignTarget::DeclarationPattern(declpattern) => { + replace_declpattern(syms, &mut nodes, declpattern); + } + } + nodes.push(unsafe { extend_lifetime(orig.rhs_mut()) }); + } + Node::AsyncFunctionDecl(orig) => { + map_sym(syms, orig.name_mut()); + orig.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, &mut nodes, fp)); + nodes.extend( + orig.body_mut() + .items_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); + } + Node::AsyncFunctionExpr(orig) => { + replace_afe(syms, &mut nodes, orig); + } + Node::AsyncGeneratorExpr(orig) => { + replace_age(syms, &mut nodes, orig); + } + Node::AsyncGeneratorDecl(orig) => { + map_sym(syms, orig.name_mut()); + orig.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, &mut nodes, fp)); + nodes.extend( + orig.body_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); + } + Node::AwaitExpr(orig) => nodes.push(orig.expr_mut()), + Node::BinOp(orig) => { + nodes.push(unsafe { extend_lifetime(orig.lhs_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.rhs_mut()) }); + } + Node::Block(orig) => replace_block(&mut nodes, orig), + Node::Break(orig) => { + if let Some(sym) = orig.label_mut() { + map_sym(syms, sym); + } + } + Node::Call(orig) => { + nodes.push(unsafe { extend_lifetime(orig.expr_mut()) }); + nodes.extend( + orig.args_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); + } + Node::ConditionalOp(orig) => { + nodes.push(unsafe { extend_lifetime(orig.cond_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.if_true_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.if_false_mut()) }); + } + Node::Const(Const::String(s)) => map_sym(syms, s), + Node::ConstDeclList(orig) | Node::LetDeclList(orig) | Node::VarDeclList(orig) => { + replace_decllist(syms, &mut nodes, orig); + } + Node::Continue(orig) => { + if let Some(sym) = orig.label_mut() { + map_sym(syms, sym); + } + } + Node::DoWhileLoop(orig) => { + nodes.push(unsafe { extend_lifetime(orig.body_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.cond_mut()) }); + if let Some(sym) = orig.label_mut() { + map_sym(syms, sym); + } + } + Node::FunctionDecl(orig) => { + map_sym(syms, orig.name_mut()); + orig.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, &mut nodes, fp)); + nodes.extend( + orig.body_mut() + .items_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); + } + Node::FunctionExpr(orig) => replace_fe(syms, &mut nodes, orig), + Node::GetConstField(orig) => { + nodes.push(unsafe { extend_lifetime(orig.obj_mut()) }); + map_sym(syms, orig.field_mut()); + } + Node::GetField(orig) => { + nodes.push(unsafe { extend_lifetime(orig.obj_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.field_mut()) }); + } + Node::ForLoop(orig) => { + if let Some(n) = orig.init_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + if let Some(n) = orig.condition_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + if let Some(n) = orig.final_expr_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + if let Some(s) = orig.label_mut() { + map_sym(syms, s); + } + nodes.push(unsafe { extend_lifetime(orig.body_mut()) }); + } + Node::ForInLoop(orig) => { + replace_ili(syms, &mut nodes, orig.init_mut()); + nodes.push(unsafe { extend_lifetime(orig.expr_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.body_mut()) }); + if let Some(sym) = orig.label_mut() { + map_sym(syms, sym); + } + } + Node::ForOfLoop(orig) => { + replace_ili(syms, &mut nodes, orig.init_mut()); + nodes.push(unsafe { extend_lifetime(orig.iterable_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.body_mut()) }); + if let Some(sym) = orig.label_mut() { + map_sym(syms, sym); + } + } + Node::If(orig) => { + nodes.push(unsafe { extend_lifetime(orig.cond_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.body_mut()) }); + if let Some(n) = orig.else_node_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + Node::Identifier(orig) => replace_ident(syms, orig), + Node::New(orig) => { + nodes.push(unsafe { extend_lifetime(orig.expr_mut()) }); + nodes.extend( + orig.args_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); + } + Node::Object(orig) => { + orig.properties_mut() + .iter_mut() + .for_each(|pd| replace_propdef(syms, &mut nodes, pd)); + } + Node::Return(orig) => { + if let Some(n) = orig.expr_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + if let Some(s) = orig.label_mut() { + map_sym(syms, s); + } + } + Node::Switch(orig) => { + nodes.push(unsafe { extend_lifetime(orig.val_mut()) }); + orig.cases_mut().iter_mut().for_each(|c| { + nodes.push(unsafe { extend_lifetime(c.condition_mut()) }); + c.body_mut() + .items_mut() + .iter_mut() + .for_each(|n| nodes.push(unsafe { extend_lifetime(n) })); + }); + if let Some(list) = orig.default_mut() { + nodes.extend(list.iter_mut().map(|n| unsafe { extend_lifetime(n) })); + } + } + Node::Spread(orig) => nodes.push(orig.val_mut()), + Node::TaggedTemplate(orig) => { + nodes.push(unsafe { extend_lifetime(orig.tag_mut()) }); + orig.raws_mut().iter_mut().for_each(|s| map_sym(syms, s)); + orig.cookeds_mut() + .iter_mut() + .filter_map(Option::as_mut) + .for_each(|s| { + map_sym(syms, s); + }); + orig.exprs_mut() + .iter_mut() + .for_each(|n| nodes.push(unsafe { extend_lifetime(n) })); + } + Node::TemplateLit(orig) => { + orig.elements_mut().iter_mut().for_each(|te| match te { + TemplateElement::String(s) => map_sym(syms, s), + TemplateElement::Expr(n) => nodes.push(n), + }); + } + Node::Throw(orig) => nodes.push(orig.expr_mut()), + Node::Try(orig) => { + replace_block(&mut nodes, orig.block_mut()); + if let Some(c) = orig.catch_mut() { + if let Some(decl) = c.parameter_mut() { + replace_decl(syms, &mut nodes, decl); + } + replace_block(&mut nodes, c.block_mut()); + }; + if let Some(block) = orig.finally_mut() { + replace_block(&mut nodes, block); + } + } + Node::UnaryOp(orig) => { + nodes.push(unsafe { extend_lifetime(orig.target_mut()) }); + } + Node::WhileLoop(orig) => { + nodes.push(unsafe { extend_lifetime(orig.cond_mut()) }); + nodes.push(unsafe { extend_lifetime(orig.body_mut()) }); + if let Some(sym) = orig.label_mut() { + map_sym(syms, sym); + } + } + Node::Yield(orig) => { + if let Some(n) = orig.expr_mut() { + nodes.push(unsafe { extend_lifetime(n) }); + } + } + Node::GeneratorDecl(orig) => { + map_sym(syms, orig.name_mut()); + orig.parameters_mut() + .items_mut() + .iter_mut() + .for_each(|fp| replace_fp(syms, &mut nodes, fp)); + nodes.extend( + orig.body_mut() + .iter_mut() + .map(|n| unsafe { extend_lifetime(n) }), + ); + } + Node::GeneratorExpr(orig) => { + replace_ge(syms, &mut nodes, orig); + } + Node::This | Node::Empty | Node::Const(_) => {} + } + } +} diff --git a/boa_inputgen/src/lib.rs b/boa_inputgen/src/lib.rs new file mode 100644 index 00000000000..5e3e7d9a308 --- /dev/null +++ b/boa_inputgen/src/lib.rs @@ -0,0 +1,77 @@ +//! Input generator for Boa. +//! +//! Generating JavaScript code is tough, but generating ASTs is easy! Since Boa offers a mechanism +//! for converting from AST to source code, we can use [arbitrary](https://docs.rs/arbitrary) to +//! generate the valid AST and convert it back into source code. +//! +//! There is a snag, however; the interner expects identifiers to replace the +//! [Sym](boa_interner::Sym) instances, but we've generated Syms with arbitrary IDs. To get around +//! this, we walk the AST to replace all identifiers in the AST with valid identifiers as generated +//! by arbitrary. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" +)] +#![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![warn( + clippy::perf, + clippy::single_match_else, + clippy::dbg_macro, + clippy::doc_markdown, + clippy::wildcard_imports, + clippy::struct_excessive_bools, + clippy::doc_markdown, + clippy::semicolon_if_nothing_returned, + clippy::pedantic +)] +#![deny( + clippy::all, + clippy::cast_lossless, + clippy::redundant_closure_for_method_calls, + clippy::use_self, + clippy::unnested_or_patterns, + clippy::trivially_copy_pass_by_ref, + clippy::needless_pass_by_value, + clippy::match_wildcard_for_single_variants, + clippy::map_unwrap_or, + unused_qualifications, + unused_import_braces, + unused_lifetimes, + unreachable_pub, + trivial_numeric_casts, + // rustdoc, + missing_debug_implementations, + missing_copy_implementations, + deprecated_in_future, + meta_variable_misuse, + non_ascii_idents, + rust_2018_compatibility, + rust_2018_idioms, + future_incompatible, + nonstandard_style, +)] +#![allow( + clippy::module_name_repetitions, + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_precision_loss, + clippy::cast_possible_wrap, + clippy::cast_ptr_alignment, + clippy::missing_panics_doc, + clippy::too_many_lines, + clippy::unreadable_literal, + clippy::missing_inline_in_public_items, + clippy::cognitive_complexity, + clippy::must_use_candidate, + clippy::missing_errors_doc, + clippy::as_conversions, + clippy::let_unit_value, + rustdoc::missing_doc_code_examples +)] + +pub use data::FuzzData; +pub(crate) use ident_walk::replace_syms; + +mod data; +mod ident_walk; diff --git a/boa_interner/Cargo.toml b/boa_interner/Cargo.toml index 4793cdfe5ea..4cf0a32cd86 100644 --- a/boa_interner/Cargo.toml +++ b/boa_interner/Cargo.toml @@ -11,6 +11,10 @@ categories = ["data-structures"] license = "Unlicense/MIT" [dependencies] +arbitrary = { version = "1", features = ["derive"], optional = true } string-interner = "0.14.0" serde = { version = "1.0.136", features = ["derive"], optional = true } gc = { version = "0.4.1", features = ["derive"] } + +[features] +fuzzer = ["arbitrary"] diff --git a/boa_interner/src/lib.rs b/boa_interner/src/lib.rs index 09af26fe3a5..5a0943a0194 100644 --- a/boa_interner/src/lib.rs +++ b/boa_interner/src/lib.rs @@ -246,6 +246,7 @@ impl Default for Interner { #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Finalize)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(transparent))] +#[cfg_attr(feature = "fuzzer", derive(arbitrary::Arbitrary))] #[allow(clippy::unsafe_derive_deserialize)] pub struct Sym { value: NonZeroUsize, @@ -297,9 +298,15 @@ impl Sym { } /// Retrieves the raw `NonZeroUsize` for this symbol. + #[cfg(not(feature = "fuzzer"))] const fn as_raw(self) -> NonZeroUsize { self.value } + + #[cfg(feature = "fuzzer")] + pub const fn as_raw(self) -> NonZeroUsize { + self.value + } } impl Symbol for Sym { diff --git a/docs/fuzzing.md b/docs/fuzzing.md new file mode 100644 index 00000000000..02957c57fb4 --- /dev/null +++ b/docs/fuzzing.md @@ -0,0 +1,64 @@ +# Fuzzing + +Fuzzing is a process which allows us to identify and test unusual cases that may not be discovered by manual review. +This can be used to identify cases which may cause a crash, incorrectness, or, at worst, a security threat. + +## Setup + +You'll need to install cargo-fuzz: `cargo install cargo-fuzz` + +You may optionally wish to use a corpus; you will need to have generated this corpus from previous executions of the +fuzzer you intend to use, as these fuzzers are limited to Arbitrary-derived data. [There is currently a PR in process to +allow us to convert from JS source back to Arbitrary](https://github.com/rust-fuzz/arbitrary/pull/94). + +## Picking a fuzzer + +There are currently two fuzzers available: syntax_fuzzer and interp_fuzzer. + +### syntax_fuzzer + +As the name suggests, this fuzzer is designed to identify issues in the syntax processing phase of Boa. You can run the +fuzzer with the following command from the root of the project: + +```shell +cargo fuzz run -s none syntax_fuzzer -- -timeout=5 +``` + +This will execute the fuzzer without sanitisation and a 5-second timeout per test case. In most cases, this will be +sufficient as Rust prevents much of the memory safety violations that would be detected by sanitisation. You can review +options provided to you by cargo-fuzz with `cargo fuzz run --help` and the options provided by libfuzzer with +`cargo fuzz run -s none interp_fuzzer -- -help=1`. + +### interp_fuzzer + +This fuzzer is designed to identify issues in the VM of Boa, but it will also detect issues in the syntax processing. +If you just want to fuzz Boa whatsoever, you probably want this fuzzer. + +You can run the fuzzer with the following command from the root of the project: + +```shell +cargo fuzz run -s none interp_fuzzer -- -timeout=5 +``` + +This will execute the fuzzer without sanitisation and a 5-second timeout per test case. In most cases, this will be +sufficient as Rust prevents much of the memory safety violations that would be detected by sanitisation. You can review +options provided to you by cargo-fuzz with `cargo fuzz run --help` and the options provided by libfuzzer with +`cargo fuzz run -s none interp_fuzzer -- -help=1`. + +## Parallelisation + +You may wish to use multiple cores to fuzz. In that case, you only need to add `--jobs`. As an example, the following +command executes the interpreter fuzzer with 8 threads: + +```shell +cargo fuzz run -s none interp_fuzzer --jobs 8 +``` + +You should not execute the fuzzer in excess of the number of CPUs on your system. Make sure to review that your level +of parallelisation is not being restricted by overhead from disk, which may occur with high CPU counts. + +## Using an existing corpus + +Someone's given you their copy of their corpus. Great! You should put the corpus entries (which will each be named as +the SHA-1 hash of their content) in a folder under `fuzz/corpus/`, replacing `` with your intended +fuzz target (either interp_fuzzer or syntax_fuzzer). diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 00000000000..1a45eee7760 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock new file mode 100644 index 00000000000..63c6c10213c --- /dev/null +++ b/fuzz/Cargo.lock @@ -0,0 +1,542 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "anyhow" +version = "1.0.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" + +[[package]] +name = "arbitrary" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "boa_engine" +version = "0.14.0" +dependencies = [ + "arbitrary", + "bitflags", + "boa_gc", + "boa_interner", + "boa_profiler", + "boa_unicode", + "chrono", + "dyn-clone", + "fast-float", + "gc", + "indexmap", + "num-bigint", + "num-integer", + "num-traits", + "once_cell", + "rand", + "regress", + "rustc-hash", + "ryu-js", + "serde", + "serde_json", + "tap", + "unicode-normalization", +] + +[[package]] +name = "boa_fuzz" +version = "0.0.0" +dependencies = [ + "anyhow", + "boa_engine", + "boa_inputgen", + "boa_interner", + "libfuzzer-sys", +] + +[[package]] +name = "boa_gc" +version = "0.14.0" +dependencies = [ + "gc", +] + +[[package]] +name = "boa_inputgen" +version = "0.14.0" +dependencies = [ + "arbitrary", + "boa_engine", + "boa_interner", + "spin", + "string-interner", +] + +[[package]] +name = "boa_interner" +version = "0.14.0" +dependencies = [ + "arbitrary", + "gc", + "string-interner", +] + +[[package]] +name = "boa_profiler" +version = "0.14.0" + +[[package]] +name = "boa_unicode" +version = "0.14.0" +dependencies = [ + "unicode-general-category", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "derive_arbitrary" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98e23c06c035dac87bd802d98f368df73a7f2cb05a66ffbd1f377e821fac4af9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" + +[[package]] +name = "fast-float" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" + +[[package]] +name = "gc" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edaac0f5832202ebc99520cb77c932248010c4645d20be1dc62d6579f5b3752" +dependencies = [ + "gc_derive", +] + +[[package]] +name = "gc_derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60df8444f094ff7885631d80e78eb7d88c3c2361a98daaabb06256e4500db941" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "getrandom" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "libc" +version = "0.2.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "arbitrary", + "autocfg", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regress" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a92ff21fe8026ce3f2627faaf43606f0b67b014dbc9ccf027181a804f75d92e" +dependencies = [ + "memchr", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "ryu-js" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] +name = "string-interner" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e2531d8525b29b514d25e275a43581320d587b86db302b9a7e464bac579648" +dependencies = [ + "cfg-if", + "hashbrown", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-general-category" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1218098468b8085b19a2824104c70d976491d247ce194bbd9dc77181150cdfd6" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 00000000000..3fde9b7a86d --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "boa_fuzz" +version = "0.0.0" +edition = "2021" +authors = ["Addison Crump "] +publish = false +license = "Unlicense/MIT" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +anyhow = "1" +libfuzzer-sys = "0.4" + +[dependencies.boa_engine] +path = "../boa_engine" +features = ["fuzzer"] + +[dependencies.boa_inputgen] +path = "../boa_inputgen" + +[dependencies.boa_interner] +path = "../boa_interner" +features = ["fuzzer"] + +# Prevent this from interfering with workspaces +[workspace] +members = [""] + +[[bin]] +name = "syntax_fuzzer" +path = "fuzz_targets/syntax_fuzzer.rs" +test = false +doc = false + +[[bin]] +name = "interp_fuzzer" +path = "fuzz_targets/interp_fuzzer.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/interp_fuzzer.rs b/fuzz/fuzz_targets/interp_fuzzer.rs new file mode 100644 index 00000000000..fc0ff8ef58d --- /dev/null +++ b/fuzz/fuzz_targets/interp_fuzzer.rs @@ -0,0 +1,25 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +use boa_engine::syntax::Parser; +use boa_engine::Context; +use boa_inputgen::*; +use boa_interner::Interner; + +fn do_fuzz(data: FuzzData) -> anyhow::Result<()> { + let mut interner = Interner::default(); + let source = data.get_source(); + if let Ok(parsed) = Parser::new(source.as_bytes(), false).parse_all(&mut interner) { + let mut context = Context::new(interner); + context.set_max_insns(1 << 12); + if let Ok(compiled) = context.compile(&parsed) { + let _ = context.execute(compiled); + } + } + Ok(()) +} + +fuzz_target!(|data: FuzzData| { + let _ = do_fuzz(data); +}); diff --git a/fuzz/fuzz_targets/syntax_fuzzer.rs b/fuzz/fuzz_targets/syntax_fuzzer.rs new file mode 100644 index 00000000000..342f8cd238c --- /dev/null +++ b/fuzz/fuzz_targets/syntax_fuzzer.rs @@ -0,0 +1,22 @@ +#![no_main] +#![feature(bench_black_box)] + +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; + +use boa_engine::syntax::Parser; +use boa_inputgen::*; +use boa_interner::Interner; + +fn do_fuzz(data: FuzzData) { + let mut interner = Interner::default(); + let source = data.get_source(); + drop(black_box( + Parser::new(source.as_bytes(), false).parse_all(&mut interner), + )); +} + +fuzz_target!(|data: FuzzData| { + do_fuzz(data); +});