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