#![no_main] mod common; use crate::common::FuzzData; use boa_ast::scope::Scope; use boa_interner::ToInternedString; use boa_parser::{Parser, Source}; use libfuzzer_sys::{fuzz_target, Corpus}; use std::{error::Error, 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(Source::from_reader(Cursor::new(&original), None)); let scope = Scope::new_global(); 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_script(&scope, &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(Source::from_reader(Cursor::new(&first_interned), None)); let second_scope = Scope::new_global(); // Now, we most assuredly should produce valid code. It has already gone through a first pass. let second = parser .parse_script(&second_scope, &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 } });