/src/wasm-tools/crates/wit-parser/src/lib.rs
Line | Count | Source |
1 | | #![no_std] |
2 | | |
3 | | extern crate alloc; |
4 | | |
5 | | #[cfg(feature = "std")] |
6 | | extern crate std; |
7 | | |
8 | | use crate::abi::AbiVariant; |
9 | | use alloc::format; |
10 | | use alloc::string::{String, ToString}; |
11 | | use alloc::vec::Vec; |
12 | | #[cfg(feature = "std")] |
13 | | use anyhow::Context; |
14 | | use anyhow::{Result, bail}; |
15 | | use id_arena::{Arena, Id}; |
16 | | use semver::Version; |
17 | | |
18 | | #[cfg(feature = "std")] |
19 | | pub type IndexMap<K, V> = indexmap::IndexMap<K, V, std::hash::RandomState>; |
20 | | #[cfg(feature = "std")] |
21 | | pub type IndexSet<T> = indexmap::IndexSet<T, std::hash::RandomState>; |
22 | | #[cfg(not(feature = "std"))] |
23 | | pub type IndexMap<K, V> = indexmap::IndexMap<K, V, hashbrown::DefaultHashBuilder>; |
24 | | #[cfg(not(feature = "std"))] |
25 | | pub type IndexSet<T> = indexmap::IndexSet<T, hashbrown::DefaultHashBuilder>; |
26 | | |
27 | | #[cfg(feature = "std")] |
28 | | pub(crate) use std::collections::{HashMap, HashSet}; |
29 | | |
30 | | #[cfg(not(feature = "std"))] |
31 | | pub(crate) use hashbrown::{HashMap, HashSet}; |
32 | | |
33 | | use alloc::borrow::Cow; |
34 | | use core::fmt; |
35 | | use core::hash::{Hash, Hasher}; |
36 | | #[cfg(feature = "std")] |
37 | | use std::path::Path; |
38 | | |
39 | | #[cfg(feature = "decoding")] |
40 | | pub mod decoding; |
41 | | #[cfg(feature = "decoding")] |
42 | | mod metadata; |
43 | | #[cfg(feature = "decoding")] |
44 | | pub use metadata::PackageMetadata; |
45 | | |
46 | | pub mod abi; |
47 | | mod ast; |
48 | | pub use ast::SourceMap; |
49 | | pub use ast::lex::Span; |
50 | | pub use ast::{ParsedUsePath, parse_use_path}; |
51 | | mod sizealign; |
52 | | pub use sizealign::*; |
53 | | mod resolve; |
54 | | pub use resolve::*; |
55 | | mod live; |
56 | | pub use live::{LiveTypes, TypeIdVisitor}; |
57 | | |
58 | | #[cfg(feature = "serde")] |
59 | | use serde_derive::Serialize; |
60 | | #[cfg(feature = "serde")] |
61 | | mod serde_; |
62 | | #[cfg(feature = "serde")] |
63 | | use serde_::*; |
64 | | |
65 | | /// Checks if the given string is a legal identifier in wit. |
66 | 0 | pub fn validate_id(s: &str) -> Result<()> { |
67 | 0 | ast::validate_id(0, s)?; |
68 | 0 | Ok(()) |
69 | 0 | } |
70 | | |
71 | | pub type WorldId = Id<World>; |
72 | | pub type InterfaceId = Id<Interface>; |
73 | | pub type TypeId = Id<TypeDef>; |
74 | | |
75 | | /// Representation of a parsed WIT package which has not resolved external |
76 | | /// dependencies yet. |
77 | | /// |
78 | | /// This representation has performed internal resolution of the WIT package |
79 | | /// itself, ensuring that all references internally are valid and the WIT was |
80 | | /// syntactically valid and such. |
81 | | /// |
82 | | /// The fields of this structure represent a flat list of arrays unioned from |
83 | | /// all documents within the WIT package. This means, for example, that all |
84 | | /// types from all documents are located in `self.types`. The fields of each |
85 | | /// item can help splitting back out into packages/interfaces/etc as necessary. |
86 | | /// |
87 | | /// Note that an `UnresolvedPackage` cannot be queried in general about |
88 | | /// information such as size or alignment as that would require resolution of |
89 | | /// foreign dependencies. Translations such as to-binary additionally are not |
90 | | /// supported on an `UnresolvedPackage` due to the lack of knowledge about the |
91 | | /// foreign types. This is intended to be an intermediate state which can be |
92 | | /// inspected by embedders, if necessary, before quickly transforming to a |
93 | | /// [`Resolve`] to fully work with a WIT package. |
94 | | /// |
95 | | /// After an [`UnresolvedPackage`] is parsed it can be fully resolved with |
96 | | /// [`Resolve::push`]. During this operation a dependency map is specified which |
97 | | /// will connect the `foreign_deps` field of this structure to packages |
98 | | /// previously inserted within the [`Resolve`]. Embedders are responsible for |
99 | | /// performing this resolution themselves. |
100 | | #[derive(Clone)] |
101 | | pub struct UnresolvedPackage { |
102 | | /// The namespace, name, and version information for this package. |
103 | | pub name: PackageName, |
104 | | |
105 | | /// All worlds from all documents within this package. |
106 | | /// |
107 | | /// Each world lists the document that it is from. |
108 | | pub worlds: Arena<World>, |
109 | | |
110 | | /// All interfaces from all documents within this package. |
111 | | /// |
112 | | /// Each interface lists the document that it is from. Interfaces are listed |
113 | | /// in topological order as well so iteration through this arena will only |
114 | | /// reference prior elements already visited when working with recursive |
115 | | /// references. |
116 | | pub interfaces: Arena<Interface>, |
117 | | |
118 | | /// All types from all documents within this package. |
119 | | /// |
120 | | /// Each type lists the interface or world that defined it, or nothing if |
121 | | /// it's an anonymous type. Types are listed in this arena in topological |
122 | | /// order to ensure that iteration through this arena will only reference |
123 | | /// other types transitively that are already iterated over. |
124 | | pub types: Arena<TypeDef>, |
125 | | |
126 | | /// All foreign dependencies that this package depends on. |
127 | | /// |
128 | | /// These foreign dependencies must be resolved to convert this unresolved |
129 | | /// package into a `Resolve`. The map here is keyed by the name of the |
130 | | /// foreign package that this depends on, and the sub-map is keyed by an |
131 | | /// interface name followed by the identifier within `self.interfaces`. The |
132 | | /// fields of `self.interfaces` describes the required types that are from |
133 | | /// each foreign interface. |
134 | | pub foreign_deps: IndexMap<PackageName, IndexMap<String, (AstItem, Vec<Stability>)>>, |
135 | | |
136 | | /// Doc comments for this package. |
137 | | pub docs: Docs, |
138 | | |
139 | | #[cfg_attr(not(feature = "std"), allow(dead_code))] |
140 | | package_name_span: Span, |
141 | | unknown_type_spans: Vec<Span>, |
142 | | foreign_dep_spans: Vec<Span>, |
143 | | required_resource_types: Vec<(TypeId, Span)>, |
144 | | } |
145 | | |
146 | | impl UnresolvedPackage { |
147 | | /// Adjusts all spans in this package by adding the given byte offset. |
148 | | /// |
149 | | /// This is used when merging source maps to update spans to point to the |
150 | | /// correct location in the combined source map. |
151 | 13.0k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
152 | | // Adjust parallel vec spans |
153 | 13.0k | self.package_name_span.adjust(offset); |
154 | 13.0k | for span in &mut self.unknown_type_spans { |
155 | 2.10k | span.adjust(offset); |
156 | 2.10k | } |
157 | 13.0k | for span in &mut self.foreign_dep_spans { |
158 | 2.05k | span.adjust(offset); |
159 | 2.05k | } |
160 | 13.0k | for (_, span) in &mut self.required_resource_types { |
161 | 16 | span.adjust(offset); |
162 | 16 | } |
163 | | |
164 | | // Adjust spans on arena items |
165 | 16.8k | for (_, world) in self.worlds.iter_mut() { |
166 | 16.8k | world.adjust_spans(offset); |
167 | 16.8k | } |
168 | 56.5k | for (_, iface) in self.interfaces.iter_mut() { |
169 | 56.5k | iface.adjust_spans(offset); |
170 | 56.5k | } |
171 | 142k | for (_, ty) in self.types.iter_mut() { |
172 | 142k | ty.adjust_spans(offset); |
173 | 142k | } |
174 | 13.0k | } |
175 | | } |
176 | | |
177 | | /// Tracks a set of packages, all pulled from the same group of WIT source files. |
178 | | #[derive(Clone)] |
179 | | pub struct UnresolvedPackageGroup { |
180 | | /// The "main" package in this package group which was found at the root of |
181 | | /// the WIT files. |
182 | | /// |
183 | | /// Note that this is required to be present in all WIT files. |
184 | | pub main: UnresolvedPackage, |
185 | | |
186 | | /// Nested packages found while parsing `main`, if any. |
187 | | pub nested: Vec<UnresolvedPackage>, |
188 | | |
189 | | /// A set of processed source files from which these packages have been parsed. |
190 | | pub source_map: SourceMap, |
191 | | } |
192 | | |
193 | | #[derive(Debug, Copy, Clone)] |
194 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
195 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
196 | | pub enum AstItem { |
197 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
198 | | Interface(InterfaceId), |
199 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
200 | | World(WorldId), |
201 | | } |
202 | | |
203 | | /// A structure used to keep track of the name of a package, containing optional |
204 | | /// information such as a namespace and version information. |
205 | | /// |
206 | | /// This is directly encoded as an "ID" in the binary component representation |
207 | | /// with an interfaced tacked on as well. |
208 | | #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] |
209 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
210 | | #[cfg_attr(feature = "serde", serde(into = "String"))] |
211 | | pub struct PackageName { |
212 | | /// A namespace such as `wasi` in `wasi:foo/bar` |
213 | | pub namespace: String, |
214 | | /// The kebab-name of this package, which is always specified. |
215 | | pub name: String, |
216 | | /// Optional major/minor version information. |
217 | | pub version: Option<Version>, |
218 | | } |
219 | | |
220 | | impl From<PackageName> for String { |
221 | 0 | fn from(name: PackageName) -> String { |
222 | 0 | name.to_string() |
223 | 0 | } |
224 | | } |
225 | | |
226 | | impl PackageName { |
227 | | /// Returns the ID that this package name would assign the `interface` name |
228 | | /// specified. |
229 | 17.3k | pub fn interface_id(&self, interface: &str) -> String { |
230 | 17.3k | let mut s = String::new(); |
231 | 17.3k | s.push_str(&format!("{}:{}/{interface}", self.namespace, self.name)); |
232 | 17.3k | if let Some(version) = &self.version { |
233 | 10.6k | s.push_str(&format!("@{version}")); |
234 | 10.6k | } |
235 | 17.3k | s |
236 | 17.3k | } |
237 | | |
238 | | /// Determines the "semver compatible track" for the given version. |
239 | | /// |
240 | | /// This method implements the logic from the component model where semver |
241 | | /// versions can be compatible with one another. For example versions 1.2.0 |
242 | | /// and 1.2.1 would be considered both compatible with one another because |
243 | | /// they're on the same semver compatible track. |
244 | | /// |
245 | | /// This predicate is used during |
246 | | /// [`Resolve::merge_world_imports_based_on_semver`] for example to |
247 | | /// determine whether two imports can be merged together. This is |
248 | | /// additionally used when creating components to match up imports in |
249 | | /// core wasm to imports in worlds. |
250 | 411 | pub fn version_compat_track(version: &Version) -> Version { |
251 | 411 | let mut version = version.clone(); |
252 | 411 | version.build = semver::BuildMetadata::EMPTY; |
253 | 411 | if !version.pre.is_empty() { |
254 | 372 | return version; |
255 | 39 | } |
256 | 39 | if version.major != 0 { |
257 | 33 | version.minor = 0; |
258 | 33 | version.patch = 0; |
259 | 33 | return version; |
260 | 6 | } |
261 | 6 | if version.minor != 0 { |
262 | 5 | version.patch = 0; |
263 | 5 | return version; |
264 | 1 | } |
265 | 1 | version |
266 | 411 | } |
267 | | |
268 | | /// Returns the string corresponding to |
269 | | /// [`PackageName::version_compat_track`]. This is done to match the |
270 | | /// component model's expected naming scheme of imports and exports. |
271 | 92 | pub fn version_compat_track_string(version: &Version) -> String { |
272 | 92 | let version = Self::version_compat_track(version); |
273 | 92 | if !version.pre.is_empty() { |
274 | 90 | return version.to_string(); |
275 | 2 | } |
276 | 2 | if version.major != 0 { |
277 | 2 | return format!("{}", version.major); |
278 | 0 | } |
279 | 0 | if version.minor != 0 { |
280 | 0 | return format!("{}.{}", version.major, version.minor); |
281 | 0 | } |
282 | 0 | version.to_string() |
283 | 92 | } |
284 | | } |
285 | | |
286 | | impl fmt::Display for PackageName { |
287 | 6.80k | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
288 | 6.80k | write!(f, "{}:{}", self.namespace, self.name)?; |
289 | 6.80k | if let Some(version) = &self.version { |
290 | 5.10k | write!(f, "@{version}")?; |
291 | 1.69k | } |
292 | 6.80k | Ok(()) |
293 | 6.80k | } |
294 | | } |
295 | | |
296 | | #[derive(Debug)] |
297 | | struct Error { |
298 | | span: Span, |
299 | | msg: String, |
300 | | highlighted: Option<String>, |
301 | | } |
302 | | |
303 | | impl Error { |
304 | 0 | fn new(span: Span, msg: impl Into<String>) -> Error { |
305 | 0 | Error { |
306 | 0 | span, |
307 | 0 | msg: msg.into(), |
308 | 0 | highlighted: None, |
309 | 0 | } |
310 | 0 | } Unexecuted instantiation: <wit_parser::Error>::new::<alloc::string::String> Unexecuted instantiation: <wit_parser::Error>::new::<&str> |
311 | | |
312 | | /// Highlights this error using the given source map, if the span is known. |
313 | 0 | fn highlight(&mut self, source_map: &ast::SourceMap) { |
314 | 0 | if self.highlighted.is_none() { |
315 | 0 | self.highlighted = source_map.highlight_span(self.span, &self.msg); |
316 | 0 | } |
317 | 0 | } |
318 | | } |
319 | | |
320 | | impl fmt::Display for Error { |
321 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
322 | 0 | self.highlighted.as_ref().unwrap_or(&self.msg).fmt(f) |
323 | 0 | } |
324 | | } |
325 | | |
326 | | impl core::error::Error for Error {} |
327 | | |
328 | | #[derive(Debug)] |
329 | | struct PackageNotFoundError { |
330 | | span: Span, |
331 | | requested: PackageName, |
332 | | known: Vec<PackageName>, |
333 | | highlighted: Option<String>, |
334 | | } |
335 | | |
336 | | impl PackageNotFoundError { |
337 | 0 | pub fn new(span: Span, requested: PackageName, known: Vec<PackageName>) -> Self { |
338 | 0 | Self { |
339 | 0 | span, |
340 | 0 | requested, |
341 | 0 | known, |
342 | 0 | highlighted: None, |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | | /// Highlights this error using the given source map, if the span is known. |
347 | 0 | fn highlight(&mut self, source_map: &ast::SourceMap) { |
348 | 0 | if self.highlighted.is_none() { |
349 | 0 | self.highlighted = source_map.highlight_span(self.span, &format!("{self}")); |
350 | 0 | } |
351 | 0 | } |
352 | | } |
353 | | |
354 | | impl fmt::Display for PackageNotFoundError { |
355 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
356 | 0 | if let Some(highlighted) = &self.highlighted { |
357 | 0 | return highlighted.fmt(f); |
358 | 0 | } |
359 | 0 | if self.known.is_empty() { |
360 | 0 | write!( |
361 | 0 | f, |
362 | | "package '{}' not found. no known packages.", |
363 | | self.requested |
364 | 0 | )?; |
365 | | } else { |
366 | 0 | write!( |
367 | 0 | f, |
368 | | "package '{}' not found. known packages:\n", |
369 | | self.requested |
370 | 0 | )?; |
371 | 0 | for known in self.known.iter() { |
372 | 0 | write!(f, " {known}\n")?; |
373 | | } |
374 | | } |
375 | 0 | Ok(()) |
376 | 0 | } |
377 | | } |
378 | | |
379 | | impl core::error::Error for PackageNotFoundError {} |
380 | | |
381 | | impl UnresolvedPackageGroup { |
382 | | /// Parses the given string as a wit document. |
383 | | /// |
384 | | /// The `path` argument is used for error reporting. The `contents` provided |
385 | | /// are considered to be the contents of `path`. This function does not read |
386 | | /// the filesystem. |
387 | | #[cfg(feature = "std")] |
388 | 0 | pub fn parse(path: impl AsRef<Path>, contents: &str) -> Result<UnresolvedPackageGroup> { |
389 | 0 | let path = path |
390 | 0 | .as_ref() |
391 | 0 | .to_str() |
392 | 0 | .ok_or_else(|| anyhow::anyhow!("path is not valid utf-8: {:?}", path.as_ref()))?; |
393 | 0 | Self::parse_str(path, contents) |
394 | 0 | } |
395 | | |
396 | | /// Parses the given string as a wit document. |
397 | | /// |
398 | | /// The `path` argument is used for error reporting. The `contents` provided |
399 | | /// are considered to be the contents of `path`. This function does not read |
400 | | /// the filesystem. |
401 | 5.20k | pub fn parse_str(path: &str, contents: &str) -> Result<UnresolvedPackageGroup> { |
402 | 5.20k | let mut map = SourceMap::default(); |
403 | 5.20k | map.push_str(path, contents); |
404 | 5.20k | map.parse() |
405 | 5.20k | } |
406 | | |
407 | | /// Parse a WIT package at the provided path. |
408 | | /// |
409 | | /// The path provided is inferred whether it's a file or a directory. A file |
410 | | /// is parsed with [`UnresolvedPackageGroup::parse_file`] and a directory is |
411 | | /// parsed with [`UnresolvedPackageGroup::parse_dir`]. |
412 | | #[cfg(feature = "std")] |
413 | 0 | pub fn parse_path(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> { |
414 | 0 | let path = path.as_ref(); |
415 | 0 | if path.is_dir() { |
416 | 0 | UnresolvedPackageGroup::parse_dir(path) |
417 | | } else { |
418 | 0 | UnresolvedPackageGroup::parse_file(path) |
419 | | } |
420 | 0 | } |
421 | | |
422 | | /// Parses a WIT package from the file provided. |
423 | | /// |
424 | | /// The return value represents all packages found in the WIT file which |
425 | | /// might be either one or multiple depending on the syntax used. |
426 | | #[cfg(feature = "std")] |
427 | 0 | pub fn parse_file(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> { |
428 | 0 | let path = path.as_ref(); |
429 | 0 | let contents = std::fs::read_to_string(path) |
430 | 0 | .with_context(|| format!("failed to read file {path:?}"))?; |
431 | 0 | Self::parse(path, &contents) |
432 | 0 | } |
433 | | |
434 | | /// Parses a WIT package from the directory provided. |
435 | | /// |
436 | | /// This method will look at all files under the `path` specified. All |
437 | | /// `*.wit` files are parsed and assumed to be part of the same package |
438 | | /// grouping. This is useful when a WIT package is split across multiple |
439 | | /// files. |
440 | | #[cfg(feature = "std")] |
441 | 0 | pub fn parse_dir(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> { |
442 | 0 | let path = path.as_ref(); |
443 | 0 | let mut map = SourceMap::default(); |
444 | 0 | let cx = || format!("failed to read directory {path:?}");Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::Path>::{closure#0}Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::PathBuf>::{closure#0} |
445 | 0 | for entry in path.read_dir().with_context(&cx)? { |
446 | 0 | let entry = entry.with_context(&cx)?; |
447 | 0 | let path = entry.path(); |
448 | 0 | let ty = entry.file_type().with_context(&cx)?; |
449 | 0 | if ty.is_dir() { |
450 | 0 | continue; |
451 | 0 | } |
452 | 0 | if ty.is_symlink() { |
453 | 0 | if path.is_dir() { |
454 | 0 | continue; |
455 | 0 | } |
456 | 0 | } |
457 | 0 | let filename = match path.file_name().and_then(|s| s.to_str()) {Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::Path>::{closure#1}Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::PathBuf>::{closure#1} |
458 | 0 | Some(name) => name, |
459 | 0 | None => continue, |
460 | | }; |
461 | 0 | if !filename.ends_with(".wit") { |
462 | 0 | continue; |
463 | 0 | } |
464 | 0 | map.push_file(&path)?; |
465 | | } |
466 | 0 | map.parse() |
467 | 0 | } Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::Path> Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::PathBuf> |
468 | | } |
469 | | |
470 | | #[derive(Debug, Clone)] |
471 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
472 | | pub struct World { |
473 | | /// The WIT identifier name of this world. |
474 | | pub name: String, |
475 | | |
476 | | /// All imported items into this interface, both worlds and functions. |
477 | | pub imports: IndexMap<WorldKey, WorldItem>, |
478 | | |
479 | | /// All exported items from this interface, both worlds and functions. |
480 | | pub exports: IndexMap<WorldKey, WorldItem>, |
481 | | |
482 | | /// The package that owns this world. |
483 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))] |
484 | | pub package: Option<PackageId>, |
485 | | |
486 | | /// Documentation associated with this world declaration. |
487 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
488 | | pub docs: Docs, |
489 | | |
490 | | /// Stability annotation for this world itself. |
491 | | #[cfg_attr( |
492 | | feature = "serde", |
493 | | serde(skip_serializing_if = "Stability::is_unknown") |
494 | | )] |
495 | | pub stability: Stability, |
496 | | |
497 | | /// All the included worlds from this world. Empty if this is fully resolved. |
498 | | #[cfg_attr(feature = "serde", serde(skip))] |
499 | | pub includes: Vec<WorldInclude>, |
500 | | |
501 | | /// Source span for this world. |
502 | | #[cfg_attr(feature = "serde", serde(skip))] |
503 | | pub span: Span, |
504 | | } |
505 | | |
506 | | impl World { |
507 | | /// Adjusts all spans in this world by adding the given byte offset. |
508 | 32.4k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
509 | 32.4k | self.span.adjust(offset); |
510 | 51.8k | for item in self.imports.values_mut().chain(self.exports.values_mut()) { |
511 | 51.8k | item.adjust_spans(offset); |
512 | 51.8k | } |
513 | 32.4k | for include in &mut self.includes { |
514 | 0 | include.span.adjust(offset); |
515 | 0 | } |
516 | 32.4k | } |
517 | | } |
518 | | |
519 | | #[derive(Debug, Clone)] |
520 | | pub struct IncludeName { |
521 | | /// The name of the item |
522 | | pub name: String, |
523 | | |
524 | | /// The name to be replaced with |
525 | | pub as_: String, |
526 | | } |
527 | | |
528 | | /// An entry in the `includes` list of a world, representing an `include` |
529 | | /// statement in WIT. |
530 | | #[derive(Debug, Clone)] |
531 | | pub struct WorldInclude { |
532 | | /// The stability annotation on this include. |
533 | | pub stability: Stability, |
534 | | |
535 | | /// The world being included. |
536 | | pub id: WorldId, |
537 | | |
538 | | /// Names being renamed as part of this include. |
539 | | pub names: Vec<IncludeName>, |
540 | | |
541 | | /// Source span for this include statement. |
542 | | pub span: Span, |
543 | | } |
544 | | |
545 | | /// The key to the import/export maps of a world. Either a kebab-name or a |
546 | | /// unique interface. |
547 | | #[derive(Debug, Clone, Eq)] |
548 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
549 | | #[cfg_attr(feature = "serde", serde(into = "String"))] |
550 | | pub enum WorldKey { |
551 | | /// A kebab-name. |
552 | | Name(String), |
553 | | /// An interface which is assigned no kebab-name. |
554 | | Interface(InterfaceId), |
555 | | } |
556 | | |
557 | | impl Hash for WorldKey { |
558 | 208k | fn hash<H: Hasher>(&self, hasher: &mut H) { |
559 | 208k | match self { |
560 | 188k | WorldKey::Name(s) => { |
561 | 188k | 0u8.hash(hasher); |
562 | 188k | s.as_str().hash(hasher); |
563 | 188k | } |
564 | 19.7k | WorldKey::Interface(i) => { |
565 | 19.7k | 1u8.hash(hasher); |
566 | 19.7k | i.hash(hasher); |
567 | 19.7k | } |
568 | | } |
569 | 208k | } |
570 | | } |
571 | | |
572 | | impl PartialEq for WorldKey { |
573 | 34.7k | fn eq(&self, other: &WorldKey) -> bool { |
574 | 34.7k | match (self, other) { |
575 | 23.3k | (WorldKey::Name(a), WorldKey::Name(b)) => a.as_str() == b.as_str(), |
576 | 600 | (WorldKey::Name(_), _) => false, |
577 | 10.2k | (WorldKey::Interface(a), WorldKey::Interface(b)) => a == b, |
578 | 572 | (WorldKey::Interface(_), _) => false, |
579 | | } |
580 | 34.7k | } |
581 | | } |
582 | | |
583 | | impl From<WorldKey> for String { |
584 | 0 | fn from(key: WorldKey) -> String { |
585 | 0 | match key { |
586 | 0 | WorldKey::Name(name) => name, |
587 | 0 | WorldKey::Interface(id) => format!("interface-{}", id.index()), |
588 | | } |
589 | 0 | } |
590 | | } |
591 | | |
592 | | impl WorldKey { |
593 | | /// Asserts that this is `WorldKey::Name` and returns the name. |
594 | | #[track_caller] |
595 | 9.54k | pub fn unwrap_name(self) -> String { |
596 | 9.54k | match self { |
597 | 9.54k | WorldKey::Name(name) => name, |
598 | 0 | WorldKey::Interface(_) => panic!("expected a name, found interface"), |
599 | | } |
600 | 9.54k | } |
601 | | } |
602 | | |
603 | | #[derive(Debug, Clone, PartialEq)] |
604 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
605 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
606 | | pub enum WorldItem { |
607 | | /// An interface is being imported or exported from a world, indicating that |
608 | | /// it's a namespace of functions. |
609 | | Interface { |
610 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
611 | | id: InterfaceId, |
612 | | #[cfg_attr( |
613 | | feature = "serde", |
614 | | serde(skip_serializing_if = "Stability::is_unknown") |
615 | | )] |
616 | | stability: Stability, |
617 | | #[cfg_attr(feature = "serde", serde(skip))] |
618 | | span: Span, |
619 | | }, |
620 | | |
621 | | /// A function is being directly imported or exported from this world. |
622 | | Function(Function), |
623 | | |
624 | | /// A type is being exported from this world. |
625 | | /// |
626 | | /// Note that types are never imported into worlds at this time. |
627 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_ignore_span"))] |
628 | | Type { id: TypeId, span: Span }, |
629 | | } |
630 | | |
631 | | impl WorldItem { |
632 | 31.9k | pub fn stability<'a>(&'a self, resolve: &'a Resolve) -> &'a Stability { |
633 | 31.9k | match self { |
634 | 6.31k | WorldItem::Interface { stability, .. } => stability, |
635 | 6.94k | WorldItem::Function(f) => &f.stability, |
636 | 18.7k | WorldItem::Type { id, .. } => &resolve.types[*id].stability, |
637 | | } |
638 | 31.9k | } |
639 | | |
640 | 27.9k | pub fn span(&self) -> Span { |
641 | 27.9k | match self { |
642 | 4.11k | WorldItem::Interface { span, .. } => *span, |
643 | 5.08k | WorldItem::Function(f) => f.span, |
644 | 18.7k | WorldItem::Type { span, .. } => *span, |
645 | | } |
646 | 27.9k | } |
647 | | |
648 | 51.8k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
649 | 51.8k | match self { |
650 | 10.1k | WorldItem::Function(f) => f.adjust_spans(offset), |
651 | 8.59k | WorldItem::Interface { span, .. } => span.adjust(offset), |
652 | 33.0k | WorldItem::Type { span, .. } => span.adjust(offset), |
653 | | } |
654 | 51.8k | } |
655 | | } |
656 | | |
657 | | #[derive(Debug, Clone)] |
658 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
659 | | pub struct Interface { |
660 | | /// Optionally listed name of this interface. |
661 | | /// |
662 | | /// This is `None` for inline interfaces in worlds. |
663 | | pub name: Option<String>, |
664 | | |
665 | | /// Exported types from this interface. |
666 | | /// |
667 | | /// Export names are listed within the types themselves. Note that the |
668 | | /// export name here matches the name listed in the `TypeDef`. |
669 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] |
670 | | pub types: IndexMap<String, TypeId>, |
671 | | |
672 | | /// Exported functions from this interface. |
673 | | pub functions: IndexMap<String, Function>, |
674 | | |
675 | | /// Documentation associated with this interface. |
676 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
677 | | pub docs: Docs, |
678 | | |
679 | | /// Stability attribute for this interface. |
680 | | #[cfg_attr( |
681 | | feature = "serde", |
682 | | serde(skip_serializing_if = "Stability::is_unknown") |
683 | | )] |
684 | | pub stability: Stability, |
685 | | |
686 | | /// The package that owns this interface. |
687 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))] |
688 | | pub package: Option<PackageId>, |
689 | | |
690 | | /// Source span for this interface. |
691 | | #[cfg_attr(feature = "serde", serde(skip))] |
692 | | pub span: Span, |
693 | | |
694 | | /// The interface that this one was cloned from, if any. |
695 | | /// |
696 | | /// Applicable for [`Resolve::generate_nominal_type_ids`]. |
697 | | #[cfg_attr( |
698 | | feature = "serde", |
699 | | serde( |
700 | | skip_serializing_if = "Option::is_none", |
701 | | serialize_with = "serialize_optional_id", |
702 | | ) |
703 | | )] |
704 | | pub clone_of: Option<InterfaceId>, |
705 | | } |
706 | | |
707 | | impl Interface { |
708 | | /// Adjusts all spans in this interface by adding the given byte offset. |
709 | 60.4k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
710 | 60.4k | self.span.adjust(offset); |
711 | 60.4k | for func in self.functions.values_mut() { |
712 | 24.3k | func.adjust_spans(offset); |
713 | 24.3k | } |
714 | 60.4k | } |
715 | | } |
716 | | |
717 | | #[derive(Debug, Clone, PartialEq)] |
718 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
719 | | pub struct TypeDef { |
720 | | pub name: Option<String>, |
721 | | pub kind: TypeDefKind, |
722 | | pub owner: TypeOwner, |
723 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
724 | | pub docs: Docs, |
725 | | /// Stability attribute for this type. |
726 | | #[cfg_attr( |
727 | | feature = "serde", |
728 | | serde(skip_serializing_if = "Stability::is_unknown") |
729 | | )] |
730 | | pub stability: Stability, |
731 | | /// Source span for this type. |
732 | | #[cfg_attr(feature = "serde", serde(skip))] |
733 | | pub span: Span, |
734 | | } |
735 | | |
736 | | impl TypeDef { |
737 | | /// Adjusts all spans in this type definition by adding the given byte offset. |
738 | | /// |
739 | | /// This is used when merging source maps to update spans to point to the |
740 | | /// correct location in the combined source map. |
741 | 197k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
742 | 197k | self.span.adjust(offset); |
743 | 197k | match &mut self.kind { |
744 | 5.54k | TypeDefKind::Record(r) => { |
745 | 15.0k | for field in &mut r.fields { |
746 | 15.0k | field.span.adjust(offset); |
747 | 15.0k | } |
748 | | } |
749 | 8.72k | TypeDefKind::Variant(v) => { |
750 | 29.6k | for case in &mut v.cases { |
751 | 29.6k | case.span.adjust(offset); |
752 | 29.6k | } |
753 | | } |
754 | 31.9k | TypeDefKind::Enum(e) => { |
755 | 151k | for case in &mut e.cases { |
756 | 151k | case.span.adjust(offset); |
757 | 151k | } |
758 | | } |
759 | 1.75k | TypeDefKind::Flags(f) => { |
760 | 5.06k | for flag in &mut f.flags { |
761 | 5.06k | flag.span.adjust(offset); |
762 | 5.06k | } |
763 | | } |
764 | 149k | _ => {} |
765 | | } |
766 | 197k | } |
767 | | } |
768 | | |
769 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
770 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
771 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
772 | | pub enum TypeDefKind { |
773 | | Record(Record), |
774 | | Resource, |
775 | | Handle(Handle), |
776 | | Flags(Flags), |
777 | | Tuple(Tuple), |
778 | | Variant(Variant), |
779 | | Enum(Enum), |
780 | | Option(Type), |
781 | | Result(Result_), |
782 | | List(Type), |
783 | | Map(Type, Type), |
784 | | FixedLengthList(Type, u32), |
785 | | Future(Option<Type>), |
786 | | Stream(Option<Type>), |
787 | | Type(Type), |
788 | | |
789 | | /// This represents a type of unknown structure imported from a foreign |
790 | | /// interface. |
791 | | /// |
792 | | /// This variant is only used during the creation of `UnresolvedPackage` but |
793 | | /// by the time a `Resolve` is created then this will not exist. |
794 | | Unknown, |
795 | | } |
796 | | |
797 | | impl TypeDefKind { |
798 | 0 | pub fn as_str(&self) -> &'static str { |
799 | 0 | match self { |
800 | 0 | TypeDefKind::Record(_) => "record", |
801 | 0 | TypeDefKind::Resource => "resource", |
802 | 0 | TypeDefKind::Handle(handle) => match handle { |
803 | 0 | Handle::Own(_) => "own", |
804 | 0 | Handle::Borrow(_) => "borrow", |
805 | | }, |
806 | 0 | TypeDefKind::Flags(_) => "flags", |
807 | 0 | TypeDefKind::Tuple(_) => "tuple", |
808 | 0 | TypeDefKind::Variant(_) => "variant", |
809 | 0 | TypeDefKind::Enum(_) => "enum", |
810 | 0 | TypeDefKind::Option(_) => "option", |
811 | 0 | TypeDefKind::Result(_) => "result", |
812 | 0 | TypeDefKind::List(_) => "list", |
813 | 0 | TypeDefKind::Map(_, _) => "map", |
814 | 0 | TypeDefKind::FixedLengthList(..) => "fixed-length list", |
815 | 0 | TypeDefKind::Future(_) => "future", |
816 | 0 | TypeDefKind::Stream(_) => "stream", |
817 | 0 | TypeDefKind::Type(_) => "type", |
818 | 0 | TypeDefKind::Unknown => "unknown", |
819 | | } |
820 | 0 | } |
821 | | } |
822 | | |
823 | | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
824 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
825 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
826 | | pub enum TypeOwner { |
827 | | /// This type was defined within a `world` block. |
828 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
829 | | World(WorldId), |
830 | | /// This type was defined within an `interface` block. |
831 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
832 | | Interface(InterfaceId), |
833 | | /// This type wasn't inherently defined anywhere, such as a `list<T>`, which |
834 | | /// doesn't need an owner. |
835 | | #[cfg_attr(feature = "serde", serde(untagged, serialize_with = "serialize_none"))] |
836 | | None, |
837 | | } |
838 | | |
839 | | #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] |
840 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
841 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
842 | | pub enum Handle { |
843 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
844 | | Own(TypeId), |
845 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
846 | | Borrow(TypeId), |
847 | | } |
848 | | |
849 | | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)] |
850 | | pub enum Type { |
851 | | Bool, |
852 | | U8, |
853 | | U16, |
854 | | U32, |
855 | | U64, |
856 | | S8, |
857 | | S16, |
858 | | S32, |
859 | | S64, |
860 | | F32, |
861 | | F64, |
862 | | Char, |
863 | | String, |
864 | | ErrorContext, |
865 | | Id(TypeId), |
866 | | } |
867 | | |
868 | | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
869 | | pub enum Int { |
870 | | U8, |
871 | | U16, |
872 | | U32, |
873 | | U64, |
874 | | } |
875 | | |
876 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
877 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
878 | | pub struct Record { |
879 | | pub fields: Vec<Field>, |
880 | | } |
881 | | |
882 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
883 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
884 | | pub struct Field { |
885 | | pub name: String, |
886 | | #[cfg_attr(feature = "serde", serde(rename = "type"))] |
887 | | pub ty: Type, |
888 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
889 | | pub docs: Docs, |
890 | | /// Source span for this field. |
891 | | #[cfg_attr(feature = "serde", serde(skip))] |
892 | | pub span: Span, |
893 | | } |
894 | | |
895 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
896 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
897 | | pub struct Flags { |
898 | | pub flags: Vec<Flag>, |
899 | | } |
900 | | |
901 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
902 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
903 | | pub struct Flag { |
904 | | pub name: String, |
905 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
906 | | pub docs: Docs, |
907 | | /// Source span for this flag. |
908 | | #[cfg_attr(feature = "serde", serde(skip))] |
909 | | pub span: Span, |
910 | | } |
911 | | |
912 | | #[derive(Debug, Clone, PartialEq)] |
913 | | pub enum FlagsRepr { |
914 | | U8, |
915 | | U16, |
916 | | U32(usize), |
917 | | } |
918 | | |
919 | | impl Flags { |
920 | 305 | pub fn repr(&self) -> FlagsRepr { |
921 | 305 | match self.flags.len() { |
922 | 0 | 0 => FlagsRepr::U32(0), |
923 | 305 | n if n <= 8 => FlagsRepr::U8, |
924 | 21 | n if n <= 16 => FlagsRepr::U16, |
925 | 0 | n => FlagsRepr::U32(sizealign::align_to(n, 32) / 32), |
926 | | } |
927 | 305 | } |
928 | | } |
929 | | |
930 | | impl FlagsRepr { |
931 | 18 | pub fn count(&self) -> usize { |
932 | 18 | match self { |
933 | 18 | FlagsRepr::U8 => 1, |
934 | 0 | FlagsRepr::U16 => 1, |
935 | 0 | FlagsRepr::U32(n) => *n, |
936 | | } |
937 | 18 | } |
938 | | } |
939 | | |
940 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
941 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
942 | | pub struct Tuple { |
943 | | pub types: Vec<Type>, |
944 | | } |
945 | | |
946 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
947 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
948 | | pub struct Variant { |
949 | | pub cases: Vec<Case>, |
950 | | } |
951 | | |
952 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
953 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
954 | | pub struct Case { |
955 | | pub name: String, |
956 | | #[cfg_attr(feature = "serde", serde(rename = "type"))] |
957 | | pub ty: Option<Type>, |
958 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
959 | | pub docs: Docs, |
960 | | /// Source span for this variant case. |
961 | | #[cfg_attr(feature = "serde", serde(skip))] |
962 | | pub span: Span, |
963 | | } |
964 | | |
965 | | impl Variant { |
966 | 1.80k | pub fn tag(&self) -> Int { |
967 | 1.80k | discriminant_type(self.cases.len()) |
968 | 1.80k | } |
969 | | } |
970 | | |
971 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
972 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
973 | | pub struct Enum { |
974 | | pub cases: Vec<EnumCase>, |
975 | | } |
976 | | |
977 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
978 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
979 | | pub struct EnumCase { |
980 | | pub name: String, |
981 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
982 | | pub docs: Docs, |
983 | | /// Source span for this enum case. |
984 | | #[cfg_attr(feature = "serde", serde(skip))] |
985 | | pub span: Span, |
986 | | } |
987 | | |
988 | | impl Enum { |
989 | 1.49k | pub fn tag(&self) -> Int { |
990 | 1.49k | discriminant_type(self.cases.len()) |
991 | 1.49k | } |
992 | | } |
993 | | |
994 | | /// This corresponds to the `discriminant_type` function in the Canonical ABI. |
995 | 3.30k | fn discriminant_type(num_cases: usize) -> Int { |
996 | 3.30k | match num_cases.checked_sub(1) { |
997 | 0 | None => Int::U8, |
998 | 3.30k | Some(n) if n <= u8::max_value() as usize => Int::U8, |
999 | 0 | Some(n) if n <= u16::max_value() as usize => Int::U16, |
1000 | 0 | Some(n) if n <= u32::max_value() as usize => Int::U32, |
1001 | 0 | _ => panic!("too many cases to fit in a repr"), |
1002 | | } |
1003 | 3.30k | } |
1004 | | |
1005 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
1006 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
1007 | | pub struct Result_ { |
1008 | | pub ok: Option<Type>, |
1009 | | pub err: Option<Type>, |
1010 | | } |
1011 | | |
1012 | | #[derive(Clone, Default, Debug, PartialEq, Eq, Hash)] |
1013 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
1014 | | pub struct Docs { |
1015 | | pub contents: Option<String>, |
1016 | | } |
1017 | | |
1018 | | impl Docs { |
1019 | 0 | pub fn is_empty(&self) -> bool { |
1020 | 0 | self.contents.is_none() |
1021 | 0 | } |
1022 | | } |
1023 | | |
1024 | | #[derive(Debug, Clone, PartialEq, Eq)] |
1025 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
1026 | | pub struct Param { |
1027 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "String::is_empty"))] |
1028 | | pub name: String, |
1029 | | #[cfg_attr(feature = "serde", serde(rename = "type"))] |
1030 | | pub ty: Type, |
1031 | | #[cfg_attr(feature = "serde", serde(skip))] |
1032 | | pub span: Span, |
1033 | | } |
1034 | | |
1035 | | #[derive(Debug, Clone, PartialEq, Eq)] |
1036 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
1037 | | pub struct Function { |
1038 | | pub name: String, |
1039 | | pub kind: FunctionKind, |
1040 | | pub params: Vec<Param>, |
1041 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] |
1042 | | pub result: Option<Type>, |
1043 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
1044 | | pub docs: Docs, |
1045 | | /// Stability attribute for this function. |
1046 | | #[cfg_attr( |
1047 | | feature = "serde", |
1048 | | serde(skip_serializing_if = "Stability::is_unknown") |
1049 | | )] |
1050 | | pub stability: Stability, |
1051 | | |
1052 | | /// Source span for this function. |
1053 | | #[cfg_attr(feature = "serde", serde(skip))] |
1054 | | pub span: Span, |
1055 | | } |
1056 | | |
1057 | | #[derive(Debug, Clone, PartialEq, Eq)] |
1058 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
1059 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
1060 | | pub enum FunctionKind { |
1061 | | /// A freestanding function. |
1062 | | /// |
1063 | | /// ```wit |
1064 | | /// interface foo { |
1065 | | /// the-func: func(); |
1066 | | /// } |
1067 | | /// ``` |
1068 | | Freestanding, |
1069 | | |
1070 | | /// An async freestanding function. |
1071 | | /// |
1072 | | /// ```wit |
1073 | | /// interface foo { |
1074 | | /// the-func: async func(); |
1075 | | /// } |
1076 | | /// ``` |
1077 | | AsyncFreestanding, |
1078 | | |
1079 | | /// A resource method where the first parameter is implicitly |
1080 | | /// `borrow<T>`. |
1081 | | /// |
1082 | | /// ```wit |
1083 | | /// interface foo { |
1084 | | /// resource r { |
1085 | | /// the-func: func(); |
1086 | | /// } |
1087 | | /// } |
1088 | | /// ``` |
1089 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1090 | | Method(TypeId), |
1091 | | |
1092 | | /// An async resource method where the first parameter is implicitly |
1093 | | /// `borrow<T>`. |
1094 | | /// |
1095 | | /// ```wit |
1096 | | /// interface foo { |
1097 | | /// resource r { |
1098 | | /// the-func: async func(); |
1099 | | /// } |
1100 | | /// } |
1101 | | /// ``` |
1102 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1103 | | AsyncMethod(TypeId), |
1104 | | |
1105 | | /// A static resource method. |
1106 | | /// |
1107 | | /// ```wit |
1108 | | /// interface foo { |
1109 | | /// resource r { |
1110 | | /// the-func: static func(); |
1111 | | /// } |
1112 | | /// } |
1113 | | /// ``` |
1114 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1115 | | Static(TypeId), |
1116 | | |
1117 | | /// An async static resource method. |
1118 | | /// |
1119 | | /// ```wit |
1120 | | /// interface foo { |
1121 | | /// resource r { |
1122 | | /// the-func: static async func(); |
1123 | | /// } |
1124 | | /// } |
1125 | | /// ``` |
1126 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1127 | | AsyncStatic(TypeId), |
1128 | | |
1129 | | /// A resource constructor where the return value is implicitly `own<T>`. |
1130 | | /// |
1131 | | /// ```wit |
1132 | | /// interface foo { |
1133 | | /// resource r { |
1134 | | /// constructor(); |
1135 | | /// } |
1136 | | /// } |
1137 | | /// ``` |
1138 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1139 | | Constructor(TypeId), |
1140 | | } |
1141 | | |
1142 | | impl FunctionKind { |
1143 | | /// Returns whether this is an async function or not. |
1144 | 59.8k | pub fn is_async(&self) -> bool { |
1145 | 59.8k | match self { |
1146 | | FunctionKind::Freestanding |
1147 | | | FunctionKind::Method(_) |
1148 | | | FunctionKind::Static(_) |
1149 | 26.5k | | FunctionKind::Constructor(_) => false, |
1150 | | FunctionKind::AsyncFreestanding |
1151 | | | FunctionKind::AsyncMethod(_) |
1152 | 33.3k | | FunctionKind::AsyncStatic(_) => true, |
1153 | | } |
1154 | 59.8k | } |
1155 | | |
1156 | | /// Returns the resource, if present, that this function kind refers to. |
1157 | 57.0k | pub fn resource(&self) -> Option<TypeId> { |
1158 | 57.0k | match self { |
1159 | 41.3k | FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None, |
1160 | 1.97k | FunctionKind::Method(id) |
1161 | 1.81k | | FunctionKind::Static(id) |
1162 | 1.70k | | FunctionKind::Constructor(id) |
1163 | 6.74k | | FunctionKind::AsyncMethod(id) |
1164 | 15.6k | | FunctionKind::AsyncStatic(id) => Some(*id), |
1165 | | } |
1166 | 57.0k | } |
1167 | | |
1168 | | /// Returns the resource, if present, that this function kind refers to. |
1169 | 41.4k | pub fn resource_mut(&mut self) -> Option<&mut TypeId> { |
1170 | 41.4k | match self { |
1171 | 32.7k | FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None, |
1172 | 1.19k | FunctionKind::Method(id) |
1173 | 999 | | FunctionKind::Static(id) |
1174 | 987 | | FunctionKind::Constructor(id) |
1175 | 3.56k | | FunctionKind::AsyncMethod(id) |
1176 | 8.65k | | FunctionKind::AsyncStatic(id) => Some(id), |
1177 | | } |
1178 | 41.4k | } |
1179 | | } |
1180 | | |
1181 | | /// Possible forms of name mangling that are supported by this crate. |
1182 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1183 | | pub enum Mangling { |
1184 | | /// The "standard" component model mangling format for 32-bit linear |
1185 | | /// memories. This is specified in WebAssembly/component-model#378 |
1186 | | Standard32, |
1187 | | |
1188 | | /// The "legacy" name mangling supported in versions 218-and-prior for this |
1189 | | /// crate. This is the original support for how components were created from |
1190 | | /// core wasm modules and this does not correspond to any standard. This is |
1191 | | /// preserved for now while tools transition to the new scheme. |
1192 | | Legacy, |
1193 | | } |
1194 | | |
1195 | | impl core::str::FromStr for Mangling { |
1196 | | type Err = anyhow::Error; |
1197 | | |
1198 | 0 | fn from_str(s: &str) -> Result<Mangling> { |
1199 | 0 | match s { |
1200 | 0 | "legacy" => Ok(Mangling::Legacy), |
1201 | 0 | "standard32" => Ok(Mangling::Standard32), |
1202 | | _ => { |
1203 | 0 | bail!( |
1204 | | "unknown name mangling `{s}`, \ |
1205 | | supported values are `legacy` or `standard32`" |
1206 | | ) |
1207 | | } |
1208 | | } |
1209 | 0 | } |
1210 | | } |
1211 | | |
1212 | | /// Possible lift/lower ABI choices supported when mangling names. |
1213 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1214 | | pub enum LiftLowerAbi { |
1215 | | /// Both imports and exports will use the synchronous ABI. |
1216 | | Sync, |
1217 | | |
1218 | | /// Both imports and exports will use the async ABI (with a callback for |
1219 | | /// each export). |
1220 | | AsyncCallback, |
1221 | | |
1222 | | /// Both imports and exports will use the async ABI (with no callbacks for |
1223 | | /// exports). |
1224 | | AsyncStackful, |
1225 | | } |
1226 | | |
1227 | | impl LiftLowerAbi { |
1228 | 2.20k | fn import_prefix(self) -> &'static str { |
1229 | 2.20k | match self { |
1230 | 1.92k | Self::Sync => "", |
1231 | 281 | Self::AsyncCallback | Self::AsyncStackful => "[async-lower]", |
1232 | | } |
1233 | 2.20k | } |
1234 | | |
1235 | | /// Get the import [`AbiVariant`] corresponding to this [`LiftLowerAbi`] |
1236 | 1.75k | pub fn import_variant(self) -> AbiVariant { |
1237 | 1.75k | match self { |
1238 | 1.46k | Self::Sync => AbiVariant::GuestImport, |
1239 | 281 | Self::AsyncCallback | Self::AsyncStackful => AbiVariant::GuestImportAsync, |
1240 | | } |
1241 | 1.75k | } |
1242 | | |
1243 | 2.05k | fn export_prefix(self) -> &'static str { |
1244 | 2.05k | match self { |
1245 | 1.64k | Self::Sync => "", |
1246 | 339 | Self::AsyncCallback => "[async-lift]", |
1247 | 71 | Self::AsyncStackful => "[async-lift-stackful]", |
1248 | | } |
1249 | 2.05k | } |
1250 | | |
1251 | | /// Get the export [`AbiVariant`] corresponding to this [`LiftLowerAbi`] |
1252 | 1.02k | pub fn export_variant(self) -> AbiVariant { |
1253 | 1.02k | match self { |
1254 | 795 | Self::Sync => AbiVariant::GuestExport, |
1255 | 164 | Self::AsyncCallback => AbiVariant::GuestExportAsync, |
1256 | 64 | Self::AsyncStackful => AbiVariant::GuestExportAsyncStackful, |
1257 | | } |
1258 | 1.02k | } |
1259 | | } |
1260 | | |
1261 | | /// Combination of [`Mangling`] and [`LiftLowerAbi`]. |
1262 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1263 | | pub enum ManglingAndAbi { |
1264 | | /// See [`Mangling::Standard32`]. |
1265 | | /// |
1266 | | /// As of this writing, the standard name mangling only supports the |
1267 | | /// synchronous ABI. |
1268 | | Standard32, |
1269 | | |
1270 | | /// See [`Mangling::Legacy`] and [`LiftLowerAbi`]. |
1271 | | Legacy(LiftLowerAbi), |
1272 | | } |
1273 | | |
1274 | | impl ManglingAndAbi { |
1275 | | /// Get the import [`AbiVariant`] corresponding to this [`ManglingAndAbi`] |
1276 | 1.85k | pub fn import_variant(self) -> AbiVariant { |
1277 | 1.85k | match self { |
1278 | 103 | Self::Standard32 => AbiVariant::GuestImport, |
1279 | 1.75k | Self::Legacy(abi) => abi.import_variant(), |
1280 | | } |
1281 | 1.85k | } |
1282 | | |
1283 | | /// Get the export [`AbiVariant`] corresponding to this [`ManglingAndAbi`] |
1284 | 1.12k | pub fn export_variant(self) -> AbiVariant { |
1285 | 1.12k | match self { |
1286 | 104 | Self::Standard32 => AbiVariant::GuestExport, |
1287 | 1.02k | Self::Legacy(abi) => abi.export_variant(), |
1288 | | } |
1289 | 1.12k | } |
1290 | | |
1291 | | /// Switch the ABI to be sync if it's async. |
1292 | 513 | pub fn sync(self) -> Self { |
1293 | 456 | match self { |
1294 | 421 | Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => self, |
1295 | | Self::Legacy(LiftLowerAbi::AsyncCallback) |
1296 | 92 | | Self::Legacy(LiftLowerAbi::AsyncStackful) => Self::Legacy(LiftLowerAbi::Sync), |
1297 | | } |
1298 | 513 | } |
1299 | | |
1300 | | /// Returns whether this is an async ABI |
1301 | 7.08k | pub fn is_async(&self) -> bool { |
1302 | 6.65k | match self { |
1303 | 5.97k | Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => false, |
1304 | | Self::Legacy(LiftLowerAbi::AsyncCallback) |
1305 | 1.11k | | Self::Legacy(LiftLowerAbi::AsyncStackful) => true, |
1306 | | } |
1307 | 7.08k | } |
1308 | | |
1309 | 228 | pub fn mangling(&self) -> Mangling { |
1310 | 228 | match self { |
1311 | 0 | Self::Standard32 => Mangling::Standard32, |
1312 | 228 | Self::Legacy(_) => Mangling::Legacy, |
1313 | | } |
1314 | 228 | } |
1315 | | } |
1316 | | |
1317 | | impl Function { |
1318 | | /// Adjusts all spans in this function by adding the given byte offset. |
1319 | 34.4k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
1320 | 34.4k | self.span.adjust(offset); |
1321 | 79.1k | for param in &mut self.params { |
1322 | 79.1k | param.span.adjust(offset); |
1323 | 79.1k | } |
1324 | 34.4k | } |
1325 | | |
1326 | 10.4k | pub fn item_name(&self) -> &str { |
1327 | 10.4k | match &self.kind { |
1328 | 8.75k | FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &self.name, |
1329 | | FunctionKind::Method(_) |
1330 | | | FunctionKind::Static(_) |
1331 | | | FunctionKind::AsyncMethod(_) |
1332 | 1.73k | | FunctionKind::AsyncStatic(_) => &self.name[self.name.find('.').unwrap() + 1..], |
1333 | 0 | FunctionKind::Constructor(_) => "constructor", |
1334 | | } |
1335 | 10.4k | } |
1336 | | |
1337 | | /// Returns an iterator over the types used in parameters and results. |
1338 | | /// |
1339 | | /// Note that this iterator is not transitive, it only iterates over the |
1340 | | /// direct references to types that this function has. |
1341 | 2.49k | pub fn parameter_and_result_types(&self) -> impl Iterator<Item = Type> + '_ { |
1342 | 2.49k | self.params.iter().map(|p| p.ty).chain(self.result) |
1343 | 2.49k | } |
1344 | | |
1345 | | /// Gets the core export name for this function. |
1346 | 0 | pub fn standard32_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { |
1347 | 0 | self.core_export_name(interface, Mangling::Standard32) |
1348 | 0 | } |
1349 | | |
1350 | 13.7k | pub fn legacy_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { |
1351 | 13.7k | self.core_export_name(interface, Mangling::Legacy) |
1352 | 13.7k | } |
1353 | | /// Gets the core export name for this function. |
1354 | 13.7k | pub fn core_export_name<'a>( |
1355 | 13.7k | &'a self, |
1356 | 13.7k | interface: Option<&str>, |
1357 | 13.7k | mangling: Mangling, |
1358 | 13.7k | ) -> Cow<'a, str> { |
1359 | 13.7k | match interface { |
1360 | 12.4k | Some(interface) => match mangling { |
1361 | 0 | Mangling::Standard32 => Cow::Owned(format!("cm32p2|{interface}|{}", self.name)), |
1362 | 12.4k | Mangling::Legacy => Cow::Owned(format!("{interface}#{}", self.name)), |
1363 | | }, |
1364 | 1.29k | None => match mangling { |
1365 | 0 | Mangling::Standard32 => Cow::Owned(format!("cm32p2||{}", self.name)), |
1366 | 1.29k | Mangling::Legacy => Cow::Borrowed(&self.name), |
1367 | | }, |
1368 | | } |
1369 | 13.7k | } |
1370 | | /// Collect any future and stream types appearing in the signature of this |
1371 | | /// function by doing a depth-first search over the parameter types and then |
1372 | | /// the result types. |
1373 | | /// |
1374 | | /// For example, given the WIT function `foo: func(x: future<future<u32>>, |
1375 | | /// y: u32) -> stream<u8>`, we would return `[future<u32>, |
1376 | | /// future<future<u32>>, stream<u8>]`. |
1377 | | /// |
1378 | | /// This may be used by binding generators to refer to specific `future` and |
1379 | | /// `stream` types when importing canonical built-ins such as `stream.new`, |
1380 | | /// `future.read`, etc. Using the example above, the import |
1381 | | /// `[future-new-0]foo` would indicate a call to `future.new` for the type |
1382 | | /// `future<u32>`. Likewise, `[future-new-1]foo` would indicate a call to |
1383 | | /// `future.new` for `future<future<u32>>`, and `[stream-new-2]foo` would |
1384 | | /// indicate a call to `stream.new` for `stream<u8>`. |
1385 | 1.77k | pub fn find_futures_and_streams(&self, resolve: &Resolve) -> Vec<TypeId> { |
1386 | 1.77k | let mut results = Vec::new(); |
1387 | 4.74k | for param in self.params.iter() { |
1388 | 4.74k | find_futures_and_streams(resolve, param.ty, &mut results); |
1389 | 4.74k | } |
1390 | 1.77k | if let Some(ty) = self.result { |
1391 | 1.60k | find_futures_and_streams(resolve, ty, &mut results); |
1392 | 1.60k | } |
1393 | 1.77k | results |
1394 | 1.77k | } |
1395 | | |
1396 | | /// Check if this function is a resource constructor in shorthand form. |
1397 | | /// I.e. without an explicit return type annotation. |
1398 | 11.5k | pub fn is_constructor_shorthand(&self, resolve: &Resolve) -> bool { |
1399 | 11.5k | let FunctionKind::Constructor(containing_resource_id) = self.kind else { |
1400 | 11.2k | return false; |
1401 | | }; |
1402 | | |
1403 | 242 | let Some(Type::Id(id)) = &self.result else { |
1404 | 0 | return false; |
1405 | | }; |
1406 | | |
1407 | 242 | let TypeDefKind::Handle(Handle::Own(returned_resource_id)) = resolve.types[*id].kind else { |
1408 | 0 | return false; |
1409 | | }; |
1410 | | |
1411 | 242 | return containing_resource_id == returned_resource_id; |
1412 | 11.5k | } |
1413 | | |
1414 | | /// Returns the `module`, `name`, and signature to use when importing this |
1415 | | /// function's `task.return` intrinsic using the `mangling` specified. |
1416 | 228 | pub fn task_return_import( |
1417 | 228 | &self, |
1418 | 228 | resolve: &Resolve, |
1419 | 228 | interface: Option<&WorldKey>, |
1420 | 228 | mangling: Mangling, |
1421 | 228 | ) -> (String, String, abi::WasmSignature) { |
1422 | 228 | match mangling { |
1423 | 0 | Mangling::Standard32 => todo!(), |
1424 | 228 | Mangling::Legacy => {} |
1425 | | } |
1426 | | // For exported async functions, generate a `task.return` intrinsic. |
1427 | 228 | let module = match interface { |
1428 | 175 | Some(key) => format!("[export]{}", resolve.name_world_key(key)), |
1429 | 53 | None => "[export]$root".to_string(), |
1430 | | }; |
1431 | 228 | let name = format!("[task-return]{}", self.name); |
1432 | | |
1433 | 228 | let mut func_tmp = self.clone(); |
1434 | 228 | func_tmp.params = Vec::new(); |
1435 | 228 | func_tmp.result = None; |
1436 | 228 | if let Some(ty) = self.result { |
1437 | 181 | func_tmp.params.push(Param { |
1438 | 181 | name: "x".to_string(), |
1439 | 181 | ty, |
1440 | 181 | span: Default::default(), |
1441 | 181 | }); |
1442 | 181 | } |
1443 | 228 | let sig = resolve.wasm_signature(AbiVariant::GuestImport, &func_tmp); |
1444 | 228 | (module, name, sig) |
1445 | 228 | } |
1446 | | |
1447 | | // push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func); |
1448 | | } |
1449 | | |
1450 | 25.3k | fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeId>) { |
1451 | 25.3k | let Type::Id(id) = ty else { |
1452 | 8.45k | return; |
1453 | | }; |
1454 | | |
1455 | 16.8k | match &resolve.types[id].kind { |
1456 | | TypeDefKind::Resource |
1457 | | | TypeDefKind::Handle(_) |
1458 | | | TypeDefKind::Flags(_) |
1459 | 177 | | TypeDefKind::Enum(_) => {} |
1460 | 19 | TypeDefKind::Record(r) => { |
1461 | 19 | for Field { ty, .. } in &r.fields { |
1462 | 19 | find_futures_and_streams(resolve, *ty, results); |
1463 | 19 | } |
1464 | | } |
1465 | 1.29k | TypeDefKind::Tuple(t) => { |
1466 | 3.51k | for ty in &t.types { |
1467 | 3.51k | find_futures_and_streams(resolve, *ty, results); |
1468 | 3.51k | } |
1469 | | } |
1470 | 2 | TypeDefKind::Variant(v) => { |
1471 | 10 | for Case { ty, .. } in &v.cases { |
1472 | 10 | if let Some(ty) = ty { |
1473 | 10 | find_futures_and_streams(resolve, *ty, results); |
1474 | 10 | } |
1475 | | } |
1476 | | } |
1477 | 381 | TypeDefKind::Option(ty) |
1478 | 163 | | TypeDefKind::List(ty) |
1479 | 134 | | TypeDefKind::FixedLengthList(ty, ..) |
1480 | 685 | | TypeDefKind::Type(ty) => { |
1481 | 685 | find_futures_and_streams(resolve, *ty, results); |
1482 | 685 | } |
1483 | 0 | TypeDefKind::Map(k, v) => { |
1484 | 0 | find_futures_and_streams(resolve, *k, results); |
1485 | 0 | find_futures_and_streams(resolve, *v, results); |
1486 | 0 | } |
1487 | 528 | TypeDefKind::Result(r) => { |
1488 | 528 | if let Some(ty) = r.ok { |
1489 | 480 | find_futures_and_streams(resolve, ty, results); |
1490 | 480 | } |
1491 | 528 | if let Some(ty) = r.err { |
1492 | 253 | find_futures_and_streams(resolve, ty, results); |
1493 | 275 | } |
1494 | | } |
1495 | 922 | TypeDefKind::Future(ty) => { |
1496 | 922 | if let Some(ty) = ty { |
1497 | 772 | find_futures_and_streams(resolve, *ty, results); |
1498 | 772 | } |
1499 | 922 | results.push(id); |
1500 | | } |
1501 | 13.2k | TypeDefKind::Stream(ty) => { |
1502 | 13.2k | if let Some(ty) = ty { |
1503 | 13.2k | find_futures_and_streams(resolve, *ty, results); |
1504 | 13.2k | } |
1505 | 13.2k | results.push(id); |
1506 | | } |
1507 | 0 | TypeDefKind::Unknown => unreachable!(), |
1508 | | } |
1509 | 25.3k | } |
1510 | | |
1511 | | /// Representation of the stability attributes associated with a world, |
1512 | | /// interface, function, or type. |
1513 | | /// |
1514 | | /// This is added for WebAssembly/component-model#332 where @since and @unstable |
1515 | | /// annotations were added to WIT. |
1516 | | /// |
1517 | | /// The order of the of enum values is significant since it is used with Ord and PartialOrd |
1518 | | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] |
1519 | | #[cfg_attr(feature = "serde", derive(serde_derive::Deserialize, Serialize))] |
1520 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
1521 | | pub enum Stability { |
1522 | | /// This item does not have either `@since` or `@unstable`. |
1523 | | Unknown, |
1524 | | |
1525 | | /// `@unstable(feature = foo)` |
1526 | | /// |
1527 | | /// This item is explicitly tagged `@unstable`. A feature name is listed and |
1528 | | /// this item is excluded by default in `Resolve` unless explicitly enabled. |
1529 | | Unstable { |
1530 | | feature: String, |
1531 | | #[cfg_attr( |
1532 | | feature = "serde", |
1533 | | serde( |
1534 | | skip_serializing_if = "Option::is_none", |
1535 | | default, |
1536 | | serialize_with = "serialize_optional_version", |
1537 | | deserialize_with = "deserialize_optional_version" |
1538 | | ) |
1539 | | )] |
1540 | | deprecated: Option<Version>, |
1541 | | }, |
1542 | | |
1543 | | /// `@since(version = 1.2.3)` |
1544 | | /// |
1545 | | /// This item is explicitly tagged with `@since` as stable since the |
1546 | | /// specified version. This may optionally have a feature listed as well. |
1547 | | Stable { |
1548 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_version"))] |
1549 | | #[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_version"))] |
1550 | | since: Version, |
1551 | | #[cfg_attr( |
1552 | | feature = "serde", |
1553 | | serde( |
1554 | | skip_serializing_if = "Option::is_none", |
1555 | | default, |
1556 | | serialize_with = "serialize_optional_version", |
1557 | | deserialize_with = "deserialize_optional_version" |
1558 | | ) |
1559 | | )] |
1560 | | deprecated: Option<Version>, |
1561 | | }, |
1562 | | } |
1563 | | |
1564 | | impl Stability { |
1565 | | /// Returns whether this is `Stability::Unknown`. |
1566 | 151k | pub fn is_unknown(&self) -> bool { |
1567 | 151k | matches!(self, Stability::Unknown) |
1568 | 151k | } |
1569 | | |
1570 | 0 | pub fn is_stable(&self) -> bool { |
1571 | 0 | matches!(self, Stability::Stable { .. }) |
1572 | 0 | } |
1573 | | } |
1574 | | |
1575 | | impl Default for Stability { |
1576 | 289k | fn default() -> Stability { |
1577 | 289k | Stability::Unknown |
1578 | 289k | } |
1579 | | } |
1580 | | |
1581 | | #[cfg(test)] |
1582 | | mod test { |
1583 | | use super::*; |
1584 | | use alloc::vec; |
1585 | | |
1586 | | #[test] |
1587 | | fn test_discriminant_type() { |
1588 | | assert_eq!(discriminant_type(1), Int::U8); |
1589 | | assert_eq!(discriminant_type(0x100), Int::U8); |
1590 | | assert_eq!(discriminant_type(0x101), Int::U16); |
1591 | | assert_eq!(discriminant_type(0x10000), Int::U16); |
1592 | | assert_eq!(discriminant_type(0x10001), Int::U32); |
1593 | | if let Ok(num_cases) = usize::try_from(0x100000000_u64) { |
1594 | | assert_eq!(discriminant_type(num_cases), Int::U32); |
1595 | | } |
1596 | | } |
1597 | | |
1598 | | #[test] |
1599 | | fn test_find_futures_and_streams() { |
1600 | | let mut resolve = Resolve::default(); |
1601 | | let t0 = resolve.types.alloc(TypeDef { |
1602 | | name: None, |
1603 | | kind: TypeDefKind::Future(Some(Type::U32)), |
1604 | | owner: TypeOwner::None, |
1605 | | docs: Docs::default(), |
1606 | | stability: Stability::Unknown, |
1607 | | span: Default::default(), |
1608 | | }); |
1609 | | let t1 = resolve.types.alloc(TypeDef { |
1610 | | name: None, |
1611 | | kind: TypeDefKind::Future(Some(Type::Id(t0))), |
1612 | | owner: TypeOwner::None, |
1613 | | docs: Docs::default(), |
1614 | | stability: Stability::Unknown, |
1615 | | span: Default::default(), |
1616 | | }); |
1617 | | let t2 = resolve.types.alloc(TypeDef { |
1618 | | name: None, |
1619 | | kind: TypeDefKind::Stream(Some(Type::U32)), |
1620 | | owner: TypeOwner::None, |
1621 | | docs: Docs::default(), |
1622 | | stability: Stability::Unknown, |
1623 | | span: Default::default(), |
1624 | | }); |
1625 | | let found = Function { |
1626 | | name: "foo".into(), |
1627 | | kind: FunctionKind::Freestanding, |
1628 | | params: vec![ |
1629 | | Param { |
1630 | | name: "p1".into(), |
1631 | | ty: Type::Id(t1), |
1632 | | span: Default::default(), |
1633 | | }, |
1634 | | Param { |
1635 | | name: "p2".into(), |
1636 | | ty: Type::U32, |
1637 | | span: Default::default(), |
1638 | | }, |
1639 | | ], |
1640 | | result: Some(Type::Id(t2)), |
1641 | | docs: Docs::default(), |
1642 | | stability: Stability::Unknown, |
1643 | | span: Default::default(), |
1644 | | } |
1645 | | .find_futures_and_streams(&resolve); |
1646 | | assert_eq!(3, found.len()); |
1647 | | assert_eq!(t0, found[0]); |
1648 | | assert_eq!(t1, found[1]); |
1649 | | assert_eq!(t2, found[2]); |
1650 | | } |
1651 | | } |