Remove recursion building types in component_api fuzzer (#4694)
* Remove recursion building types in `component_api` fuzzer Sure enough the fuzzers found an input that blows the stack, so the type-building here was rewritten to use a heap-based stack instead of a stack-based-stack. * Review comments
This commit is contained in:
@@ -554,112 +554,136 @@ pub fn rust_type(ty: &Type, name_counter: &mut u32, declarations: &mut TokenStre
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_component_name(name_counter: &mut u32) -> String {
|
#[derive(Default)]
|
||||||
let name = format!("$Foo{name_counter}");
|
struct TypesBuilder<'a> {
|
||||||
*name_counter += 1;
|
next: u32,
|
||||||
name
|
worklist: Vec<(u32, &'a Type)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_component_type(
|
impl<'a> TypesBuilder<'a> {
|
||||||
ty: &Type,
|
fn write_ref(&mut self, ty: &'a Type, dst: &mut String) {
|
||||||
f: &mut String,
|
match ty {
|
||||||
name_counter: &mut u32,
|
// Primitive types can be referenced directly
|
||||||
declarations: &mut String,
|
Type::Unit => dst.push_str("unit"),
|
||||||
) {
|
Type::Bool => dst.push_str("bool"),
|
||||||
match ty {
|
Type::S8 => dst.push_str("s8"),
|
||||||
Type::Unit => f.push_str("unit"),
|
Type::U8 => dst.push_str("u8"),
|
||||||
Type::Bool => f.push_str("bool"),
|
Type::S16 => dst.push_str("s16"),
|
||||||
Type::S8 => f.push_str("s8"),
|
Type::U16 => dst.push_str("u16"),
|
||||||
Type::U8 => f.push_str("u8"),
|
Type::S32 => dst.push_str("s32"),
|
||||||
Type::S16 => f.push_str("s16"),
|
Type::U32 => dst.push_str("u32"),
|
||||||
Type::U16 => f.push_str("u16"),
|
Type::S64 => dst.push_str("s64"),
|
||||||
Type::S32 => f.push_str("s32"),
|
Type::U64 => dst.push_str("u64"),
|
||||||
Type::U32 => f.push_str("u32"),
|
Type::Float32 => dst.push_str("float32"),
|
||||||
Type::S64 => f.push_str("s64"),
|
Type::Float64 => dst.push_str("float64"),
|
||||||
Type::U64 => f.push_str("u64"),
|
Type::Char => dst.push_str("char"),
|
||||||
Type::Float32 => f.push_str("float32"),
|
Type::String => dst.push_str("string"),
|
||||||
Type::Float64 => f.push_str("float64"),
|
|
||||||
Type::Char => f.push_str("char"),
|
// Otherwise emit a reference to the type and remember to generate
|
||||||
Type::String => f.push_str("string"),
|
// the corresponding type alias later.
|
||||||
Type::List(ty) => {
|
Type::List(_)
|
||||||
let mut case = String::new();
|
| Type::Record(_)
|
||||||
write_component_type(ty, &mut case, name_counter, declarations);
|
| Type::Tuple(_)
|
||||||
let name = make_component_name(name_counter);
|
| Type::Variant(_)
|
||||||
write!(declarations, "(type {name} (list {case}))").unwrap();
|
| Type::Enum(_)
|
||||||
f.push_str(&name);
|
| Type::Union(_)
|
||||||
}
|
| Type::Option(_)
|
||||||
Type::Record(types) => {
|
| Type::Expected { .. }
|
||||||
let mut fields = String::new();
|
| Type::Flags(_) => {
|
||||||
for (index, ty) in types.iter().enumerate() {
|
let idx = self.next;
|
||||||
write!(fields, r#" (field "f{index}" "#).unwrap();
|
self.next += 1;
|
||||||
write_component_type(ty, &mut fields, name_counter, declarations);
|
write!(dst, "$t{idx}").unwrap();
|
||||||
fields.push_str(")");
|
self.worklist.push((idx, ty));
|
||||||
}
|
}
|
||||||
let name = make_component_name(name_counter);
|
|
||||||
write!(declarations, "(type {name} (record{fields}))").unwrap();
|
|
||||||
f.push_str(&name);
|
|
||||||
}
|
}
|
||||||
Type::Tuple(types) => {
|
}
|
||||||
let mut fields = String::new();
|
|
||||||
for ty in types.0.iter() {
|
fn write_decl(&mut self, idx: u32, ty: &'a Type) -> String {
|
||||||
fields.push_str(" ");
|
let mut decl = format!("(type $t{idx} ");
|
||||||
write_component_type(ty, &mut fields, name_counter, declarations);
|
match ty {
|
||||||
|
Type::Unit
|
||||||
|
| Type::Bool
|
||||||
|
| Type::S8
|
||||||
|
| Type::U8
|
||||||
|
| Type::S16
|
||||||
|
| Type::U16
|
||||||
|
| Type::S32
|
||||||
|
| Type::U32
|
||||||
|
| Type::S64
|
||||||
|
| Type::U64
|
||||||
|
| Type::Float32
|
||||||
|
| Type::Float64
|
||||||
|
| Type::Char
|
||||||
|
| Type::String => unreachable!(),
|
||||||
|
|
||||||
|
Type::List(ty) => {
|
||||||
|
decl.push_str("(list ");
|
||||||
|
self.write_ref(ty, &mut decl);
|
||||||
|
decl.push_str(")");
|
||||||
}
|
}
|
||||||
let name = make_component_name(name_counter);
|
Type::Record(types) => {
|
||||||
write!(declarations, "(type {name} (tuple{fields}))").unwrap();
|
decl.push_str("(record");
|
||||||
f.push_str(&name);
|
for (index, ty) in types.iter().enumerate() {
|
||||||
}
|
write!(decl, r#" (field "f{index}" "#).unwrap();
|
||||||
Type::Variant(types) => {
|
self.write_ref(ty, &mut decl);
|
||||||
let mut cases = String::new();
|
decl.push_str(")");
|
||||||
for (index, ty) in types.0.iter().enumerate() {
|
}
|
||||||
write!(cases, r#" (case "C{index}" "#).unwrap();
|
decl.push_str(")");
|
||||||
write_component_type(ty, &mut cases, name_counter, declarations);
|
|
||||||
cases.push_str(")");
|
|
||||||
}
|
}
|
||||||
let name = make_component_name(name_counter);
|
Type::Tuple(types) => {
|
||||||
write!(declarations, "(type {name} (variant{cases}))").unwrap();
|
decl.push_str("(tuple");
|
||||||
f.push_str(&name);
|
for ty in types.iter() {
|
||||||
}
|
decl.push_str(" ");
|
||||||
Type::Enum(count) => {
|
self.write_ref(ty, &mut decl);
|
||||||
f.push_str("(enum");
|
}
|
||||||
for index in 0..count.0 {
|
decl.push_str(")");
|
||||||
write!(f, r#" "C{index}""#).unwrap();
|
|
||||||
}
|
}
|
||||||
f.push_str(")");
|
Type::Variant(types) => {
|
||||||
}
|
decl.push_str("(variant");
|
||||||
Type::Union(types) => {
|
for (index, ty) in types.iter().enumerate() {
|
||||||
let mut cases = String::new();
|
write!(decl, r#" (case "C{index}" "#).unwrap();
|
||||||
for ty in types.0.iter() {
|
self.write_ref(ty, &mut decl);
|
||||||
cases.push_str(" ");
|
decl.push_str(")");
|
||||||
write_component_type(ty, &mut cases, name_counter, declarations);
|
}
|
||||||
|
decl.push_str(")");
|
||||||
}
|
}
|
||||||
let name = make_component_name(name_counter);
|
Type::Enum(count) => {
|
||||||
write!(declarations, "(type {name} (union{cases}))").unwrap();
|
decl.push_str("(enum");
|
||||||
f.push_str(&name);
|
for index in 0..count.0 {
|
||||||
}
|
write!(decl, r#" "E{index}""#).unwrap();
|
||||||
Type::Option(ty) => {
|
}
|
||||||
let mut case = String::new();
|
decl.push_str(")");
|
||||||
write_component_type(ty, &mut case, name_counter, declarations);
|
}
|
||||||
let name = make_component_name(name_counter);
|
Type::Union(types) => {
|
||||||
write!(declarations, "(type {name} (option {case}))").unwrap();
|
decl.push_str("(union");
|
||||||
f.push_str(&name);
|
for ty in types.iter() {
|
||||||
}
|
decl.push_str(" ");
|
||||||
Type::Expected { ok, err } => {
|
self.write_ref(ty, &mut decl);
|
||||||
let mut cases = String::new();
|
}
|
||||||
write_component_type(ok, &mut cases, name_counter, declarations);
|
decl.push_str(")");
|
||||||
cases.push_str(" ");
|
}
|
||||||
write_component_type(err, &mut cases, name_counter, declarations);
|
Type::Option(ty) => {
|
||||||
let name = make_component_name(name_counter);
|
decl.push_str("(option ");
|
||||||
write!(declarations, "(type {name} (expected {cases}))").unwrap();
|
self.write_ref(ty, &mut decl);
|
||||||
f.push_str(&name);
|
decl.push_str(")");
|
||||||
}
|
}
|
||||||
Type::Flags(count) => {
|
Type::Expected { ok, err } => {
|
||||||
f.push_str("(flags");
|
decl.push_str("(expected ");
|
||||||
for index in 0..count.0 {
|
self.write_ref(ok, &mut decl);
|
||||||
write!(f, r#" "F{index}""#).unwrap();
|
decl.push_str(" ");
|
||||||
|
self.write_ref(err, &mut decl);
|
||||||
|
decl.push_str(")");
|
||||||
|
}
|
||||||
|
Type::Flags(count) => {
|
||||||
|
decl.push_str("(flags");
|
||||||
|
for index in 0..count.0 {
|
||||||
|
write!(decl, r#" "F{index}""#).unwrap();
|
||||||
|
}
|
||||||
|
decl.push_str(")");
|
||||||
}
|
}
|
||||||
f.push_str(")");
|
|
||||||
}
|
}
|
||||||
|
decl.push_str(")");
|
||||||
|
decl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,34 +798,39 @@ pub struct TestCase {
|
|||||||
impl TestCase {
|
impl TestCase {
|
||||||
/// Generate a `Declarations` for this `TestCase` which may be used to build a component to execute the case.
|
/// Generate a `Declarations` for this `TestCase` which may be used to build a component to execute the case.
|
||||||
pub fn declarations(&self) -> Declarations {
|
pub fn declarations(&self) -> Declarations {
|
||||||
let mut types = String::new();
|
let mut builder = TypesBuilder::default();
|
||||||
let name_counter = &mut 0;
|
|
||||||
|
|
||||||
let params = self
|
let mut params = String::new();
|
||||||
.params
|
for ty in self.params.iter() {
|
||||||
.iter()
|
params.push_str(" (param ");
|
||||||
.map(|ty| {
|
builder.write_ref(ty, &mut params);
|
||||||
let mut tmp = String::new();
|
params.push_str(")");
|
||||||
write_component_type(ty, &mut tmp, name_counter, &mut types);
|
|
||||||
format!("(param {tmp})")
|
|
||||||
})
|
|
||||||
.collect::<Box<[_]>>()
|
|
||||||
.join(" ")
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let result = {
|
|
||||||
let mut tmp = String::new();
|
|
||||||
write_component_type(&self.result, &mut tmp, name_counter, &mut types);
|
|
||||||
format!("(result {tmp})")
|
|
||||||
}
|
}
|
||||||
.into();
|
|
||||||
|
let mut result = String::from("(result ");
|
||||||
|
builder.write_ref(&self.result, &mut result);
|
||||||
|
result.push_str(")");
|
||||||
|
|
||||||
let import_and_export = make_import_and_export(&self.params, &self.result);
|
let import_and_export = make_import_and_export(&self.params, &self.result);
|
||||||
|
|
||||||
|
let mut type_decls = Vec::new();
|
||||||
|
while let Some((idx, ty)) = builder.worklist.pop() {
|
||||||
|
type_decls.push(builder.write_decl(idx, ty));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that types are printed here in reverse order since they were
|
||||||
|
// pushed onto `type_decls` as they were referenced meaning the last one
|
||||||
|
// is the "base" one.
|
||||||
|
let mut types = String::new();
|
||||||
|
for decl in type_decls.into_iter().rev() {
|
||||||
|
types.push_str(&decl);
|
||||||
|
types.push_str("\n");
|
||||||
|
}
|
||||||
|
|
||||||
Declarations {
|
Declarations {
|
||||||
types: types.into(),
|
types: types.into(),
|
||||||
params,
|
params: params.into(),
|
||||||
result,
|
result: result.into(),
|
||||||
import_and_export: import_and_export.into(),
|
import_and_export: import_and_export.into(),
|
||||||
encoding1: self.encoding1,
|
encoding1: self.encoding1,
|
||||||
encoding2: self.encoding2,
|
encoding2: self.encoding2,
|
||||||
|
|||||||
Reference in New Issue
Block a user