/src/wasm-tools/crates/wit-smith/src/generate.rs
Line | Count | Source |
1 | | use crate::config::Config; |
2 | | use arbitrary::{Arbitrary, Result, Unstructured}; |
3 | | use indexmap::{IndexMap, IndexSet}; |
4 | | use semver::Version; |
5 | | use std::collections::HashSet; |
6 | | use std::collections::hash_map::{Entry, HashMap}; |
7 | | use std::collections::hash_set::Intersection; |
8 | | use std::fmt::Write; |
9 | | use std::hash::RandomState; |
10 | | use std::mem; |
11 | | use std::rc::Rc; |
12 | | use std::str; |
13 | | use wit_parser::*; |
14 | | |
15 | | pub struct Generator { |
16 | | config: Config, |
17 | | packages: Packages, |
18 | | next_interface_id: u32, |
19 | | } |
20 | | |
21 | | #[derive(PartialEq, Eq, Hash)] |
22 | | pub struct PackageWorldKey { |
23 | | package_name: String, |
24 | | world_name: String, |
25 | | } |
26 | | |
27 | | struct InterfaceGenerator<'a> { |
28 | | generator: &'a mut Generator, |
29 | | file: &'a mut File, |
30 | | unique_names: HashSet<String>, |
31 | | types_in_interface: Vec<Type>, |
32 | | package_name: &'a str, |
33 | | version: Option<Version>, |
34 | | } |
35 | | |
36 | | #[derive(Clone)] |
37 | | struct Type { |
38 | | name: String, |
39 | | size: usize, |
40 | | is_resource: bool, |
41 | | } |
42 | | |
43 | | #[derive(Default)] |
44 | | struct Packages { |
45 | | list: Vec<Package>, |
46 | | packages_with_interfaces: Vec<usize>, |
47 | | packages_with_worlds: Vec<usize>, |
48 | | package_unique_names: IndexMap<PackageWorldKey, HashSet<String>>, |
49 | | } |
50 | | |
51 | | impl Packages { |
52 | 15.6k | fn add_name(&mut self, package_name: String, world_name: String, name: String) { |
53 | 15.6k | let key = PackageWorldKey { |
54 | 15.6k | package_name, |
55 | 15.6k | world_name, |
56 | 15.6k | }; |
57 | 15.6k | let world_names = self |
58 | 15.6k | .package_unique_names |
59 | 15.6k | .entry(key) |
60 | 15.6k | .or_insert_with(HashSet::new); |
61 | 15.6k | world_names.insert(name); |
62 | 15.6k | } |
63 | | |
64 | 0 | fn contains_name(&self, package_name: String, world_name: String, name: &str) -> bool { |
65 | 0 | let key = PackageWorldKey { |
66 | 0 | package_name, |
67 | 0 | world_name, |
68 | 0 | }; |
69 | 0 | if let Some(world_names) = self.package_unique_names.get(&key) { |
70 | 0 | return world_names.contains(name); |
71 | 0 | } |
72 | 0 | false |
73 | 0 | } |
74 | | |
75 | 0 | fn intersect( |
76 | 0 | &self, |
77 | 0 | current_world: PackageWorldKey, |
78 | 0 | include_world: PackageWorldKey, |
79 | 0 | ) -> Option<Intersection<'_, String, RandomState>> { |
80 | 0 | let current_world_names = self.package_unique_names.get(¤t_world); |
81 | 0 | let include_world_names = self.package_unique_names.get(&include_world); |
82 | | |
83 | 0 | if let (Some(current_world_names), Some(include_world_names)) = |
84 | 0 | (current_world_names, include_world_names) |
85 | | { |
86 | 0 | let intersection = current_world_names.intersection(include_world_names); |
87 | 0 | if intersection.clone().count() > 0 { |
88 | 0 | return Some(intersection); |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | 0 | return None; |
93 | 0 | } |
94 | | } |
95 | | |
96 | | pub struct Package { |
97 | | pub name: PackageName, |
98 | | pub sources: SourceMap, |
99 | | file: File, |
100 | | } |
101 | | |
102 | | #[derive(Clone, Debug)] |
103 | | pub struct PackageName { |
104 | | pub namespace: String, |
105 | | pub name: String, |
106 | | pub version: Option<Version>, |
107 | | } |
108 | | |
109 | | impl Generator { |
110 | 4.49k | pub fn new(config: Config) -> Generator { |
111 | 4.49k | Generator { |
112 | 4.49k | config, |
113 | 4.49k | packages: Default::default(), |
114 | 4.49k | next_interface_id: 0, |
115 | 4.49k | } |
116 | 4.49k | } |
117 | | |
118 | 4.49k | pub fn generate(&mut self, u: &mut Unstructured<'_>) -> Result<Vec<Package>> { |
119 | 4.49k | let mut names = HashSet::new(); |
120 | 11.8k | while self.packages.list.len() < self.config.max_packages |
121 | 11.1k | && (self.packages.list.is_empty() || u.arbitrary()?) |
122 | | { |
123 | 7.33k | let pkg = self.gen_package(u, &mut names)?; |
124 | 7.33k | let i = self.packages.list.len(); |
125 | 7.33k | if pkg.file.interfaces.len() > 0 { |
126 | 2.98k | self.packages.packages_with_interfaces.push(i); |
127 | 4.34k | } |
128 | 7.33k | if pkg.file.worlds.len() > 0 { |
129 | 5.22k | self.packages.packages_with_worlds.push(i); |
130 | 5.22k | } |
131 | 7.33k | self.packages.list.push(pkg); |
132 | | } |
133 | 4.49k | Ok(mem::take(&mut self.packages.list)) |
134 | 4.49k | } |
135 | | |
136 | 7.33k | fn gen_package( |
137 | 7.33k | &mut self, |
138 | 7.33k | u: &mut Unstructured<'_>, |
139 | 7.33k | names: &mut HashSet<String>, |
140 | 7.33k | ) -> Result<Package> { |
141 | 7.33k | let namespace = gen_unique_name(u, names)?; |
142 | 7.33k | let package_name = gen_unique_name(u, names)?; |
143 | | |
144 | 7.33k | let version = if u.arbitrary()? { |
145 | 5.22k | Some(gen_version(u)?) |
146 | | } else { |
147 | 2.11k | None |
148 | | }; |
149 | 7.33k | let mut ret = Package { |
150 | 7.33k | name: PackageName { |
151 | 7.33k | namespace, |
152 | 7.33k | name: package_name.clone(), |
153 | 7.33k | version: version.clone(), |
154 | 7.33k | }, |
155 | 7.33k | file: File::default(), |
156 | 7.33k | sources: SourceMap::new(), |
157 | 7.33k | }; |
158 | | |
159 | 0 | #[derive(Arbitrary, Clone)] Unexecuted instantiation: <<wit_smith::generate::Generator>::gen_package::Generate as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::Generator>::gen_package::Generate as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::Generator>::gen_package::Generate as arbitrary::Arbitrary>::arbitrary::{closure#1} |
160 | | enum Generate { |
161 | | Interface, |
162 | | Use, |
163 | | World, |
164 | | Done, |
165 | | } |
166 | | |
167 | 7.33k | let mut items = 0; |
168 | 7.33k | let mut empty = true; |
169 | 7.33k | let mut files = vec![File::default()]; |
170 | 7.33k | let mut package_names = HashSet::new(); |
171 | 7.33k | log::debug!("===================== new package ===================="); |
172 | 47.8k | while items < self.config.max_pkg_items { |
173 | 41.6k | items += 1; |
174 | 41.6k | let max = if files.len() < self.config.max_files_per_package { |
175 | 29.6k | files.len() + 1 |
176 | | } else { |
177 | 12.0k | files.len() |
178 | | }; |
179 | 41.6k | let i = u.int_in_range(0..=max)?; |
180 | 41.6k | let file = match files.get_mut(i) { |
181 | 32.3k | Some(file) => file, |
182 | | None => { |
183 | 9.33k | files.push(ret.file.clone()); |
184 | 9.33k | files.last_mut().unwrap() |
185 | | } |
186 | | }; |
187 | | |
188 | | // Only generate Use/Done if we've already generated a world or interface. This ensures |
189 | | // that we never generate empty packages, which aren't representable. |
190 | 41.6k | let generate = if empty { |
191 | 11.7k | u.choose(&[Generate::World, Generate::Interface])?.clone() |
192 | | } else { |
193 | 29.9k | u.arbitrary()? |
194 | | }; |
195 | | |
196 | 41.6k | match generate { |
197 | | Generate::World => { |
198 | 9.40k | let world_name = |
199 | 9.40k | file.gen_unique_package_name(u, &mut package_names, DefinitionKind::World)?; |
200 | 9.40k | log::debug!("new world `{world_name}` in {i}"); |
201 | 9.40k | let world = |
202 | 9.40k | self.gen_world(u, &world_name, file, &package_name, version.clone())?; |
203 | 9.40k | file.items.push(world); |
204 | | |
205 | | // Insert the world at the package and file level, asserting |
206 | | // uniqueness. |
207 | 9.40k | assert!(ret.file.worlds.insert(world_name.clone())); |
208 | 9.40k | assert!(file.worlds.insert(world_name.clone())); |
209 | 9.40k | let prev = ret.file.namespace.insert( |
210 | 9.40k | world_name.clone(), |
211 | 9.40k | (DefinitionLevel::Package, DefinitionKind::World), |
212 | | ); |
213 | 9.40k | assert!(prev.is_none()); |
214 | | |
215 | | // Insert the definition into all other files as well. |
216 | 19.2k | for file in files.iter_mut() { |
217 | 19.2k | if file.insert_definition(&world_name, DefinitionKind::World) { |
218 | 9.78k | assert!(file.worlds.insert(world_name.clone())); |
219 | 9.42k | } |
220 | | } |
221 | | |
222 | 9.40k | empty = false; |
223 | | } |
224 | | Generate::Interface => { |
225 | 27.4k | let name = file.gen_unique_package_name( |
226 | 27.4k | u, |
227 | 27.4k | &mut package_names, |
228 | 27.4k | DefinitionKind::Interface, |
229 | 0 | )?; |
230 | 27.4k | log::debug!("new interface `{name}` in {i}"); |
231 | 27.4k | let id = self.next_interface_id; |
232 | 27.4k | self.next_interface_id += 1; |
233 | 27.4k | let (src, types) = |
234 | 27.4k | self.gen_interface(u, Some(&name), file, &package_name, None, None)?; |
235 | 27.4k | file.items.push(src); |
236 | 27.4k | if types.is_empty() { |
237 | 22.0k | continue; |
238 | 5.39k | } |
239 | 5.39k | let interface = FileInterface { |
240 | 5.39k | name, |
241 | 5.39k | id, |
242 | 5.39k | types: Rc::new(types), |
243 | 5.39k | }; |
244 | | |
245 | | // This interface is defined at the package level, and it |
246 | | // must be unique. |
247 | 5.39k | let prev = ret |
248 | 5.39k | .file |
249 | 5.39k | .interfaces |
250 | 5.39k | .insert(interface.name.clone(), interface.clone()); |
251 | 5.39k | assert!(prev.is_none()); |
252 | 5.39k | let prev = ret.file.namespace.insert( |
253 | 5.39k | interface.name.clone(), |
254 | 5.39k | (DefinitionLevel::Package, DefinitionKind::Interface), |
255 | | ); |
256 | 5.39k | assert!(prev.is_none()); |
257 | | |
258 | | // This is also defined at the file level, and it must be |
259 | | // unique here too. |
260 | 5.39k | let prev = file |
261 | 5.39k | .interfaces |
262 | 5.39k | .insert(interface.name.clone(), interface.clone()); |
263 | 5.39k | assert!(prev.is_none()); |
264 | | |
265 | | // Insert the definition into all other files as well. |
266 | 13.0k | for file in files.iter_mut() { |
267 | 13.0k | if file.insert_definition(&interface.name, DefinitionKind::Interface) { |
268 | 7.65k | let prev = file |
269 | 7.65k | .interfaces |
270 | 7.65k | .insert(interface.name.clone(), interface.clone()); |
271 | 7.65k | assert!(prev.is_none()); |
272 | 5.41k | } |
273 | | } |
274 | | |
275 | 5.39k | empty = false; |
276 | | } |
277 | | Generate::Use => { |
278 | 3.59k | let mut piece = String::new(); |
279 | 3.59k | piece.push_str("use "); |
280 | 2.64k | let (name, id, types) = |
281 | 3.59k | match self.gen_interface_path(u, &mut ret.file, &mut piece)? { |
282 | 2.64k | Some(i) => i, |
283 | 948 | None => continue, |
284 | | }; |
285 | 2.64k | let name = name.to_string(); |
286 | 2.64k | let types = types.clone(); |
287 | | // If this interface's name already exist within this `file` |
288 | | // then this must be renamed with `as`. If the name exists |
289 | | // only at the package level then it's ok to replace it with |
290 | | // something else. |
291 | | // |
292 | | // If the name doesn't exist then use the fuzz input to |
293 | | // determine whether a rename should happen. |
294 | 2.64k | let name = |
295 | 2.64k | if matches!(file.namespace.get(&name), Some((DefinitionLevel::File, _))) |
296 | 1.27k | || u.arbitrary()? |
297 | | { |
298 | 2.27k | let name = file.gen_unique_file_name(u, DefinitionKind::Interface)?; |
299 | 2.27k | piece.push_str(" as %"); |
300 | 2.27k | piece.push_str(&name); |
301 | 2.27k | name |
302 | | } else { |
303 | 374 | file.namespace.insert( |
304 | 374 | name.clone(), |
305 | 374 | (DefinitionLevel::File, DefinitionKind::Interface), |
306 | | ); |
307 | 374 | name |
308 | | }; |
309 | 2.64k | piece.push_str(";"); |
310 | 2.64k | log::debug!("new use `{name}` in {i}"); |
311 | 2.64k | file.worlds.swap_remove(&name); |
312 | 2.64k | file.interfaces |
313 | 2.64k | .insert(name.clone(), FileInterface { name, id, types }); |
314 | 2.64k | file.items.push(piece) |
315 | | } |
316 | 1.22k | Generate::Done => break, |
317 | | }; |
318 | | } |
319 | | |
320 | 7.33k | shuffle(u, &mut files)?; |
321 | 16.6k | for file in files.iter_mut() { |
322 | 16.6k | shuffle(u, &mut file.items)?; |
323 | | } |
324 | | |
325 | 7.33k | let mut has_name = false; |
326 | 7.33k | let len = files.len(); |
327 | 16.6k | for (i, file) in files.iter_mut().enumerate() { |
328 | 16.6k | let mut s = String::new(); |
329 | 16.6k | if u.arbitrary()? || (!has_name && i == len - 1) { |
330 | 10.8k | has_name = true; |
331 | 10.8k | s.push_str("package "); |
332 | 10.8k | s.push_str("%"); |
333 | 10.8k | s.push_str(&ret.name.namespace); |
334 | 10.8k | s.push_str(":"); |
335 | 10.8k | s.push_str("%"); |
336 | 10.8k | s.push_str(&ret.name.name); |
337 | 10.8k | if let Some(version) = &ret.name.version { |
338 | 8.02k | s.push_str(&format!("@{version}")); |
339 | 8.02k | } |
340 | 10.8k | s.push_str(";\n\n"); |
341 | 5.79k | } |
342 | 39.5k | for piece in file.items.iter() { |
343 | 39.5k | s.push_str(&piece); |
344 | 39.5k | s.push_str("\n\n"); |
345 | 39.5k | } |
346 | 16.6k | log::trace!("==============================================="); |
347 | 16.6k | log::trace!("{s}"); |
348 | 16.6k | ret.sources.push(format!("wit{i}.wit").as_ref(), &s); |
349 | | } |
350 | 7.33k | Ok(ret) |
351 | 7.33k | } |
352 | | |
353 | 9.40k | fn gen_world( |
354 | 9.40k | &mut self, |
355 | 9.40k | u: &mut Unstructured<'_>, |
356 | 9.40k | name: &str, |
357 | 9.40k | file: &mut File, |
358 | 9.40k | package_name: &str, |
359 | 9.40k | version: Option<Version>, |
360 | 9.40k | ) -> Result<String> { |
361 | 9.40k | InterfaceGenerator::new(self, file, package_name, version).gen_world(u, name) |
362 | 9.40k | } |
363 | | |
364 | 27.4k | fn gen_interface( |
365 | 27.4k | &mut self, |
366 | 27.4k | u: &mut Unstructured<'_>, |
367 | 27.4k | name: Option<&str>, |
368 | 27.4k | file: &mut File, |
369 | 27.4k | package_name: &str, |
370 | 27.4k | world_name: Option<&str>, |
371 | 27.4k | version: Option<Version>, |
372 | 27.4k | ) -> Result<(String, Vec<Type>)> { |
373 | 27.4k | let mut generator = InterfaceGenerator::new(self, file, package_name, version); |
374 | 27.4k | let ret = generator.gen_interface(u, name, world_name)?; |
375 | 27.4k | Ok((ret, generator.types_in_interface)) |
376 | 27.4k | } |
377 | | |
378 | 88.0k | fn gen_interface_path<'a>( |
379 | 88.0k | &'a self, |
380 | 88.0k | u: &mut Unstructured<'_>, |
381 | 88.0k | file: &'a mut File, |
382 | 88.0k | dst: &mut String, |
383 | 88.0k | ) -> Result<Option<(&'a str, u32, &'a Rc<Vec<Type>>)>> { |
384 | | enum Choice { |
385 | | Interfaces, |
386 | | Packages, |
387 | | } |
388 | 88.0k | let mut choices = Vec::new(); |
389 | 88.0k | if !file.interfaces.is_empty() { |
390 | 11.0k | choices.push(Choice::Interfaces); |
391 | 76.9k | } |
392 | 88.0k | if !self.packages.packages_with_interfaces.is_empty() { |
393 | 9.80k | choices.push(Choice::Packages); |
394 | 78.2k | } |
395 | | |
396 | 88.0k | if choices.is_empty() { |
397 | 74.3k | return Ok(None); |
398 | 13.7k | } |
399 | 13.7k | Ok(match u.choose(&choices)? { |
400 | | Choice::Interfaces => { |
401 | 4.56k | let i = u.int_in_range(0..=file.interfaces.len() - 1)?; |
402 | 4.56k | let (name, i) = file.interfaces.get_index(i).unwrap(); |
403 | | // Once a name is used from a file's local namespace then it |
404 | | // can't be overridden in that namespace so switch it to a file |
405 | | // definition from whatever it previously was. |
406 | 4.56k | file.namespace.insert( |
407 | 4.56k | name.clone(), |
408 | 4.56k | (DefinitionLevel::File, DefinitionKind::Interface), |
409 | | ); |
410 | 4.56k | file.worlds.swap_remove(name); |
411 | 4.56k | dst.push_str("%"); |
412 | 4.56k | dst.push_str(&i.name); |
413 | 4.56k | Some((&i.name, i.id, &i.types)) |
414 | | } |
415 | | Choice::Packages => { |
416 | 9.14k | let pkg = u.choose(&self.packages.packages_with_interfaces)?; |
417 | 9.14k | let pkg = &self.packages.list[*pkg]; |
418 | 9.14k | dst.push_str("%"); |
419 | 9.14k | dst.push_str(&pkg.name.namespace); |
420 | 9.14k | dst.push_str(":"); |
421 | 9.14k | dst.push_str("%"); |
422 | 9.14k | dst.push_str(&pkg.name.name); |
423 | 9.14k | dst.push_str("/"); |
424 | 9.14k | let i = u.int_in_range(0..=pkg.file.interfaces.len() - 1)?; |
425 | 9.14k | let i = &pkg.file.interfaces[i]; |
426 | 9.14k | dst.push_str("%"); |
427 | 9.14k | dst.push_str(&i.name); |
428 | 9.14k | if let Some(version) = &pkg.name.version { |
429 | 7.98k | dst.push_str(&format!("@{version}")); |
430 | 7.98k | } |
431 | 9.14k | Some((&i.name, i.id, &i.types)) |
432 | | } |
433 | | }) |
434 | 88.0k | } |
435 | | |
436 | 0 | fn gen_world_path<'a>( |
437 | 0 | &'a self, |
438 | 0 | u: &mut Unstructured<'_>, |
439 | 0 | file: &'a mut File, |
440 | 0 | dst: &mut String, |
441 | 0 | includes: &mut HashSet<String>, |
442 | 0 | ) -> Result<WorldPath<'a>> { |
443 | | enum Choice { |
444 | | Worlds, |
445 | | Packages, |
446 | | } |
447 | 0 | let mut choices = Vec::new(); |
448 | 0 | if !file.worlds.is_empty() { |
449 | 0 | choices.push(Choice::Worlds); |
450 | 0 | } |
451 | 0 | if !self.packages.packages_with_worlds.is_empty() { |
452 | 0 | choices.push(Choice::Packages); |
453 | 0 | } |
454 | | |
455 | 0 | if choices.is_empty() { |
456 | 0 | return Ok(WorldPath::None); |
457 | 0 | } |
458 | 0 | Ok(match u.choose(&choices)? { |
459 | | Choice::Worlds => { |
460 | 0 | let i = u.int_in_range(0..=file.worlds.len() - 1)?; |
461 | 0 | let name = &file.worlds[i]; |
462 | | |
463 | 0 | if !includes.insert(name.to_string()) { |
464 | 0 | return Ok(WorldPath::None); |
465 | 0 | } |
466 | | |
467 | 0 | dst.push_str("%"); |
468 | 0 | dst.push_str(&name); |
469 | | // Same as `gen_interface_path`, once a name is used as a world |
470 | | // it's forced to always be a world so update its definition to |
471 | | // be a file-level world. |
472 | | |
473 | 0 | file.namespace |
474 | 0 | .insert(name.clone(), (DefinitionLevel::File, DefinitionKind::World)); |
475 | 0 | WorldPath::Local(name) |
476 | | } |
477 | | Choice::Packages => { |
478 | 0 | let pkg = u.choose(&self.packages.packages_with_worlds)?; |
479 | 0 | let pkg = &self.packages.list[*pkg]; |
480 | 0 | dst.push_str("%"); |
481 | 0 | dst.push_str(&pkg.name.namespace); |
482 | 0 | dst.push_str(":"); |
483 | 0 | dst.push_str("%"); |
484 | 0 | dst.push_str(&pkg.name.name); |
485 | 0 | dst.push_str("/"); |
486 | 0 | let i = u.int_in_range(0..=pkg.file.worlds.len() - 1)?; |
487 | 0 | let w = &pkg.file.worlds[i]; |
488 | 0 | dst.push_str("%"); |
489 | 0 | dst.push_str(&w); |
490 | 0 | if let Some(version) = &pkg.name.version { |
491 | 0 | dst.push_str(&format!("@{version}")); |
492 | 0 | } |
493 | 0 | WorldPath::Remote |
494 | | } |
495 | | }) |
496 | 0 | } |
497 | | } |
498 | | |
499 | | impl<'a> InterfaceGenerator<'a> { |
500 | 38.4k | fn new( |
501 | 38.4k | generator: &'a mut Generator, |
502 | 38.4k | file: &'a mut File, |
503 | 38.4k | package_name: &'a str, |
504 | 38.4k | version: Option<Version>, |
505 | 38.4k | ) -> InterfaceGenerator<'a> { |
506 | 38.4k | InterfaceGenerator { |
507 | 38.4k | generator, |
508 | 38.4k | file, |
509 | 38.4k | types_in_interface: Vec::new(), |
510 | 38.4k | // Claim the name `memory` to avoid conflicting with the canonical |
511 | 38.4k | // ABI always using a linear memory named `memory`. |
512 | 38.4k | unique_names: HashSet::from_iter(["memory".to_string()]), |
513 | 38.4k | package_name: package_name, |
514 | 38.4k | version, |
515 | 38.4k | } |
516 | 38.4k | } |
517 | | |
518 | | // Generate a feature gate annotation (@since, @unstable, or @deprecated) |
519 | | // If version is provided, ensures the annotation is compatible with the version |
520 | 153k | fn gen_feature_annotation(&self, u: &mut Unstructured<'_>) -> Result<Option<String>> { |
521 | 153k | if u.arbitrary()? { |
522 | 125k | return Ok(None); |
523 | 28.4k | } |
524 | | |
525 | 28.4k | let feature_names = ["active", "inactive"]; |
526 | 0 | #[derive(Arbitrary)] Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_feature_annotation::AnnotationType as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_feature_annotation::AnnotationType as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_feature_annotation::AnnotationType as arbitrary::Arbitrary>::arbitrary::{closure#1} |
527 | | enum AnnotationType { |
528 | | Since, |
529 | | Unstable, |
530 | | Deprecated, |
531 | | } |
532 | | |
533 | 28.4k | match self.version { |
534 | | None => { |
535 | | // No package version available |
536 | 25.2k | return Ok(None); |
537 | | } |
538 | 3.21k | Some(_) => match u.arbitrary()? { |
539 | | AnnotationType::Since => { |
540 | 1.66k | let v = gen_version_less_than(u, &self.version)?; |
541 | 1.66k | Ok(Some(format!("@since(version = {v})"))) |
542 | | } |
543 | | AnnotationType::Unstable => { |
544 | 756 | let feature = u.choose(&feature_names)?; |
545 | 756 | Ok(Some(format!("@unstable(feature = {feature})"))) |
546 | | } |
547 | | AnnotationType::Deprecated => { |
548 | 788 | let depreciation_version = gen_version_less_than(u, &self.version)?; |
549 | 788 | let since_version = |
550 | 788 | gen_version_less_than(u, &Some(depreciation_version.clone()))?; |
551 | 788 | Ok(Some(format!( |
552 | 788 | "@deprecated(version = {depreciation_version})\n@since(version = {since_version})", |
553 | 788 | ))) |
554 | | } |
555 | | }, |
556 | | } |
557 | 153k | } |
558 | | |
559 | 29.0k | fn gen_interface( |
560 | 29.0k | &mut self, |
561 | 29.0k | u: &mut Unstructured<'_>, |
562 | 29.0k | name: Option<&str>, |
563 | 29.0k | world_name: Option<&str>, |
564 | 29.0k | ) -> Result<String> { |
565 | 29.0k | let mut ret = String::new(); |
566 | | |
567 | 29.0k | if let Some(annotation) = self.gen_feature_annotation(u)? { |
568 | 0 | ret.push_str(&annotation); |
569 | 0 | ret.push_str("\n"); |
570 | 29.0k | } |
571 | | |
572 | 29.0k | ret.push_str("interface "); |
573 | 29.0k | if let Some(name) = name { |
574 | 27.4k | ret.push_str("%"); |
575 | 27.4k | ret.push_str(name); |
576 | 27.4k | ret.push_str(" "); |
577 | 27.4k | } |
578 | 29.0k | ret.push_str("{\n"); |
579 | | |
580 | 0 | #[derive(Arbitrary)] Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_interface::Generate as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_interface::Generate as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_interface::Generate as arbitrary::Arbitrary>::arbitrary::{closure#1} |
581 | | enum Generate { |
582 | | Use, |
583 | | Type, |
584 | | Function, |
585 | | } |
586 | | |
587 | 29.0k | let mut parts = Vec::new(); |
588 | 129k | while parts.len() < self.generator.config.max_interface_items && u.arbitrary()? { |
589 | 100k | let mut part = String::new(); |
590 | 100k | if let Some(annotation) = self.gen_feature_annotation(u)? { |
591 | 0 | part.push_str(&annotation); |
592 | 0 | part.push_str("\n"); |
593 | 100k | } |
594 | | |
595 | 100k | match u.arbitrary()? { |
596 | | Generate::Use => { |
597 | 80.2k | if !self.gen_use(u, &mut part, world_name)? { |
598 | 70.5k | continue; |
599 | 9.71k | } |
600 | | } |
601 | | Generate::Type => { |
602 | 12.2k | let name = self.gen_unique_name(u)?; |
603 | 12.2k | let ty = self.gen_typedef(u, &name, &mut part)?; |
604 | 12.2k | let is_resource = ty.is_resource; |
605 | 12.2k | self.types_in_interface.push(ty); |
606 | 12.2k | if is_resource { |
607 | 937 | if u.arbitrary()? { |
608 | 586 | part.push_str(" {\n"); |
609 | 586 | self.gen_resource_funcs(&name, u, &mut part)?; |
610 | 586 | part.push_str("}"); |
611 | 351 | } else { |
612 | 351 | part.push_str(";"); |
613 | 351 | } |
614 | 11.2k | } |
615 | | } |
616 | | Generate::Function => { |
617 | 7.76k | self.gen_func(u, &mut part)?; |
618 | | } |
619 | | } |
620 | 29.6k | parts.push(part); |
621 | | } |
622 | | |
623 | 29.0k | shuffle(u, &mut parts)?; |
624 | 58.7k | for part in parts { |
625 | 29.6k | ret.push_str(&part); |
626 | 29.6k | ret.push_str("\n\n"); |
627 | 29.6k | } |
628 | | |
629 | 29.0k | ret.push_str("}"); |
630 | 29.0k | Ok(ret) |
631 | 29.0k | } |
632 | | |
633 | 9.40k | fn gen_world(&mut self, u: &mut Unstructured<'_>, world_name: &str) -> Result<String> { |
634 | 9.40k | let mut ret = String::new(); |
635 | 9.40k | ret.push_str("world %"); |
636 | 9.40k | ret.push_str(world_name); |
637 | 9.40k | ret.push_str(" {\n"); |
638 | | |
639 | 0 | #[derive(Arbitrary, Copy, Clone, Debug)] Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_world::Direction as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_world::Direction as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_world::Direction as arbitrary::Arbitrary>::arbitrary::{closure#1} |
640 | | enum Direction { |
641 | | Import, |
642 | | Export, |
643 | | } |
644 | | |
645 | 0 | #[derive(Arbitrary)] Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_world::ItemKind as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_world::ItemKind as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_world::ItemKind as arbitrary::Arbitrary>::arbitrary::{closure#1} |
646 | | enum ItemKind { |
647 | | Func(Direction), |
648 | | Interface(Direction), |
649 | | AnonInterface(Direction), |
650 | | Type, |
651 | | Use, |
652 | | Include, |
653 | | } |
654 | | |
655 | 9.40k | let mut parts = Vec::new(); |
656 | 9.40k | let mut imported_interfaces = HashSet::new(); |
657 | 9.40k | let mut exported_interfaces = HashSet::new(); |
658 | 9.40k | let mut includes: HashSet<String> = HashSet::new(); |
659 | | |
660 | | // Claim the name `memory` to avoid conflicting with the canonical |
661 | | // ABI always using a linear memory named `memory`. |
662 | 9.40k | let mut export_names = HashSet::from_iter(["memory".to_string()]); |
663 | | |
664 | 31.7k | while parts.len() < self.generator.config.max_world_items |
665 | 28.3k | && !u.is_empty() |
666 | 26.4k | && u.arbitrary()? |
667 | | { |
668 | 22.3k | let kind = u.arbitrary::<ItemKind>()?; |
669 | 22.3k | let (direction, named) = match kind { |
670 | 3.46k | ItemKind::Func(dir) | ItemKind::AnonInterface(dir) => (Some(dir), true), |
671 | 1.18k | ItemKind::Interface(dir) => (Some(dir), false), |
672 | 11.1k | ItemKind::Type => (None, true), |
673 | 2.95k | ItemKind::Use => (None, false), |
674 | 3.57k | ItemKind::Include => (None, false), |
675 | | }; |
676 | | |
677 | 22.3k | let mut part = String::new(); |
678 | | |
679 | 22.3k | if let Some(annotation) = self.gen_feature_annotation(u)? { |
680 | 3.09k | part.push_str(&annotation); |
681 | 3.09k | part.push_str("\n"); |
682 | 19.2k | } |
683 | | |
684 | 22.3k | if let Some(dir) = direction { |
685 | 4.64k | part.push_str(match dir { |
686 | 3.09k | Direction::Import => "import ", |
687 | 1.54k | Direction::Export => "export ", |
688 | | }); |
689 | 17.7k | } |
690 | | |
691 | 22.3k | let name = if named { |
692 | 14.6k | let names = match direction { |
693 | 13.4k | Some(Direction::Import) | None => &mut self.unique_names, |
694 | 1.21k | Some(Direction::Export) => &mut export_names, |
695 | | }; |
696 | 14.6k | let mut name = gen_unique_name(u, names)?; |
697 | | |
698 | | // check to see if any includes have a name clash, if so regenerate the name |
699 | | // this does have potential to throw away add a few names but that should be fine |
700 | 14.6k | for i in includes.iter() { |
701 | 0 | if self.generator.packages.contains_name( |
702 | 0 | self.package_name.to_string(), |
703 | 0 | i.to_string(), |
704 | 0 | &name, |
705 | | ) { |
706 | 0 | name = gen_unique_name(u, names)?; |
707 | 0 | } |
708 | | } |
709 | | |
710 | 14.6k | if direction.is_some() { |
711 | 3.46k | part.push_str("%"); |
712 | 3.46k | part.push_str(&name); |
713 | 3.46k | part.push_str(": "); |
714 | 11.1k | } |
715 | | |
716 | 14.6k | self.generator.packages.add_name( |
717 | 14.6k | self.package_name.to_string(), |
718 | 14.6k | world_name.to_string(), |
719 | 14.6k | name.to_string(), |
720 | | ); |
721 | 14.6k | Some(name) |
722 | | } else { |
723 | 7.72k | None |
724 | | }; |
725 | | |
726 | 22.3k | match kind { |
727 | | ItemKind::Func(_) => { |
728 | 1.87k | self.gen_func_sig(u, &mut part, false)?; |
729 | | } |
730 | 1.18k | ItemKind::Interface(dir) => { |
731 | 1.18k | let id = match self.generator.gen_interface_path(u, self.file, &mut part)? { |
732 | 386 | Some((_name, id, _types)) => id, |
733 | | // If an interface couldn't be chosen or wasn't |
734 | | // chosen then skip this import. A unique name was |
735 | | // selecteed above but we just sort of leave that |
736 | | // floating in the wild to get handled by some other |
737 | | // test case. |
738 | 799 | None => continue, |
739 | | }; |
740 | | |
741 | | // If this interface has already been imported or |
742 | | // exported this document can't do so again. Throw out |
743 | | // this item in that situation. |
744 | 386 | let unique = match dir { |
745 | 262 | Direction::Import => imported_interfaces.insert(id), |
746 | 124 | Direction::Export => exported_interfaces.insert(id), |
747 | | }; |
748 | 386 | if !unique { |
749 | 69 | continue; |
750 | 317 | } |
751 | 317 | part.push_str(";"); |
752 | | } |
753 | | ItemKind::AnonInterface(_) => { |
754 | 1.58k | let iface = |
755 | 1.58k | InterfaceGenerator::new(self.generator, self.file, self.package_name, None) |
756 | 1.58k | .gen_interface(u, None, Some(world_name))?; |
757 | 1.58k | part.push_str(&iface); |
758 | | } |
759 | | |
760 | | ItemKind::Type => { |
761 | 11.1k | let name = name.unwrap(); |
762 | 11.1k | let ty = self.gen_typedef(u, &name, &mut part)?; |
763 | 11.1k | let is_resource = ty.is_resource; |
764 | 11.1k | self.types_in_interface.push(ty); |
765 | | |
766 | 11.1k | if is_resource { |
767 | 382 | if u.arbitrary()? { |
768 | 294 | part.push_str(" {\n"); |
769 | 294 | self.gen_resource_funcs(&name, u, &mut part)?; |
770 | 294 | part.push_str("}"); |
771 | 88 | } else { |
772 | 88 | part.push_str(";"); |
773 | 88 | } |
774 | 10.7k | } |
775 | | } |
776 | | |
777 | | ItemKind::Use => { |
778 | 2.95k | if !self.gen_use(u, &mut part, Some(world_name))? { |
779 | 1.99k | continue; |
780 | 968 | } |
781 | | } |
782 | | |
783 | | ItemKind::Include => { |
784 | 3.57k | if !self.generator.config.world_include { |
785 | 3.57k | continue; |
786 | 0 | } |
787 | 0 | part.push_str("include "); |
788 | 0 | match self |
789 | 0 | .generator |
790 | 0 | .gen_world_path(u, self.file, &mut part, &mut includes)? |
791 | | { |
792 | 0 | WorldPath::Local(name) => { |
793 | | // rename things if there is an naming conflict with |
794 | | // the include and the world we are going into this |
795 | | // is a best effort, there are some edge cases where |
796 | | // we might not catch something in that case we just |
797 | | // throw away the generated world for fuzzing |
798 | 0 | let current_world = PackageWorldKey { |
799 | 0 | package_name: self.package_name.to_owned(), |
800 | 0 | world_name: world_name.to_owned(), |
801 | 0 | }; |
802 | 0 | let include_world = PackageWorldKey { |
803 | 0 | package_name: self.package_name.to_owned(), |
804 | 0 | world_name: name.to_owned(), |
805 | 0 | }; |
806 | 0 | let intersection = self |
807 | 0 | .generator |
808 | 0 | .packages |
809 | 0 | .intersect(current_world, include_world); |
810 | 0 | if let Some(names) = intersection { |
811 | 0 | part.push_str(" with { %"); |
812 | | |
813 | 0 | for n in names { |
814 | 0 | part.push_str(n); |
815 | 0 | part.push_str(" as %"); |
816 | | // we know it is in one of the worlds, lets |
817 | | // add it here just for good measure |
818 | 0 | self.unique_names.insert(n.to_string()); |
819 | 0 | let new_name = gen_unique_name(u, &mut self.unique_names)?; |
820 | 0 | part.push_str(&new_name); |
821 | 0 | part.push_str(","); |
822 | | } |
823 | 0 | part.push_str("}"); |
824 | 0 | } else { |
825 | 0 | // ; is only used if not renaming |
826 | 0 | part.push_str(";"); |
827 | 0 | } |
828 | | } |
829 | 0 | WorldPath::Remote => { |
830 | 0 | part.push_str(";"); |
831 | 0 | } |
832 | 0 | WorldPath::None => continue, |
833 | | }; |
834 | | } |
835 | | } |
836 | 15.9k | parts.push(part); |
837 | | } |
838 | | |
839 | 9.40k | shuffle(u, &mut parts)?; |
840 | | |
841 | 25.3k | for part in parts { |
842 | 15.9k | ret.push_str(&part); |
843 | 15.9k | ret.push_str("\n"); |
844 | 15.9k | } |
845 | | |
846 | 9.40k | ret.push_str("}"); |
847 | | |
848 | 9.40k | Ok(ret) |
849 | 9.40k | } |
850 | | |
851 | 880 | fn gen_resource_funcs( |
852 | 880 | &mut self, |
853 | 880 | resource_name: &str, |
854 | 880 | u: &mut Unstructured<'_>, |
855 | 880 | ret: &mut String, |
856 | 880 | ) -> Result<()> { |
857 | 880 | let mut parts = Vec::new(); |
858 | | |
859 | 0 | #[derive(Arbitrary)] Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_resource_funcs::Item as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_resource_funcs::Item as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_resource_funcs::Item as arbitrary::Arbitrary>::arbitrary::{closure#1} |
860 | | enum Item { |
861 | | Constructor, |
862 | | Static, |
863 | | Method, |
864 | | } |
865 | | |
866 | 880 | let mut has_constructor = false; |
867 | 880 | let mut names = HashSet::new(); |
868 | 880 | names.insert(resource_name.to_string()); |
869 | 3.30k | while parts.len() < self.generator.config.max_resource_items |
870 | 2.99k | && !u.is_empty() |
871 | 2.84k | && u.arbitrary()? |
872 | | { |
873 | 2.42k | match u.arbitrary()? { |
874 | 336 | Item::Constructor if has_constructor => {} |
875 | | Item::Constructor => { |
876 | 275 | has_constructor = true; |
877 | 275 | let mut part = String::new(); |
878 | | |
879 | 275 | if let Some(annotation) = self.gen_feature_annotation(u)? { |
880 | 34 | part.push_str(&annotation); |
881 | 34 | part.push_str("\n"); |
882 | 241 | } |
883 | | |
884 | 275 | part.push_str("constructor"); |
885 | 275 | self.gen_params(u, &mut part, false)?; |
886 | 275 | part.push_str(";"); |
887 | 275 | parts.push(part); |
888 | | } |
889 | | Item::Static => { |
890 | 745 | let mut part = String::new(); |
891 | | |
892 | 745 | if let Some(annotation) = self.gen_feature_annotation(u)? { |
893 | 57 | part.push_str(&annotation); |
894 | 57 | part.push_str("\n"); |
895 | 688 | } |
896 | | |
897 | 745 | part.push_str("%"); |
898 | 745 | part.push_str(&gen_unique_name(u, &mut names)?); |
899 | 745 | part.push_str(": static "); |
900 | 745 | self.gen_func_sig(u, &mut part, false)?; |
901 | 745 | parts.push(part); |
902 | | } |
903 | | Item::Method => { |
904 | 1.06k | let mut part = String::new(); |
905 | | |
906 | 1.06k | if let Some(annotation) = self.gen_feature_annotation(u)? { |
907 | 25 | part.push_str(&annotation); |
908 | 25 | part.push_str("\n"); |
909 | 1.04k | } |
910 | | |
911 | 1.06k | part.push_str("%"); |
912 | 1.06k | part.push_str(&gen_unique_name(u, &mut names)?); |
913 | 1.06k | part.push_str(": "); |
914 | 1.06k | self.gen_func_sig(u, &mut part, true)?; |
915 | 1.06k | parts.push(part); |
916 | | } |
917 | | } |
918 | | } |
919 | | |
920 | 880 | shuffle(u, &mut parts)?; |
921 | | |
922 | 2.96k | for part in parts { |
923 | 2.08k | ret.push_str(&part); |
924 | 2.08k | ret.push_str("\n"); |
925 | 2.08k | } |
926 | 880 | Ok(()) |
927 | 880 | } |
928 | | |
929 | 83.2k | fn gen_use( |
930 | 83.2k | &mut self, |
931 | 83.2k | u: &mut Unstructured<'_>, |
932 | 83.2k | part: &mut String, |
933 | 83.2k | world_name: Option<&str>, |
934 | 83.2k | ) -> Result<bool> { |
935 | 83.2k | let mut path = String::new(); |
936 | 10.6k | let (_name, _id, types) = |
937 | 83.2k | match self.generator.gen_interface_path(u, self.file, &mut path)? { |
938 | 10.6k | Some(types) => types, |
939 | 72.5k | None => return Ok(false), |
940 | | }; |
941 | 10.6k | part.push_str("use "); |
942 | 10.6k | part.push_str(&path); |
943 | 10.6k | part.push_str(".{"); |
944 | 10.6k | let ty = u.choose(types)?; |
945 | 10.6k | part.push_str("%"); |
946 | 10.6k | part.push_str(&ty.name); |
947 | 10.6k | let size = ty.size; |
948 | 10.6k | let is_resource = ty.is_resource; |
949 | 10.6k | let name = if self.unique_names.contains(&ty.name) || u.arbitrary()? { |
950 | 9.63k | part.push_str(" as %"); |
951 | 9.63k | let name = self.gen_unique_name(u)?; |
952 | 9.63k | part.push_str(&name); |
953 | | // if we name something then we need track it at the package level for includes |
954 | 9.63k | if let Some(world_name) = world_name { |
955 | 1.04k | self.generator.packages.add_name( |
956 | 1.04k | self.package_name.to_string(), |
957 | 1.04k | world_name.to_string(), |
958 | 1.04k | name.to_string(), |
959 | 1.04k | ); |
960 | 8.58k | } |
961 | 9.63k | name |
962 | | } else { |
963 | 1.04k | assert!(self.unique_names.insert(ty.name.clone())); |
964 | 1.04k | ty.name.clone() |
965 | | }; |
966 | 10.6k | self.types_in_interface.push(Type { |
967 | 10.6k | name, |
968 | 10.6k | size, |
969 | 10.6k | is_resource, |
970 | 10.6k | }); |
971 | 10.6k | part.push_str("};"); |
972 | 10.6k | Ok(true) |
973 | 83.2k | } |
974 | | |
975 | 23.3k | fn gen_typedef( |
976 | 23.3k | &mut self, |
977 | 23.3k | u: &mut Unstructured<'_>, |
978 | 23.3k | name: &str, |
979 | 23.3k | ret: &mut String, |
980 | 23.3k | ) -> Result<Type> { |
981 | 0 | #[derive(Arbitrary)] Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_typedef::Kind as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_typedef::Kind as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_typedef::Kind as arbitrary::Arbitrary>::arbitrary::{closure#1} |
982 | | pub enum Kind { |
983 | | Record, |
984 | | Flags, |
985 | | Variant, |
986 | | Enum, |
987 | | Anonymous, |
988 | | Resource, |
989 | | } |
990 | | |
991 | 23.3k | let mut fuel = self.generator.config.max_type_size; |
992 | | |
993 | 23.3k | let mut is_resource = false; |
994 | 23.3k | match u.arbitrary()? { |
995 | | Kind::Record => { |
996 | 2.39k | ret.push_str("record %"); |
997 | 2.39k | ret.push_str(name); |
998 | 2.39k | ret.push_str(" {\n"); |
999 | 2.39k | for _ in 0..u.int_in_range(1..=self.generator.config.max_type_parts)? { |
1000 | 6.30k | ret.push_str(" %"); |
1001 | 6.30k | ret.push_str(&self.gen_unique_name(u)?); |
1002 | 6.30k | ret.push_str(": "); |
1003 | 6.30k | self.gen_type(u, &mut fuel, ret)?; |
1004 | 6.30k | ret.push_str(",\n"); |
1005 | | } |
1006 | 2.39k | ret.push_str("}"); |
1007 | | } |
1008 | | Kind::Variant => { |
1009 | 4.45k | ret.push_str("variant %"); |
1010 | 4.45k | ret.push_str(name); |
1011 | 4.45k | ret.push_str(" {\n"); |
1012 | 4.45k | for _ in 0..u.int_in_range(1..=self.generator.config.max_type_parts)? { |
1013 | 15.1k | ret.push_str(" %"); |
1014 | 15.1k | ret.push_str(&self.gen_unique_name(u)?); |
1015 | 15.1k | if u.arbitrary()? { |
1016 | 13.3k | ret.push_str("("); |
1017 | 13.3k | self.gen_type(u, &mut fuel, ret)?; |
1018 | 13.3k | ret.push_str(")"); |
1019 | 1.89k | } |
1020 | 15.1k | ret.push_str(",\n"); |
1021 | | } |
1022 | 4.45k | ret.push_str("}"); |
1023 | | } |
1024 | | Kind::Enum => { |
1025 | 13.4k | ret.push_str("enum %"); |
1026 | 13.4k | ret.push_str(name); |
1027 | 13.4k | ret.push_str(" {\n"); |
1028 | 13.4k | for _ in 0..u.int_in_range(1..=self.generator.config.max_type_parts)? { |
1029 | 58.8k | ret.push_str(" %"); |
1030 | 58.8k | ret.push_str(&self.gen_unique_name(u)?); |
1031 | 58.8k | ret.push_str(",\n"); |
1032 | | } |
1033 | 13.4k | ret.push_str("}"); |
1034 | | } |
1035 | | Kind::Flags => { |
1036 | 830 | ret.push_str("flags %"); |
1037 | 830 | ret.push_str(name); |
1038 | 830 | ret.push_str(" {\n"); |
1039 | 830 | for _ in 0..u.int_in_range(1..=self.generator.config.max_type_parts)? { |
1040 | 2.49k | ret.push_str(" %"); |
1041 | 2.49k | ret.push_str(&self.gen_unique_name(u)?); |
1042 | 2.49k | ret.push_str(",\n"); |
1043 | | } |
1044 | 830 | ret.push_str("}"); |
1045 | | } |
1046 | | Kind::Anonymous => { |
1047 | 887 | ret.push_str("type %"); |
1048 | 887 | ret.push_str(name); |
1049 | 887 | ret.push_str(" = "); |
1050 | 887 | self.gen_type(u, &mut fuel, ret)?; |
1051 | 887 | ret.push_str(";"); |
1052 | | } |
1053 | 1.31k | Kind::Resource => { |
1054 | 1.31k | is_resource = true; |
1055 | 1.31k | ret.push_str("resource %"); |
1056 | 1.31k | ret.push_str(name); |
1057 | 1.31k | } |
1058 | | } |
1059 | | |
1060 | 23.3k | Ok(Type { |
1061 | 23.3k | size: self.generator.config.max_type_size - fuel, |
1062 | 23.3k | is_resource, |
1063 | 23.3k | name: name.to_string(), |
1064 | 23.3k | }) |
1065 | 23.3k | } |
1066 | | |
1067 | 233k | fn gen_type( |
1068 | 233k | &mut self, |
1069 | 233k | u: &mut Unstructured<'_>, |
1070 | 233k | fuel: &mut usize, |
1071 | 233k | dst: &mut String, |
1072 | 233k | ) -> Result<()> { |
1073 | 0 | #[derive(Arbitrary)] Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_type::Kind as arbitrary::Arbitrary>::try_size_hint::{closure#0}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_type::Kind as arbitrary::Arbitrary>::arbitrary_take_rest::{closure#1}Unexecuted instantiation: <<wit_smith::generate::InterfaceGenerator>::gen_type::Kind as arbitrary::Arbitrary>::arbitrary::{closure#1} |
1074 | | enum Kind { |
1075 | | Bool, |
1076 | | U8, |
1077 | | U16, |
1078 | | U32, |
1079 | | U64, |
1080 | | S8, |
1081 | | S16, |
1082 | | S32, |
1083 | | S64, |
1084 | | F32, |
1085 | | F64, |
1086 | | Char, |
1087 | | String, |
1088 | | Id, |
1089 | | Tuple, |
1090 | | Option, |
1091 | | Result, |
1092 | | List, |
1093 | | FixedLengthList, |
1094 | | Stream, |
1095 | | Future, |
1096 | | ErrorContext, |
1097 | | } |
1098 | | |
1099 | 233k | *fuel = match fuel.checked_sub(1) { |
1100 | 205k | Some(fuel) => fuel, |
1101 | | None => { |
1102 | 28.0k | dst.push_str("bool"); |
1103 | 28.0k | return Ok(()); |
1104 | | } |
1105 | | }; |
1106 | | loop { |
1107 | 222k | break match u.arbitrary()? { |
1108 | 14.7k | Kind::Bool => dst.push_str("bool"), |
1109 | 3.72k | Kind::U8 => dst.push_str("u8"), |
1110 | 1.48k | Kind::S8 => dst.push_str("s8"), |
1111 | 920 | Kind::U16 => dst.push_str("u16"), |
1112 | 776 | Kind::S16 => dst.push_str("s16"), |
1113 | 1.94k | Kind::U32 => dst.push_str("u32"), |
1114 | 1.23k | Kind::S32 => dst.push_str("s32"), |
1115 | 1.53k | Kind::U64 => dst.push_str("u64"), |
1116 | 7.28k | Kind::S64 => dst.push_str("s64"), |
1117 | 2.70k | Kind::F32 => dst.push_str("f32"), |
1118 | 5.42k | Kind::F64 => dst.push_str("f64"), |
1119 | 5.56k | Kind::Char => dst.push_str("char"), |
1120 | 1.12k | Kind::String => dst.push_str("string"), |
1121 | | Kind::Id => { |
1122 | 3.46k | if self.types_in_interface.is_empty() { |
1123 | 2.05k | continue; |
1124 | 1.40k | } |
1125 | 1.40k | let ty = u.choose(&self.types_in_interface)?; |
1126 | 1.40k | *fuel = match fuel.checked_sub(ty.size) { |
1127 | 1.37k | Some(fuel) => fuel, |
1128 | 26 | None => continue, |
1129 | | }; |
1130 | 1.37k | let own_wrapper = if ty.is_resource && u.arbitrary()? { |
1131 | 350 | dst.push_str("own<"); |
1132 | 350 | true |
1133 | | } else { |
1134 | 1.02k | false |
1135 | | }; |
1136 | 1.37k | dst.push_str("%"); |
1137 | 1.37k | dst.push_str(&ty.name); |
1138 | 1.37k | if own_wrapper { |
1139 | 350 | dst.push_str(">"); |
1140 | 1.02k | } |
1141 | | } |
1142 | | Kind::Tuple => { |
1143 | 9.78k | let fields = u.int_in_range(1..=self.generator.config.max_type_parts)?; |
1144 | 9.78k | *fuel = match fuel.checked_sub(fields) { |
1145 | 9.21k | Some(fuel) => fuel, |
1146 | 568 | None => continue, |
1147 | | }; |
1148 | 9.21k | dst.push_str("tuple<"); |
1149 | 33.8k | for i in 0..fields { |
1150 | 33.8k | if i > 0 { |
1151 | 24.6k | dst.push_str(", "); |
1152 | 24.6k | } |
1153 | 33.8k | self.gen_type(u, fuel, dst)?; |
1154 | | } |
1155 | 9.21k | dst.push_str(">"); |
1156 | | } |
1157 | | Kind::Option => { |
1158 | 11.5k | *fuel = match fuel.checked_sub(1) { |
1159 | 11.0k | Some(fuel) => fuel, |
1160 | 512 | None => continue, |
1161 | | }; |
1162 | 11.0k | dst.push_str("option<"); |
1163 | 11.0k | self.gen_type(u, fuel, dst)?; |
1164 | 11.0k | dst.push_str(">"); |
1165 | | } |
1166 | | Kind::List => { |
1167 | 3.50k | *fuel = match fuel.checked_sub(1) { |
1168 | 2.86k | Some(fuel) => fuel, |
1169 | 646 | None => continue, |
1170 | | }; |
1171 | 2.86k | dst.push_str("list<"); |
1172 | 2.86k | self.gen_type(u, fuel, dst)?; |
1173 | 2.86k | dst.push_str(">"); |
1174 | | } |
1175 | | Kind::FixedLengthList => { |
1176 | 105k | if !self.generator.config.fixed_length_lists { |
1177 | 623 | continue; |
1178 | 105k | } |
1179 | 105k | *fuel = match fuel.checked_sub(1) { |
1180 | 105k | Some(fuel) => fuel, |
1181 | 59 | None => continue, |
1182 | | }; |
1183 | 105k | let elements = |
1184 | 105k | u.int_in_range(1..=self.generator.config.max_type_parts as u32)?; |
1185 | 105k | dst.push_str("list<"); |
1186 | 105k | self.gen_type(u, fuel, dst)?; |
1187 | 105k | dst.push_str(&format!(", {elements}>")); |
1188 | | } |
1189 | | Kind::Result => { |
1190 | 13.0k | *fuel = match fuel.checked_sub(2) { |
1191 | 12.5k | Some(fuel) => fuel, |
1192 | 488 | None => continue, |
1193 | | }; |
1194 | 12.5k | dst.push_str("result"); |
1195 | 12.5k | let ok = u.arbitrary()?; |
1196 | 12.5k | let err = u.arbitrary()?; |
1197 | 12.5k | match (ok, err) { |
1198 | | (true, true) => { |
1199 | 9.42k | dst.push_str("<"); |
1200 | 9.42k | self.gen_type(u, fuel, dst)?; |
1201 | 9.42k | dst.push_str(", "); |
1202 | 9.42k | self.gen_type(u, fuel, dst)?; |
1203 | 9.42k | dst.push_str(">"); |
1204 | | } |
1205 | | (true, false) => { |
1206 | 1.78k | dst.push_str("<"); |
1207 | 1.78k | self.gen_type(u, fuel, dst)?; |
1208 | 1.78k | dst.push_str(">"); |
1209 | | } |
1210 | | (false, true) => { |
1211 | 1.09k | dst.push_str("<_, "); |
1212 | 1.09k | self.gen_type(u, fuel, dst)?; |
1213 | 1.09k | dst.push_str(">"); |
1214 | | } |
1215 | 271 | (false, false) => {} |
1216 | | } |
1217 | | } |
1218 | | Kind::Stream => { |
1219 | 3.34k | if !self.generator.config.streams { |
1220 | 1.02k | continue; |
1221 | 2.32k | } |
1222 | 2.32k | *fuel = match fuel.checked_sub(1) { |
1223 | 2.28k | Some(fuel) => fuel, |
1224 | 44 | None => continue, |
1225 | | }; |
1226 | 2.28k | dst.push_str("stream<"); |
1227 | 2.28k | self.gen_type(u, fuel, dst)?; |
1228 | 2.28k | dst.push_str(">"); |
1229 | | } |
1230 | | Kind::Future => { |
1231 | 4.62k | if !self.generator.config.futures { |
1232 | 1.44k | continue; |
1233 | 3.17k | } |
1234 | 3.17k | *fuel = match fuel.checked_sub(1) { |
1235 | 3.03k | Some(fuel) => fuel, |
1236 | 142 | None => continue, |
1237 | | }; |
1238 | 3.03k | if u.arbitrary()? { |
1239 | 1.70k | dst.push_str("future<"); |
1240 | 1.70k | self.gen_type(u, fuel, dst)?; |
1241 | 1.70k | dst.push_str(">"); |
1242 | 1.32k | } else { |
1243 | 1.32k | dst.push_str("future"); |
1244 | 1.32k | } |
1245 | | } |
1246 | | Kind::ErrorContext => { |
1247 | 18.7k | if !self.generator.config.error_context { |
1248 | 9.39k | continue; |
1249 | 9.38k | } |
1250 | 9.38k | dst.push_str("error-context"); |
1251 | | } |
1252 | | }; |
1253 | | } |
1254 | | |
1255 | 205k | Ok(()) |
1256 | 233k | } |
1257 | | |
1258 | 7.76k | fn gen_func(&mut self, u: &mut Unstructured<'_>, ret: &mut String) -> Result<()> { |
1259 | 7.76k | ret.push_str("%"); |
1260 | 7.76k | ret.push_str(&self.gen_unique_name(u)?); |
1261 | 7.76k | ret.push_str(": "); |
1262 | 7.76k | self.gen_func_sig(u, ret, false)?; |
1263 | 7.76k | Ok(()) |
1264 | 7.76k | } |
1265 | | |
1266 | 11.4k | fn gen_func_sig( |
1267 | 11.4k | &mut self, |
1268 | 11.4k | u: &mut Unstructured<'_>, |
1269 | 11.4k | dst: &mut String, |
1270 | 11.4k | method: bool, |
1271 | 11.4k | ) -> Result<()> { |
1272 | 11.4k | if self.generator.config.async_ && u.arbitrary()? { |
1273 | 6.49k | dst.push_str("async "); |
1274 | 6.49k | } |
1275 | 11.4k | dst.push_str("func"); |
1276 | 11.4k | self.gen_params(u, dst, method)?; |
1277 | 11.4k | if u.arbitrary()? { |
1278 | 8.29k | dst.push_str(" -> "); |
1279 | 8.29k | let mut fuel = self.generator.config.max_type_size; |
1280 | 8.29k | self.gen_type(u, &mut fuel, dst)?; |
1281 | 3.15k | } |
1282 | 11.4k | dst.push_str(";"); |
1283 | 11.4k | Ok(()) |
1284 | 11.4k | } |
1285 | | |
1286 | 11.7k | fn gen_params( |
1287 | 11.7k | &mut self, |
1288 | 11.7k | u: &mut Unstructured<'_>, |
1289 | 11.7k | dst: &mut String, |
1290 | 11.7k | method: bool, |
1291 | 11.7k | ) -> Result<()> { |
1292 | 11.7k | dst.push_str("("); |
1293 | 11.7k | let mut names = HashSet::new(); |
1294 | 11.7k | if method { |
1295 | 1.06k | names.insert("self".to_string()); |
1296 | 10.6k | } |
1297 | 11.7k | let mut fuel = self.generator.config.max_type_size; |
1298 | 26.0k | for i in 0..u.int_in_range(0..=self.generator.config.max_type_parts)? { |
1299 | 26.0k | if i > 0 { |
1300 | 16.8k | dst.push_str(", "); |
1301 | 16.8k | } |
1302 | 26.0k | dst.push_str("%"); |
1303 | 26.0k | dst.push_str(&gen_unique_name(u, &mut names)?); |
1304 | 26.0k | dst.push_str(": "); |
1305 | 26.0k | self.gen_type(u, &mut fuel, dst)?; |
1306 | | } |
1307 | 11.7k | dst.push_str(")"); |
1308 | 11.7k | Ok(()) |
1309 | 11.7k | } |
1310 | | |
1311 | 112k | fn gen_unique_name(&mut self, u: &mut Unstructured<'_>) -> Result<String> { |
1312 | 112k | gen_unique_name(u, &mut self.unique_names) |
1313 | 112k | } |
1314 | | } |
1315 | | |
1316 | 169k | fn gen_unique_name(u: &mut Unstructured<'_>, set: &mut HashSet<String>) -> Result<String> { |
1317 | 169k | let mut name = gen_name(u)?; |
1318 | 293k | while !set.insert(name.clone()) { |
1319 | 123k | write!(&mut name, "{}", set.len()).unwrap(); |
1320 | 123k | } |
1321 | 169k | Ok(name) |
1322 | 169k | } |
1323 | | |
1324 | 208k | fn gen_name(u: &mut Unstructured<'_>) -> Result<String> { |
1325 | 208k | let size = u.arbitrary_len::<u8>()?; |
1326 | 208k | let size = std::cmp::min(size, 20); |
1327 | 208k | let name = match str::from_utf8(u.peek_bytes(size).unwrap()) { |
1328 | 65.5k | Ok(s) => { |
1329 | 65.5k | u.bytes(size).unwrap(); |
1330 | 65.5k | s.to_string() |
1331 | | } |
1332 | 143k | Err(e) => { |
1333 | 143k | let i = e.valid_up_to(); |
1334 | 143k | let valid = u.bytes(i).unwrap(); |
1335 | 143k | str::from_utf8(valid).unwrap().to_string() |
1336 | | } |
1337 | | }; |
1338 | 208k | let name = name |
1339 | 208k | .chars() |
1340 | 758k | .map(|x| if x.is_ascii_lowercase() { x } else { 'x' }) |
1341 | 208k | .collect::<String>(); |
1342 | 208k | Ok(if name.is_empty() { |
1343 | 150k | "name".to_string() |
1344 | | } else { |
1345 | 58.2k | name |
1346 | | }) |
1347 | 208k | } |
1348 | | |
1349 | 63.3k | fn shuffle<T>(u: &mut Unstructured<'_>, mut slice: &mut [T]) -> Result<()> { |
1350 | 167k | while slice.len() > 0 { |
1351 | 103k | let pos = u.int_in_range(0..=slice.len() - 1)?; |
1352 | 103k | slice.swap(0, pos); |
1353 | 103k | slice = &mut slice[1..]; |
1354 | | } |
1355 | 63.3k | Ok(()) |
1356 | 63.3k | } wit_smith::generate::shuffle::<wit_smith::generate::File> Line | Count | Source | 1349 | 7.33k | fn shuffle<T>(u: &mut Unstructured<'_>, mut slice: &mut [T]) -> Result<()> { | 1350 | 24.0k | while slice.len() > 0 { | 1351 | 16.6k | let pos = u.int_in_range(0..=slice.len() - 1)?; | 1352 | 16.6k | slice.swap(0, pos); | 1353 | 16.6k | slice = &mut slice[1..]; | 1354 | | } | 1355 | 7.33k | Ok(()) | 1356 | 7.33k | } |
wit_smith::generate::shuffle::<alloc::string::String> Line | Count | Source | 1349 | 56.0k | fn shuffle<T>(u: &mut Unstructured<'_>, mut slice: &mut [T]) -> Result<()> { | 1350 | 143k | while slice.len() > 0 { | 1351 | 87.1k | let pos = u.int_in_range(0..=slice.len() - 1)?; | 1352 | 87.1k | slice.swap(0, pos); | 1353 | 87.1k | slice = &mut slice[1..]; | 1354 | | } | 1355 | 56.0k | Ok(()) | 1356 | 56.0k | } |
|
1357 | | |
1358 | | #[derive(Clone, Default)] |
1359 | | struct File { |
1360 | | items: Vec<String>, |
1361 | | namespace: HashMap<String, (DefinitionLevel, DefinitionKind)>, |
1362 | | interfaces: IndexMap<String, FileInterface>, |
1363 | | worlds: IndexSet<String>, |
1364 | | } |
1365 | | |
1366 | | #[derive(Clone)] |
1367 | | struct FileInterface { |
1368 | | name: String, |
1369 | | id: u32, |
1370 | | types: Rc<Vec<Type>>, |
1371 | | } |
1372 | | |
1373 | | #[derive(Debug, Copy, Clone, PartialEq)] |
1374 | | enum DefinitionLevel { |
1375 | | Package, |
1376 | | File, |
1377 | | } |
1378 | | |
1379 | | #[derive(Debug, Copy, Clone, PartialEq)] |
1380 | | enum DefinitionKind { |
1381 | | World, |
1382 | | Interface, |
1383 | | } |
1384 | | |
1385 | | enum WorldPath<'a> { |
1386 | | None, |
1387 | | Local(&'a str), |
1388 | | Remote, |
1389 | | } |
1390 | | |
1391 | | impl File { |
1392 | 36.8k | fn gen_unique_package_name( |
1393 | 36.8k | &mut self, |
1394 | 36.8k | u: &mut Unstructured<'_>, |
1395 | 36.8k | names: &mut HashSet<String>, |
1396 | 36.8k | kind: DefinitionKind, |
1397 | 36.8k | ) -> Result<String> { |
1398 | 36.8k | let mut name = gen_name(u)?; |
1399 | | loop { |
1400 | | // Find a package-unique name first |
1401 | 61.0k | if !names.insert(name.clone()) { |
1402 | 24.2k | write!(&mut name, "{}", names.len()).unwrap(); |
1403 | 24.2k | continue; |
1404 | 36.8k | } |
1405 | | |
1406 | | // Then make sure it's file-unique too |
1407 | 36.8k | if self.claim_file_name(&mut name, kind) { |
1408 | 36.8k | break; |
1409 | 17 | } |
1410 | | } |
1411 | 36.8k | Ok(name) |
1412 | 36.8k | } |
1413 | | |
1414 | 2.27k | fn gen_unique_file_name( |
1415 | 2.27k | &mut self, |
1416 | 2.27k | u: &mut Unstructured<'_>, |
1417 | 2.27k | kind: DefinitionKind, |
1418 | 2.27k | ) -> Result<String> { |
1419 | 2.27k | let mut name = gen_name(u)?; |
1420 | 3.47k | while !self.claim_file_name(&mut name, kind) { |
1421 | 1.19k | // try again on the next iteration |
1422 | 1.19k | } |
1423 | 2.27k | Ok(name) |
1424 | 2.27k | } |
1425 | | |
1426 | 40.3k | fn claim_file_name(&mut self, name: &mut String, kind: DefinitionKind) -> bool { |
1427 | 40.3k | match self.namespace.entry(name.clone()) { |
1428 | 1.40k | Entry::Occupied(mut e) => match e.get().0 { |
1429 | | // If this name is already claimed elsewhere in the package |
1430 | | // then that's ok as we're going to shadow it, so switch it |
1431 | | // to a file definition. |
1432 | 189 | DefinitionLevel::Package => *e.get_mut() = (DefinitionLevel::File, kind), |
1433 | | |
1434 | | // If it's already defined in the file try to add more stuff |
1435 | | // to the name to make the next try not collide. |
1436 | | DefinitionLevel::File => { |
1437 | 1.21k | name.push_str("y"); |
1438 | 1.21k | write!(name, "{}", self.namespace.len()).unwrap(); |
1439 | 1.21k | return false; |
1440 | | } |
1441 | | }, |
1442 | | |
1443 | | // Not defined? Claim it. |
1444 | 38.9k | Entry::Vacant(v) => { |
1445 | 38.9k | v.insert((DefinitionLevel::File, kind)); |
1446 | 38.9k | } |
1447 | | } |
1448 | 39.1k | true |
1449 | 40.3k | } |
1450 | | |
1451 | 32.2k | fn insert_definition(&mut self, name: &str, kind: DefinitionKind) -> bool { |
1452 | 32.2k | match self.namespace.get(name) { |
1453 | | // This name is already defined, so it can't be inserted. |
1454 | 14.8k | Some((DefinitionLevel::File, _)) => return false, |
1455 | 0 | Some(other) => { |
1456 | 0 | panic!("found duplicate definition when should be package-unique: {other:?}") |
1457 | | } |
1458 | 17.4k | None => {} |
1459 | | } |
1460 | 17.4k | let prev = self |
1461 | 17.4k | .namespace |
1462 | 17.4k | .insert(name.to_string(), (DefinitionLevel::Package, kind)); |
1463 | 17.4k | assert!(prev.is_none()); |
1464 | 17.4k | true |
1465 | 32.2k | } |
1466 | | } |
1467 | | |
1468 | 8.46k | fn gen_version_less_than( |
1469 | 8.46k | u: &mut Unstructured<'_>, |
1470 | 8.46k | existing_version: &Option<Version>, |
1471 | 8.46k | ) -> Result<Version> { |
1472 | | const MAX_VERSION_RANGE: u64 = 10; |
1473 | 8.46k | let (major, minor, patch) = match existing_version { |
1474 | 3.24k | Some(v) => (v.major, v.minor, v.patch), |
1475 | 5.22k | None => (MAX_VERSION_RANGE, MAX_VERSION_RANGE, MAX_VERSION_RANGE), |
1476 | | }; |
1477 | | |
1478 | 8.46k | let new_version = Version { |
1479 | 8.46k | major: u.int_in_range(0..=major)?, |
1480 | 8.46k | minor: u.int_in_range(0..=minor)?, |
1481 | 8.46k | patch: u.int_in_range(0..=patch)?, |
1482 | 8.46k | pre: if (u.arbitrary()? && existing_version.is_none()) |
1483 | 4.56k | || existing_version.as_ref().is_some_and(|x| !x.pre.is_empty()) |
1484 | | { |
1485 | 6.24k | semver::Prerelease::new("alpha.0").unwrap() |
1486 | | } else { |
1487 | 2.21k | semver::Prerelease::EMPTY |
1488 | | }, |
1489 | 8.46k | build: if (u.arbitrary()? && existing_version.is_none()) |
1490 | 4.75k | || existing_version |
1491 | 4.75k | .as_ref() |
1492 | 4.75k | .is_some_and(|x| !x.build.is_empty()) |
1493 | | { |
1494 | 5.72k | semver::BuildMetadata::new("1.2.0").unwrap() |
1495 | | } else { |
1496 | 2.73k | semver::BuildMetadata::EMPTY |
1497 | | }, |
1498 | | }; |
1499 | | |
1500 | 8.46k | if let Some(v) = existing_version { |
1501 | 3.24k | assert!(&new_version <= v, "{} <= {}", &new_version, v); |
1502 | 5.22k | } |
1503 | | |
1504 | 8.46k | Ok(new_version) |
1505 | 8.46k | } |
1506 | | |
1507 | 5.22k | fn gen_version(u: &mut Unstructured<'_>) -> Result<Version> { |
1508 | 5.22k | gen_version_less_than(u, &None) |
1509 | 5.22k | } |