From e64a20e2a6eef10523f101e2e30bd42dce8e549d Mon Sep 17 00:00:00 2001 From: Addison Crump Date: Sun, 6 Nov 2022 17:59:33 +0000 Subject: [PATCH] Parser Idempotency Fuzzer (#2400) This Pull Request offers a fuzzer which is capable of detecting faults in the parser and interner. It does so by ensuring that the parsed AST remains the same between a parsed source and the result of parsing the `to_interned_string` result of the first parsed source. It changes the following: - Adds a fuzzer for the parser and interner. Any issues I raise in association with this fuzzer will link back to this fuzzer. You may run the fuzzer using the following commands: ```bash $ cd boa_engine $ cargo +nightly fuzz run -s none parser-idempotency ``` Co-authored-by: Addison Crump --- Cargo.lock | 23 + boa_ast/Cargo.toml | 3 + boa_ast/src/declaration/mod.rs | 1 + boa_ast/src/declaration/variable.rs | 5 + boa_ast/src/expression/access.rs | 5 + boa_ast/src/expression/await.rs | 1 + boa_ast/src/expression/call.rs | 2 + boa_ast/src/expression/identifier.rs | 1 + boa_ast/src/expression/literal/array.rs | 1 + boa_ast/src/expression/literal/mod.rs | 1 + boa_ast/src/expression/literal/object.rs | 1 + boa_ast/src/expression/literal/template.rs | 2 + boa_ast/src/expression/mod.rs | 1 + boa_ast/src/expression/new.rs | 1 + boa_ast/src/expression/operator/assign/mod.rs | 2 + boa_ast/src/expression/operator/assign/op.rs | 1 + boa_ast/src/expression/operator/binary/mod.rs | 1 + boa_ast/src/expression/operator/binary/op.rs | 5 + .../src/expression/operator/conditional.rs | 1 + boa_ast/src/expression/operator/unary/mod.rs | 1 + boa_ast/src/expression/operator/unary/op.rs | 1 + boa_ast/src/expression/optional.rs | 3 + boa_ast/src/expression/spread.rs | 1 + boa_ast/src/expression/tagged_template.rs | 1 + boa_ast/src/expression/yield.rs | 1 + boa_ast/src/function/arrow_function.rs | 1 + boa_ast/src/function/async_arrow_function.rs | 1 + boa_ast/src/function/async_function.rs | 1 + boa_ast/src/function/async_generator.rs | 1 + boa_ast/src/function/class.rs | 2 + boa_ast/src/function/generator.rs | 1 + boa_ast/src/function/mod.rs | 1 + boa_ast/src/function/parameters.rs | 9 + boa_ast/src/pattern.rs | 5 + boa_ast/src/property.rs | 3 + boa_ast/src/statement/block.rs | 1 + boa_ast/src/statement/if.rs | 1 + boa_ast/src/statement/iteration/break.rs | 1 + boa_ast/src/statement/iteration/continue.rs | 1 + .../src/statement/iteration/do_while_loop.rs | 1 + .../src/statement/iteration/for_in_loop.rs | 1 + boa_ast/src/statement/iteration/for_loop.rs | 3 + .../src/statement/iteration/for_of_loop.rs | 1 + boa_ast/src/statement/iteration/mod.rs | 1 + boa_ast/src/statement/iteration/while_loop.rs | 1 + boa_ast/src/statement/labelled.rs | 2 + boa_ast/src/statement/mod.rs | 1 + boa_ast/src/statement/return.rs | 1 + boa_ast/src/statement/switch.rs | 2 + boa_ast/src/statement/throw.rs | 1 + boa_ast/src/statement/try.rs | 4 + boa_ast/src/statement_list.rs | 11 + boa_engine/Cargo.toml | 2 + boa_interner/Cargo.toml | 5 +- boa_interner/src/sym.rs | 3 +- fuzz/.gitignore | 4 + fuzz/Cargo.lock | 831 ++++++++++++++++++ fuzz/Cargo.toml | 29 + fuzz/README.md | 37 + fuzz/fuzz_targets/common.rs | 74 ++ fuzz/fuzz_targets/parser-idempotency.rs | 74 ++ 61 files changed, 1182 insertions(+), 2 deletions(-) create mode 100644 fuzz/.gitignore create mode 100644 fuzz/Cargo.lock create mode 100644 fuzz/Cargo.toml create mode 100644 fuzz/README.md create mode 100644 fuzz/fuzz_targets/common.rs create mode 100644 fuzz/fuzz_targets/parser-idempotency.rs 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 + } +});