diff --git a/Cargo.lock b/Cargo.lock
index e67f011826..1903b23469 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -32,6 +32,15 @@ version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+[[package]]
+name = "arbitrary"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880"
+dependencies = [
+ "derive_arbitrary",
+]
+
[[package]]
name = "atty"
version = "0.2.14"
@@ -59,6 +68,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
name = "boa_ast"
version = "0.16.0"
dependencies = [
+ "arbitrary",
"bitflags",
"boa_interner",
"boa_macros",
@@ -153,6 +163,7 @@ dependencies = [
name = "boa_interner"
version = "0.16.0"
dependencies = [
+ "arbitrary",
"boa_macros",
"indexmap",
"once_cell",
@@ -539,6 +550,17 @@ dependencies = [
"syn",
]
+[[package]]
+name = "derive_arbitrary"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4903dff04948f22033ca30232ab8eca2c3fc4c913a8b6a34ee5199699814817f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "dirs-next"
version = "2.0.0"
@@ -1077,6 +1099,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
+ "arbitrary",
"autocfg",
"num-integer",
"num-traits",
diff --git a/boa_ast/Cargo.toml b/boa_ast/Cargo.toml
index b248436cf1..733a25cdd8 100644
--- a/boa_ast/Cargo.toml
+++ b/boa_ast/Cargo.toml
@@ -13,6 +13,8 @@ rust-version.workspace = true
[features]
serde = ["boa_interner/serde", "dep:serde"]
+fuzz = ["arbitrary", "boa_interner/fuzz", "num-bigint/arbitrary"]
+
[dependencies]
boa_interner.workspace = true
boa_macros.workspace = true
@@ -20,3 +22,4 @@ rustc-hash = "1.1.0"
serde = { version = "1.0.147", features = ["derive"], optional = true }
bitflags = "1.3.2"
num-bigint = "0.4.3"
+arbitrary = { version = "1", optional = true, features = ["derive"] }
diff --git a/boa_ast/src/declaration/mod.rs b/boa_ast/src/declaration/mod.rs
index 1a51c438cf..02f3e584c8 100644
--- a/boa_ast/src/declaration/mod.rs
+++ b/boa_ast/src/declaration/mod.rs
@@ -27,6 +27,7 @@ pub use variable::*;
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Declaration {
/// See [`Function`]
diff --git a/boa_ast/src/declaration/variable.rs b/boa_ast/src/declaration/variable.rs
index cee81c8186..921f57ffe1 100644
--- a/boa_ast/src/declaration/variable.rs
+++ b/boa_ast/src/declaration/variable.rs
@@ -45,6 +45,7 @@ use super::Declaration;
/// [varstmt]: https://tc39.es/ecma262/#prod-VariableStatement
/// [hoisting]: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct VarDeclaration(pub VariableList);
@@ -81,6 +82,7 @@ impl VisitWith for VarDeclaration {
///
/// [lexical declaration]: https://tc39.es/ecma262/#sec-let-and-const-declarations
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum LexicalDeclaration {
/// A [const]
variable creates a constant whose scope can be either global or local
@@ -160,6 +162,7 @@ impl VisitWith for LexicalDeclaration {
/// List of variables in a variable declaration.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct VariableList {
list: Box<[Variable]>,
@@ -250,6 +253,7 @@ impl TryFrom> for VariableList {
/// [spec2]: https://tc39.es/ecma262/#prod-VariableDeclaration
/// [spec3]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Variable {
binding: Binding,
@@ -332,6 +336,7 @@ impl VisitWith for Variable {
///
/// [spec]: https://tc39.es/ecma262/#sec-declarations-and-the-variable-statement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Binding {
/// A single identifier binding.
diff --git a/boa_ast/src/expression/access.rs b/boa_ast/src/expression/access.rs
index 3b3c83adcc..b66f2a11cf 100644
--- a/boa_ast/src/expression/access.rs
+++ b/boa_ast/src/expression/access.rs
@@ -24,6 +24,7 @@ use core::ops::ControlFlow;
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyAccessField {
/// A constant property field, such as `x.prop`.
@@ -72,6 +73,7 @@ impl VisitWith for PropertyAccessField {
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyAccess {
/// A simple property access (`x.prop`).
@@ -126,6 +128,7 @@ impl VisitWith for PropertyAccess {
/// A simple property access, where the target object is an [`Expression`].
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct SimplePropertyAccess {
target: Box,
@@ -209,6 +212,7 @@ impl VisitWith for SimplePropertyAccess {
/// [spec]: https://tc39.es/ecma262/#prod-MemberExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct PrivatePropertyAccess {
target: Box,
@@ -285,6 +289,7 @@ impl VisitWith for PrivatePropertyAccess {
/// [spec]: https://tc39.es/ecma262/#prod-SuperProperty
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct SuperPropertyAccess {
field: PropertyAccessField,
diff --git a/boa_ast/src/expression/await.rs b/boa_ast/src/expression/await.rs
index 91016ee1ec..8c8786311a 100644
--- a/boa_ast/src/expression/await.rs
+++ b/boa_ast/src/expression/await.rs
@@ -16,6 +16,7 @@ use boa_interner::{Interner, ToIndentedString, ToInternedString};
/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Await {
target: Box,
diff --git a/boa_ast/src/expression/call.rs b/boa_ast/src/expression/call.rs
index c40d3396c9..3787ad99d8 100644
--- a/boa_ast/src/expression/call.rs
+++ b/boa_ast/src/expression/call.rs
@@ -21,6 +21,7 @@ use super::Expression;
/// [spec]: https://tc39.es/ecma262/#prod-CallExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions#Calling_functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Call {
function: Box,
@@ -104,6 +105,7 @@ impl VisitWith for Call {
/// [spec]: https://tc39.es/ecma262/#prod-SuperCall
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct SuperCall {
args: Box<[Expression]>,
diff --git a/boa_ast/src/expression/identifier.rs b/boa_ast/src/expression/identifier.rs
index 11320d06bc..0bf6844744 100644
--- a/boa_ast/src/expression/identifier.rs
+++ b/boa_ast/src/expression/identifier.rs
@@ -43,6 +43,7 @@ pub const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Identifier {
diff --git a/boa_ast/src/expression/literal/array.rs b/boa_ast/src/expression/literal/array.rs
index dcdc2344ec..631d0c9fb3 100644
--- a/boa_ast/src/expression/literal/array.rs
+++ b/boa_ast/src/expression/literal/array.rs
@@ -25,6 +25,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-ArrayLiteral
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ArrayLiteral {
arr: Box<[Option]>,
diff --git a/boa_ast/src/expression/literal/mod.rs b/boa_ast/src/expression/literal/mod.rs
index bc8692d7fb..60951351d3 100644
--- a/boa_ast/src/expression/literal/mod.rs
+++ b/boa_ast/src/expression/literal/mod.rs
@@ -33,6 +33,7 @@ use super::Expression;
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
/// A string literal is zero or more characters enclosed in double (`"`) or single (`'`) quotation marks.
diff --git a/boa_ast/src/expression/literal/object.rs b/boa_ast/src/expression/literal/object.rs
index fcd5a8df6d..1736a2e31f 100644
--- a/boa_ast/src/expression/literal/object.rs
+++ b/boa_ast/src/expression/literal/object.rs
@@ -33,6 +33,7 @@ use core::ops::ControlFlow;
/// [primitive]: https://developer.mozilla.org/en-US/docs/Glossary/primitive
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectLiteral {
properties: Box<[PropertyDefinition]>,
diff --git a/boa_ast/src/expression/literal/template.rs b/boa_ast/src/expression/literal/template.rs
index d97f5f63fc..7c3f4bc189 100644
--- a/boa_ast/src/expression/literal/template.rs
+++ b/boa_ast/src/expression/literal/template.rs
@@ -21,6 +21,7 @@ use crate::{
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
/// [spec]: https://tc39.es/ecma262/#sec-template-literals
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct TemplateLiteral {
elements: Box<[TemplateElement]>,
@@ -40,6 +41,7 @@ impl From for Expression {
///
/// [spec]: https://tc39.es/ecma262/#sec-template-literals
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum TemplateElement {
/// A simple string.
diff --git a/boa_ast/src/expression/mod.rs b/boa_ast/src/expression/mod.rs
index e0fc2587d8..ad04808094 100644
--- a/boa_ast/src/expression/mod.rs
+++ b/boa_ast/src/expression/mod.rs
@@ -51,6 +51,7 @@ pub mod operator;
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
/// The JavaScript `this` keyword refers to the object it belongs to.
diff --git a/boa_ast/src/expression/new.rs b/boa_ast/src/expression/new.rs
index cdc36d70ac..f93e7a9bea 100644
--- a/boa_ast/src/expression/new.rs
+++ b/boa_ast/src/expression/new.rs
@@ -21,6 +21,7 @@ use super::Expression;
/// [spec]: https://tc39.es/ecma262/#prod-NewExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct New {
call: Call,
diff --git a/boa_ast/src/expression/operator/assign/mod.rs b/boa_ast/src/expression/operator/assign/mod.rs
index dcd12a31cc..a2bbc69246 100644
--- a/boa_ast/src/expression/operator/assign/mod.rs
+++ b/boa_ast/src/expression/operator/assign/mod.rs
@@ -28,6 +28,7 @@ use crate::{
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Assign {
op: AssignOp,
@@ -110,6 +111,7 @@ impl VisitWith for Assign {
///
/// [spec]: hhttps://tc39.es/ecma262/#prod-LeftHandSideExpression
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum AssignTarget {
/// A simple identifier, such as `a`.
diff --git a/boa_ast/src/expression/operator/assign/op.rs b/boa_ast/src/expression/operator/assign/op.rs
index 8a815a022d..a2c25df8cf 100644
--- a/boa_ast/src/expression/operator/assign/op.rs
+++ b/boa_ast/src/expression/operator/assign/op.rs
@@ -12,6 +12,7 @@
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AssignOp {
/// The assignment operator assigns the value of the right operand to the left operand.
diff --git a/boa_ast/src/expression/operator/binary/mod.rs b/boa_ast/src/expression/operator/binary/mod.rs
index c63cff6d4a..2e28039264 100644
--- a/boa_ast/src/expression/operator/binary/mod.rs
+++ b/boa_ast/src/expression/operator/binary/mod.rs
@@ -31,6 +31,7 @@ use crate::{
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Binary {
op: BinaryOp,
diff --git a/boa_ast/src/expression/operator/binary/op.rs b/boa_ast/src/expression/operator/binary/op.rs
index 6430bb00ba..60ace26129 100644
--- a/boa_ast/src/expression/operator/binary/op.rs
+++ b/boa_ast/src/expression/operator/binary/op.rs
@@ -4,6 +4,7 @@ use std::fmt::{Display, Formatter, Result};
/// This represents a binary operation between two values.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BinaryOp {
/// Numeric operation.
@@ -86,6 +87,7 @@ impl Display for BinaryOp {
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ArithmeticOp {
/// The addition operator produces the sum of numeric operands or string concatenation.
@@ -196,6 +198,7 @@ impl Display for ArithmeticOp {
///
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BitwiseOp {
/// Performs the AND operation on each pair of bits. a AND b yields 1 only if both a and b are 1.
@@ -319,6 +322,7 @@ impl Display for BitwiseOp {
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RelationalOp {
/// The equality operator converts the operands if they are not of the same type, then applies
@@ -512,6 +516,7 @@ impl Display for RelationalOp {
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LogicalOp {
/// The logical AND operator returns the value of the first operand if it can be coerced into `false`;
diff --git a/boa_ast/src/expression/operator/conditional.rs b/boa_ast/src/expression/operator/conditional.rs
index a2e84237fb..a0ac5711d6 100644
--- a/boa_ast/src/expression/operator/conditional.rs
+++ b/boa_ast/src/expression/operator/conditional.rs
@@ -21,6 +21,7 @@ use core::ops::ControlFlow;
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Conditional {
condition: Box,
diff --git a/boa_ast/src/expression/operator/unary/mod.rs b/boa_ast/src/expression/operator/unary/mod.rs
index 5619ca20ef..ecf77b0f9c 100644
--- a/boa_ast/src/expression/operator/unary/mod.rs
+++ b/boa_ast/src/expression/operator/unary/mod.rs
@@ -30,6 +30,7 @@ use crate::visitor::{VisitWith, Visitor, VisitorMut};
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Unary {
op: UnaryOp,
diff --git a/boa_ast/src/expression/operator/unary/op.rs b/boa_ast/src/expression/operator/unary/op.rs
index 63527dce23..ce3c7f607c 100644
--- a/boa_ast/src/expression/operator/unary/op.rs
+++ b/boa_ast/src/expression/operator/unary/op.rs
@@ -11,6 +11,7 @@
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnaryOp {
/// The increment operator increments (adds one to) its operand and returns a value.
diff --git a/boa_ast/src/expression/optional.rs b/boa_ast/src/expression/optional.rs
index b813dca789..7d7ff48cdd 100644
--- a/boa_ast/src/expression/optional.rs
+++ b/boa_ast/src/expression/optional.rs
@@ -9,6 +9,7 @@ use super::{access::PropertyAccessField, Expression};
/// List of valid operations in an [`Optional`] chain.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum OptionalOperationKind {
/// A property access (`a?.prop`).
@@ -73,6 +74,7 @@ impl VisitWith for OptionalOperationKind {
/// In contrast, a non-shorted operation (`.prop`) will try to access the property, even if the target
/// is `undefined` or `null`.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct OptionalOperation {
kind: OptionalOperationKind,
@@ -175,6 +177,7 @@ impl VisitWith for OptionalOperation {
/// [spec]: https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#prod-OptionalExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Optional {
target: Box,
diff --git a/boa_ast/src/expression/spread.rs b/boa_ast/src/expression/spread.rs
index 1d39ccab23..fc86dde42a 100644
--- a/boa_ast/src/expression/spread.rs
+++ b/boa_ast/src/expression/spread.rs
@@ -23,6 +23,7 @@ use super::Expression;
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Spread {
target: Box,
diff --git a/boa_ast/src/expression/tagged_template.rs b/boa_ast/src/expression/tagged_template.rs
index 0420bb5fdd..2a01bbbf20 100644
--- a/boa_ast/src/expression/tagged_template.rs
+++ b/boa_ast/src/expression/tagged_template.rs
@@ -14,6 +14,7 @@ use super::Expression;
/// [moz]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates
/// [spec]: https://tc39.es/ecma262/#sec-tagged-templates
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct TaggedTemplate {
tag: Box,
diff --git a/boa_ast/src/expression/yield.rs b/boa_ast/src/expression/yield.rs
index f6ba68c7a1..7357e00c64 100644
--- a/boa_ast/src/expression/yield.rs
+++ b/boa_ast/src/expression/yield.rs
@@ -14,6 +14,7 @@ use super::Expression;
/// [spec]: https://tc39.es/ecma262/#prod-YieldExpression
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Yield {
target: Option>,
diff --git a/boa_ast/src/function/arrow_function.rs b/boa_ast/src/function/arrow_function.rs
index 794665b978..c02c3a8e2a 100644
--- a/boa_ast/src/function/arrow_function.rs
+++ b/boa_ast/src/function/arrow_function.rs
@@ -19,6 +19,7 @@ use super::FormalParameterList;
/// [spec]: https://tc39.es/ecma262/#prod-ArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ArrowFunction {
name: Option,
diff --git a/boa_ast/src/function/async_arrow_function.rs b/boa_ast/src/function/async_arrow_function.rs
index 351a3bdbd1..191de2f542 100644
--- a/boa_ast/src/function/async_arrow_function.rs
+++ b/boa_ast/src/function/async_arrow_function.rs
@@ -19,6 +19,7 @@ use boa_interner::{Interner, ToIndentedString};
/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncArrowFunction {
name: Option,
diff --git a/boa_ast/src/function/async_function.rs b/boa_ast/src/function/async_function.rs
index cda3bcbbf3..17df80504f 100644
--- a/boa_ast/src/function/async_function.rs
+++ b/boa_ast/src/function/async_function.rs
@@ -20,6 +20,7 @@ use super::FormalParameterList;
/// [spec]: https://tc39.es/ecma262/#sec-async-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncFunction {
name: Option,
diff --git a/boa_ast/src/function/async_generator.rs b/boa_ast/src/function/async_generator.rs
index bb04b089dc..420a8bbd23 100644
--- a/boa_ast/src/function/async_generator.rs
+++ b/boa_ast/src/function/async_generator.rs
@@ -19,6 +19,7 @@ use super::FormalParameterList;
/// [spec]: https://tc39.es/ecma262/#sec-async-generator-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function*
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncGenerator {
name: Option,
diff --git a/boa_ast/src/function/class.rs b/boa_ast/src/function/class.rs
index 6b88d5ba99..dea5811eed 100644
--- a/boa_ast/src/function/class.rs
+++ b/boa_ast/src/function/class.rs
@@ -22,6 +22,7 @@ use super::Function;
/// [spec]: https://tc39.es/ecma262/#sec-class-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Class {
name: Option,
@@ -406,6 +407,7 @@ impl VisitWith for Class {
///
/// [spec]: https://tc39.es/ecma262/#prod-ClassElement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ClassElement {
/// A method definition, including `get` and `set` accessors.
diff --git a/boa_ast/src/function/generator.rs b/boa_ast/src/function/generator.rs
index fe5c0ebff7..5478633d46 100644
--- a/boa_ast/src/function/generator.rs
+++ b/boa_ast/src/function/generator.rs
@@ -21,6 +21,7 @@ use super::FormalParameterList;
/// [spec]: https://tc39.es/ecma262/#sec-generator-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Generator {
name: Option,
diff --git a/boa_ast/src/function/mod.rs b/boa_ast/src/function/mod.rs
index a39c6941b2..4aa1d06c39 100644
--- a/boa_ast/src/function/mod.rs
+++ b/boa_ast/src/function/mod.rs
@@ -57,6 +57,7 @@ use super::Declaration;
/// [spec]: https://tc39.es/ecma262/#sec-function-definitions
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Function {
name: Option,
diff --git a/boa_ast/src/function/parameters.rs b/boa_ast/src/function/parameters.rs
index 7bede15e81..9d80ebd86b 100644
--- a/boa_ast/src/function/parameters.rs
+++ b/boa_ast/src/function/parameters.rs
@@ -166,6 +166,14 @@ impl VisitWith for FormalParameterList {
}
}
+#[cfg(feature = "fuzz")]
+impl<'a> arbitrary::Arbitrary<'a> for FormalParameterList {
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result {
+ let params: Vec = u.arbitrary()?;
+ Ok(Self::from(params))
+ }
+}
+
bitflags! {
/// Flags for a [`FormalParameterList`].
#[allow(clippy::unsafe_derive_deserialize)]
@@ -206,6 +214,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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct FormalParameter {
variable: Variable,
diff --git a/boa_ast/src/pattern.rs b/boa_ast/src/pattern.rs
index aed259ae88..bd9b869430 100644
--- a/boa_ast/src/pattern.rs
+++ b/boa_ast/src/pattern.rs
@@ -36,6 +36,7 @@ use core::ops::ControlFlow;
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Pattern {
/// An object pattern (`let {a, b, c} = object`).
@@ -108,6 +109,7 @@ impl VisitWith for Pattern {
/// [spec1]: https://tc39.es/ecma262/#prod-ObjectBindingPattern
/// [spec2]: https://tc39.es/ecma262/#prod-ObjectAssignmentPattern
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ObjectPattern(Box<[ObjectPatternElement]>);
@@ -196,6 +198,7 @@ impl VisitWith for ObjectPattern {
/// [spec1]: https://tc39.es/ecma262/#prod-ArrayBindingPattern
/// [spec2]: https://tc39.es/ecma262/#prod-ArrayAssignmentPattern
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ArrayPattern(Box<[ArrayPatternElement]>);
@@ -270,6 +273,7 @@ impl VisitWith for ArrayPattern {
/// [spec1]: https://tc39.es/ecma262/#prod-BindingProperty
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentProperty
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ObjectPatternElement {
/// SingleName represents one of the following properties:
@@ -569,6 +573,7 @@ impl VisitWith for ObjectPatternElement {
/// [spec1]: https://tc39.es/ecma262/#prod-BindingElement
/// [spec2]: https://tc39.es/ecma262/#prod-AssignmentElement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ArrayPatternElement {
/// Elision represents the elision of an item in the array binding pattern.
diff --git a/boa_ast/src/property.rs b/boa_ast/src/property.rs
index 7a2cc76065..c653719903 100644
--- a/boa_ast/src/property.rs
+++ b/boa_ast/src/property.rs
@@ -25,6 +25,7 @@ use super::{
/// [mdn]: https://developer.mozilla.org/en-US/docs/Glossary/property/JavaScript
// TODO: Support all features: https://tc39.es/ecma262/#prod-PropertyDefinition
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyDefinition {
/// Puts a variable into an object.
@@ -137,6 +138,7 @@ impl VisitWith for 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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum MethodDefinition {
/// The `get` syntax binds an object property to a function that will be called when that property is looked up.
@@ -248,6 +250,7 @@ impl VisitWith for MethodDefinition {
///
/// [spec]: https://tc39.es/ecma262/#prod-PropertyName
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyName {
/// A `Literal` property name can be either an identifier, a string or a numeric literal.
diff --git a/boa_ast/src/statement/block.rs b/boa_ast/src/statement/block.rs
index 37dea28870..afd402ae33 100644
--- a/boa_ast/src/statement/block.rs
+++ b/boa_ast/src/statement/block.rs
@@ -23,6 +23,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-BlockStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Block {
#[cfg_attr(feature = "serde", serde(flatten))]
diff --git a/boa_ast/src/statement/if.rs b/boa_ast/src/statement/if.rs
index 598c57f61c..f41cbae661 100644
--- a/boa_ast/src/statement/if.rs
+++ b/boa_ast/src/statement/if.rs
@@ -26,6 +26,7 @@ use core::ops::ControlFlow;
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct If {
condition: Expression,
diff --git a/boa_ast/src/statement/iteration/break.rs b/boa_ast/src/statement/iteration/break.rs
index ad0cc2dc25..578f91a902 100644
--- a/boa_ast/src/statement/iteration/break.rs
+++ b/boa_ast/src/statement/iteration/break.rs
@@ -19,6 +19,7 @@ use crate::Statement;
/// [spec]: https://tc39.es/ecma262/#prod-BreakStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/break
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Break {
label: Option,
diff --git a/boa_ast/src/statement/iteration/continue.rs b/boa_ast/src/statement/iteration/continue.rs
index 2cbe737c82..ed2a19f527 100644
--- a/boa_ast/src/statement/iteration/continue.rs
+++ b/boa_ast/src/statement/iteration/continue.rs
@@ -17,6 +17,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-ContinueStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/continue
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Continue {
label: Option,
diff --git a/boa_ast/src/statement/iteration/do_while_loop.rs b/boa_ast/src/statement/iteration/do_while_loop.rs
index 11015d6738..34fd03f33a 100644
--- a/boa_ast/src/statement/iteration/do_while_loop.rs
+++ b/boa_ast/src/statement/iteration/do_while_loop.rs
@@ -20,6 +20,7 @@ use core::ops::ControlFlow;
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct DoWhileLoop {
body: Box,
diff --git a/boa_ast/src/statement/iteration/for_in_loop.rs b/boa_ast/src/statement/iteration/for_in_loop.rs
index eb49f49ba5..41e11083ef 100644
--- a/boa_ast/src/statement/iteration/for_in_loop.rs
+++ b/boa_ast/src/statement/iteration/for_in_loop.rs
@@ -15,6 +15,7 @@ use core::ops::ControlFlow;
/// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForInLoop {
initializer: IterableLoopInitializer,
diff --git a/boa_ast/src/statement/iteration/for_loop.rs b/boa_ast/src/statement/iteration/for_loop.rs
index 4c56e3784b..ea66110044 100644
--- a/boa_ast/src/statement/iteration/for_loop.rs
+++ b/boa_ast/src/statement/iteration/for_loop.rs
@@ -19,6 +19,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForLoop {
#[cfg_attr(feature = "serde", serde(flatten))]
@@ -135,6 +136,7 @@ impl VisitWith for ForLoop {
/// Inner structure to avoid multiple indirections in the heap.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
struct InnerForLoop {
init: Option,
@@ -194,6 +196,7 @@ impl InnerForLoop {
///
/// [spec]: https://tc39.es/ecma262/#prod-ForStatement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ForLoopInitializer {
/// An expression initializer.
diff --git a/boa_ast/src/statement/iteration/for_of_loop.rs b/boa_ast/src/statement/iteration/for_of_loop.rs
index c8f3cbfec7..ead1768106 100644
--- a/boa_ast/src/statement/iteration/for_of_loop.rs
+++ b/boa_ast/src/statement/iteration/for_of_loop.rs
@@ -20,6 +20,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
/// [forawait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct ForOfLoop {
init: IterableLoopInitializer,
diff --git a/boa_ast/src/statement/iteration/mod.rs b/boa_ast/src/statement/iteration/mod.rs
index 78ef2f5e66..4377bedc69 100644
--- a/boa_ast/src/statement/iteration/mod.rs
+++ b/boa_ast/src/statement/iteration/mod.rs
@@ -35,6 +35,7 @@ use boa_interner::{Interner, ToInternedString};
///
/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum IterableLoopInitializer {
/// An already declared variable.
diff --git a/boa_ast/src/statement/iteration/while_loop.rs b/boa_ast/src/statement/iteration/while_loop.rs
index 8af88e47d4..ab56818210 100644
--- a/boa_ast/src/statement/iteration/while_loop.rs
+++ b/boa_ast/src/statement/iteration/while_loop.rs
@@ -19,6 +19,7 @@ use core::ops::ControlFlow;
/// [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 = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct WhileLoop {
condition: Expression,
diff --git a/boa_ast/src/statement/labelled.rs b/boa_ast/src/statement/labelled.rs
index 3f3dc70679..89884ff853 100644
--- a/boa_ast/src/statement/labelled.rs
+++ b/boa_ast/src/statement/labelled.rs
@@ -17,6 +17,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-LabelledItem
/// [label-fn]: https://tc39.es/ecma262/#sec-labelled-function-declarations
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum LabelledItem {
/// A labelled [`Function`].
@@ -81,6 +82,7 @@ impl VisitWith for LabelledItem {
///
/// [spec]: https://tc39.es/ecma262/#sec-labelled-statements
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Labelled {
item: Box,
diff --git a/boa_ast/src/statement/mod.rs b/boa_ast/src/statement/mod.rs
index 1978221e5d..f412a47b98 100644
--- a/boa_ast/src/statement/mod.rs
+++ b/boa_ast/src/statement/mod.rs
@@ -38,6 +38,7 @@ use super::{declaration::VarDeclaration, expression::Expression};
///
/// See the [module level documentation][self] for more information.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum Statement {
/// See [`Block`].
diff --git a/boa_ast/src/statement/return.rs b/boa_ast/src/statement/return.rs
index ded4eb3715..6cd3d36ba2 100644
--- a/boa_ast/src/statement/return.rs
+++ b/boa_ast/src/statement/return.rs
@@ -24,6 +24,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-ReturnStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Return {
target: Option,
diff --git a/boa_ast/src/statement/switch.rs b/boa_ast/src/statement/switch.rs
index 74b9430a2f..77ce5d7944 100644
--- a/boa_ast/src/statement/switch.rs
+++ b/boa_ast/src/statement/switch.rs
@@ -19,6 +19,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-CaseClause
/// [truthy]: https://developer.mozilla.org/en-US/docs/Glossary/Truthy
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Case {
condition: Expression,
@@ -83,6 +84,7 @@ impl VisitWith for Case {
/// [spec]: https://tc39.es/ecma262/#prod-SwitchStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/switch
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Switch {
val: Expression,
diff --git a/boa_ast/src/statement/throw.rs b/boa_ast/src/statement/throw.rs
index 3a955789cd..e3936d5c91 100644
--- a/boa_ast/src/statement/throw.rs
+++ b/boa_ast/src/statement/throw.rs
@@ -21,6 +21,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-ThrowStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Throw {
target: Expression,
diff --git a/boa_ast/src/statement/try.rs b/boa_ast/src/statement/try.rs
index 42cbadd19f..c8c673e69a 100644
--- a/boa_ast/src/statement/try.rs
+++ b/boa_ast/src/statement/try.rs
@@ -23,6 +23,7 @@ use core::ops::ControlFlow;
/// [spec]: https://tc39.es/ecma262/#prod-TryStatement
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Try {
block: Block,
@@ -31,6 +32,7 @@ pub struct Try {
/// The type of error handler in a [`Try`] statement.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum ErrorHandler {
/// A [`Catch`] error handler.
@@ -137,6 +139,7 @@ impl VisitWith for Try {
/// Catch block.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Catch {
parameter: Option,
@@ -205,6 +208,7 @@ impl VisitWith for Catch {
/// Finally block.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub struct Finally {
block: Block,
diff --git a/boa_ast/src/statement_list.rs b/boa_ast/src/statement_list.rs
index f035ecaf69..35221ecf07 100644
--- a/boa_ast/src/statement_list.rs
+++ b/boa_ast/src/statement_list.rs
@@ -18,6 +18,7 @@ use std::cmp::Ordering;
///
/// [spec]: https://tc39.es/ecma262/#prod-StatementListItem
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, PartialEq)]
pub enum StatementListItem {
/// See [`Statement`].
@@ -198,3 +199,13 @@ impl VisitWith for StatementList {
ControlFlow::Continue(())
}
}
+
+#[cfg(feature = "fuzz")]
+impl<'a> arbitrary::Arbitrary<'a> for StatementList {
+ fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result {
+ Ok(Self {
+ statements: u.arbitrary()?,
+ strict: false, // disable strictness; this is *not* in source data
+ })
+ }
+}
diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml
index 5c9caeaea8..b8a565eceb 100644
--- a/boa_engine/Cargo.toml
+++ b/boa_engine/Cargo.toml
@@ -24,6 +24,8 @@ intl = [
"dep:sys-locale"
]
+fuzz = ["boa_ast/fuzz", "boa_interner/fuzz"]
+
# Enable Boa's WHATWG console object implementation.
console = []
diff --git a/boa_interner/Cargo.toml b/boa_interner/Cargo.toml
index c17105a3eb..5f07b9289d 100644
--- a/boa_interner/Cargo.toml
+++ b/boa_interner/Cargo.toml
@@ -10,6 +10,9 @@ license.workspace = true
repository.workspace = true
rust-version.workspace = true
+[features]
+fuzz = ["arbitrary"]
+
[dependencies]
boa_macros.workspace = true
serde = { version = "1.0.147", features = ["derive"], optional = true }
@@ -18,4 +21,4 @@ rustc-hash = "1.1.0"
static_assertions = "1.1.0"
once_cell = "1.16.0"
indexmap = "1.9.1"
-
+arbitrary = { version = "1", optional = true, features = ["derive"] }
diff --git a/boa_interner/src/sym.rs b/boa_interner/src/sym.rs
index ce154e176e..f77c873962 100644
--- a/boa_interner/src/sym.rs
+++ b/boa_interner/src/sym.rs
@@ -13,6 +13,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
+#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
#[allow(clippy::unsafe_derive_deserialize)]
pub struct Sym {
value: NonZeroUsize,
@@ -127,7 +128,7 @@ impl Sym {
/// Returns the internal value of the [`Sym`]
#[inline]
- pub(super) const fn get(self) -> usize {
+ pub const fn get(self) -> usize {
self.value.get()
}
}
diff --git a/fuzz/.gitignore b/fuzz/.gitignore
new file mode 100644
index 0000000000..1a45eee776
--- /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 0000000000..beb643d96d
--- /dev/null
+++ b/fuzz/Cargo.lock
@@ -0,0 +1,831 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "arbitrary"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880"
+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_ast"
+version = "0.16.0"
+dependencies = [
+ "arbitrary",
+ "bitflags",
+ "boa_interner",
+ "boa_macros",
+ "num-bigint",
+ "rustc-hash",
+]
+
+[[package]]
+name = "boa_engine"
+version = "0.16.0"
+dependencies = [
+ "bitflags",
+ "boa_ast",
+ "boa_gc",
+ "boa_interner",
+ "boa_macros",
+ "boa_parser",
+ "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",
+ "sptr",
+ "static_assertions",
+ "tap",
+ "thiserror",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "boa_fuzz"
+version = "0.0.0"
+dependencies = [
+ "boa_ast",
+ "boa_engine",
+ "boa_interner",
+ "boa_parser",
+ "libfuzzer-sys",
+]
+
+[[package]]
+name = "boa_gc"
+version = "0.16.0"
+dependencies = [
+ "gc",
+]
+
+[[package]]
+name = "boa_interner"
+version = "0.16.0"
+dependencies = [
+ "arbitrary",
+ "boa_macros",
+ "indexmap",
+ "once_cell",
+ "phf",
+ "rustc-hash",
+ "static_assertions",
+]
+
+[[package]]
+name = "boa_macros"
+version = "0.16.0"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "boa_parser"
+version = "0.16.0"
+dependencies = [
+ "bitflags",
+ "boa_ast",
+ "boa_interner",
+ "boa_macros",
+ "boa_profiler",
+ "boa_unicode",
+ "fast-float",
+ "num-bigint",
+ "num-traits",
+ "rustc-hash",
+]
+
+[[package]]
+name = "boa_profiler"
+version = "0.16.0"
+
+[[package]]
+name = "boa_unicode"
+version = "0.16.0"
+dependencies = [
+ "unicode-general-category",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+
+[[package]]
+name = "cc"
+version = "1.0.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574"
+dependencies = [
+ "jobserver",
+]
+
+[[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.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-integer",
+ "num-traits",
+ "time",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "cxx"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.80"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "derive_arbitrary"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4903dff04948f22033ca30232ab8eca2c3fc4c913a8b6a34ee5199699814817f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "dyn-clone"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
+
+[[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.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
+dependencies = [
+ "cxx",
+ "cxx-build",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+
+[[package]]
+name = "jobserver"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8fff891139ee62800da71b7fd5b508d570b9ad95e614a53c6f453ca08366038"
+dependencies = [
+ "arbitrary",
+ "cc",
+ "once_cell",
+]
+
+[[package]]
+name = "link-cplusplus"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[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.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
+
+[[package]]
+name = "phf"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
+dependencies = [
+ "phf_macros",
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
+dependencies = [
+ "phf_shared",
+ "rand",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
+dependencies = [
+ "phf_generator",
+ "phf_shared",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+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.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "ryu-js"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f"
+
+[[package]]
+name = "scratch"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
+
+[[package]]
+name = "serde"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
+
+[[package]]
+name = "sptr"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "syn"
+version = "1.0.103"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[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 = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "time"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
+dependencies = [
+ "libc",
+ "wasi 0.10.0+wasi-snapshot-preview1",
+ "winapi",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+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.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
+
+[[package]]
+name = "wasi"
+version = "0.10.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[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-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[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 0000000000..cc86d751da
--- /dev/null
+++ b/fuzz/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "boa_fuzz"
+version = "0.0.0"
+publish = false
+edition = "2021"
+
+[package.metadata]
+cargo-fuzz = true
+
+[dependencies]
+libfuzzer-sys = "0.4"
+
+boa_ast = { path = "../boa_ast", features = ["fuzz"] }
+boa_engine = { path = "../boa_engine", features = ["fuzz"] }
+boa_interner = { path = "../boa_interner", features = ["fuzz"] }
+boa_parser = { path = "../boa_parser" }
+
+# Prevent this from interfering with workspaces
+[workspace]
+members = ["."]
+
+[profile.release]
+debug = 1
+
+[[bin]]
+name = "parser-idempotency"
+path = "fuzz_targets/parser-idempotency.rs"
+test = false
+doc = false
diff --git a/fuzz/README.md b/fuzz/README.md
new file mode 100644
index 0000000000..097e46ccb9
--- /dev/null
+++ b/fuzz/README.md
@@ -0,0 +1,37 @@
+# boa_engine-fuzz
+
+This directory contains fuzzers which can be used to automatically identify faults present in Boa. All the fuzzers in
+this directory are [grammar-aware](https://www.fuzzingbook.org/html/Grammars.html) (based on
+[Arbitrary](https://docs.rs/arbitrary/latest/arbitrary/)) and coverage-guided. See [common.rs](fuzz/fuzz_targets/common.rs)
+for implementation specifics.
+
+You can run any fuzzer you wish with the following command (replacing `your-fuzzer` with a fuzzer availble in
+fuzz_targets, e.g. `parser-idempotency`):
+
+```bash
+cargo fuzz run -s none your-fuzzer
+```
+
+Note that you may wish to use a different sanitizer option (`-s`) according to what kind of issue you're looking for.
+Refer to the [cargo-fuzz book](https://rust-fuzz.github.io/book/cargo-fuzz.html) for details on how to select a
+sanitizer and other flags.
+
+## Parser Fuzzer
+
+The parser fuzzer, located in [parser-idempotency.rs](fuzz/fuzz_targets/parser-idempotency.rs), identifies
+correctness issues in both the parser and the AST-to-source conversion process (e.g., via `to_interned_string`) by
+searching for inputs which are not idempotent over parsing and conversion back to source. It does this by doing the
+following:
+
+1. Generate an arbitrary AST
+2. Convert that AST to source code with `to_interned_string`; we'll call this the "original source"
+3. Parse the original source into an AST; we'll call this the "first AST"
+ - Arbitrary ASTs aren't guaranteed to be parseable; to avoid errors caused by this, we discard errors here.
+4. Convert the first AST to source code with `to_interned_string`; we'll call this the "first source"
+5. Parse the first source into an AST; we'll call this the "second AST"
+ - Since the original source was parseable, the first source must be parseable; emit any errors parsing produces.
+6. Compare the first AST and the second AST. If they are not equal, emit an error.
+ - An error here indicates that either the parser or the AST-to-source conversion lost information or added incorrect
+ information, as the inputs parsed between the two should be the same.
+
+In this way, this fuzzer can identify correctness issues present in the parser.
diff --git a/fuzz/fuzz_targets/common.rs b/fuzz/fuzz_targets/common.rs
new file mode 100644
index 0000000000..161a6145c0
--- /dev/null
+++ b/fuzz/fuzz_targets/common.rs
@@ -0,0 +1,74 @@
+use boa_ast::{
+ visitor::{VisitWith, VisitorMut},
+ Expression, StatementList,
+};
+use boa_interner::{Interner, Sym};
+use libfuzzer_sys::arbitrary;
+use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};
+use std::fmt::{Debug, Formatter};
+use std::ops::ControlFlow;
+
+/// Context for performing fuzzing. This structure contains both the generated AST as well as the
+/// context used to resolve the symbols therein.
+pub struct FuzzData {
+ pub interner: Interner,
+ pub ast: StatementList,
+}
+
+impl<'a> Arbitrary<'a> for FuzzData {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result {
+ let mut interner = Interner::with_capacity(8);
+ let mut syms_available = Vec::with_capacity(8);
+ for c in 'a'..='h' {
+ syms_available.push(interner.get_or_intern(&*String::from(c)));
+ }
+
+ let mut ast = StatementList::arbitrary(u)?;
+
+ struct FuzzReplacer<'a, 's, 'u> {
+ syms: &'s [Sym],
+ u: &'u mut Unstructured<'a>,
+ }
+ impl<'a, 's, 'u, 'ast> VisitorMut<'ast> for FuzzReplacer<'a, 's, 'u> {
+ type BreakTy = arbitrary::Error;
+
+ // TODO arbitrary strings literals?
+
+ fn visit_expression_mut(
+ &mut self,
+ node: &'ast mut Expression,
+ ) -> ControlFlow {
+ if matches!(node, Expression::FormalParameterList(_)) {
+ match self.u.arbitrary() {
+ Ok(id) => *node = Expression::Identifier(id),
+ Err(e) => return ControlFlow::Break(e),
+ }
+ }
+ node.visit_with_mut(self)
+ }
+
+ fn visit_sym_mut(&mut self, node: &'ast mut Sym) -> ControlFlow {
+ *node = self.syms[node.get() % self.syms.len()];
+ ControlFlow::Continue(())
+ }
+ }
+
+ let mut replacer = FuzzReplacer {
+ syms: &syms_available,
+ u,
+ };
+ if let ControlFlow::Break(e) = replacer.visit_statement_list_mut(&mut ast) {
+ Err(e)
+ } else {
+ Ok(Self { interner, ast })
+ }
+ }
+}
+
+impl Debug for FuzzData {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("FuzzData")
+ .field("ast", &self.ast)
+ .finish_non_exhaustive()
+ }
+}
diff --git a/fuzz/fuzz_targets/parser-idempotency.rs b/fuzz/fuzz_targets/parser-idempotency.rs
new file mode 100644
index 0000000000..cba2135b6a
--- /dev/null
+++ b/fuzz/fuzz_targets/parser-idempotency.rs
@@ -0,0 +1,74 @@
+#![no_main]
+
+mod common;
+
+use crate::common::FuzzData;
+use boa_interner::ToInternedString;
+use boa_parser::Parser;
+use libfuzzer_sys::fuzz_target;
+use libfuzzer_sys::Corpus;
+use std::error::Error;
+use std::io::Cursor;
+
+/// Fuzzer test harness. This function accepts the arbitrary AST and performs the fuzzing operation.
+///
+/// See [README.md](../README.md) for details on the design of this fuzzer.
+fn do_fuzz(mut data: FuzzData) -> Result<(), Box> {
+ let original = data.ast.to_interned_string(&data.interner);
+
+ let mut parser = Parser::new(Cursor::new(&original));
+
+ let before = data.interner.len();
+ // For a variety of reasons, we may not actually produce valid code here (e.g., nameless function).
+ // Fail fast and only make the next checks if we were valid.
+ if let Ok(first) = parser.parse_all(&mut data.interner) {
+ let after_first = data.interner.len();
+ let first_interned = first.to_interned_string(&data.interner);
+
+ assert_eq!(
+ before,
+ after_first,
+ "The number of interned symbols changed; a new string was read.\nBefore:\n{}\nAfter:\n{}\nBefore (AST):\n{:#?}\nAfter (AST):\n{:#?}",
+ original,
+ first_interned,
+ data.ast,
+ first
+ );
+ let mut parser = Parser::new(Cursor::new(&first_interned));
+
+ // Now, we most assuredly should produce valid code. It has already gone through a first pass.
+ let second = parser
+ .parse_all(&mut data.interner)
+ .expect("Could not parse the first-pass interned copy.");
+ let second_interned = second.to_interned_string(&data.interner);
+ let after_second = data.interner.len();
+ assert_eq!(
+ after_first,
+ after_second,
+ "The number of interned symbols changed; a new string was read.\nBefore:\n{}\nAfter:\n{}\nBefore (AST):\n{:#?}\nAfter (AST):\n{:#?}",
+ first_interned,
+ second_interned,
+ first,
+ second
+ );
+ assert_eq!(
+ first,
+ second,
+ "Expected the same AST after two intern passes, but found dissimilar.\nOriginal:\n{}\nFirst:\n{}\nSecond:\n{}",
+ original,
+ first_interned,
+ second_interned,
+ );
+ }
+ Ok(())
+}
+
+// Fuzz harness wrapper to expose it to libfuzzer (and thus cargo-fuzz)
+// See: https://rust-fuzz.github.io/book/cargo-fuzz.html
+fuzz_target!(|data: FuzzData| -> Corpus {
+ if do_fuzz(data).is_ok() {
+ Corpus::Keep
+ } else {
+ Corpus::Reject
+ }
+});