mirror of https://github.com/boa-dev/boa.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.9 KiB
96 lines
2.9 KiB
use boa_ast::{ |
|
visitor::{VisitWith, VisitorMut}, |
|
Expression, StatementList, |
|
}; |
|
use boa_interner::{Interner, Sym, ToInternedString}; |
|
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<Self> { |
|
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<Self::BreakTy> { |
|
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<Self::BreakTy> { |
|
*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() |
|
} |
|
} |
|
|
|
pub struct FuzzSource { |
|
pub interner: Interner, |
|
pub source: String, |
|
} |
|
|
|
impl<'a> Arbitrary<'a> for FuzzSource { |
|
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> { |
|
let data = FuzzData::arbitrary(u)?; |
|
let source = data.ast.to_interned_string(&data.interner); |
|
Ok(Self { |
|
interner: data.interner, |
|
source, |
|
}) |
|
} |
|
} |
|
|
|
impl Debug for FuzzSource { |
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
|
f.write_fmt(format_args!("Fuzzed source:\n{}", self.source)) |
|
} |
|
}
|
|
|