1use crate::analysis::{
2 analysed_type, AnalysedExport, AnalysedFunction, AnalysedFunctionParameter,
3 AnalysedFunctionResult, AnalysedInstance, AnalysedResourceId, AnalysedResourceMode,
4 AnalysedType, AnalysisFailure, AnalysisResult, AnalysisWarning,
5 InterfaceCouldNotBeAnalyzedWarning, NameOptionTypePair, NameTypePair, TypeHandle,
6};
7use crate::metadata::Producers;
8use itertools::Itertools;
9use std::cell::RefCell;
10use std::collections::HashMap;
11use std::fmt::Debug;
12use std::path::Path;
13use std::rc::Rc;
14use std::str::FromStr;
15use std::sync::{Arc, Mutex};
16use wasm_metadata::{Payload, Version};
17use wasmparser::KnownCustom;
18use wit_parser::decoding::DecodedWasm;
19use wit_parser::{
20 Function, Handle, Interface, PackageName, Resolve, Type, TypeDef, TypeDefKind, WorldItem,
21};
22use wit_parser::{TypeId, TypeOwner as WitParserTypeOwner};
23
24pub struct WitAnalysisContext {
25 wasm: DecodedWasm,
26 metadata_name: Option<String>,
27 metadata_version: Option<Version>,
28 resource_ids: Rc<RefCell<HashMap<TypeId, AnalysedResourceId>>>,
29 warnings: Rc<RefCell<Vec<AnalysisWarning>>>,
30 linear_memories: Vec<wasmparser::MemoryType>,
31 producers: Vec<Producers>,
32}
33
34impl WitAnalysisContext {
35 pub fn new(component_bytes: &[u8]) -> AnalysisResult<WitAnalysisContext> {
36 let wasm = wit_parser::decoding::decode(component_bytes).map_err(|err| {
37 AnalysisFailure::failed(format!("Failed to decode WASM component: {err:#}"))
38 })?;
39 let payload = Payload::from_binary(component_bytes).map_err(|err| {
40 AnalysisFailure::failed(format!("Failed to decode WASM component metadata: {err:#}"))
41 })?;
42 let metadata = payload.metadata();
43
44 let (linear_memories, producers) = Self::extract_additional_information(component_bytes)?;
45
46 Ok(Self {
47 wasm,
48 metadata_name: metadata.name.clone(),
49 metadata_version: metadata.version.clone(),
50 resource_ids: Rc::new(RefCell::new(HashMap::new())),
51 warnings: Rc::new(RefCell::new(Vec::new())),
52 linear_memories,
53 producers,
54 })
55 }
56
57 pub fn get_top_level_exports(&self) -> AnalysisResult<Vec<AnalysedExport>> {
60 let package_id = self.wasm.package();
61 let resolve = self.wasm.resolve();
62
63 let root_package =
64 AnalysisFailure::fail_on_missing(resolve.packages.get(package_id), "root package")?;
65
66 if root_package.worlds.len() > 1 {
67 Err(AnalysisFailure::failed(
68 "The component's root package must contains a single world",
69 ))
70 } else if root_package.worlds.is_empty() {
71 Err(AnalysisFailure::failed(
72 "The component's root package must contain a world",
73 ))
74 } else {
75 let (_world_name, world_id) = root_package.worlds.iter().next().unwrap();
76 let world = AnalysisFailure::fail_on_missing(resolve.worlds.get(*world_id), "world")?;
77
78 let mut result = Vec::new();
79 for (world_key, world_item) in &world.exports {
80 let world_name = resolve.name_world_key(world_key);
81 match world_item {
82 WorldItem::Interface { id, .. } => {
83 let interface = AnalysisFailure::fail_on_missing(
84 resolve.interfaces.get(*id),
85 "interface",
86 )?;
87
88 match self.analyse_interface(interface) {
89 Ok(instance) => result.push(AnalysedExport::Instance(instance)),
90 Err(failure) => {
91 self.warning(AnalysisWarning::InterfaceCouldNotBeAnalyzed(
92 InterfaceCouldNotBeAnalyzedWarning {
93 name: world_name,
94 failure,
95 },
96 ))
97 }
98 }
99 }
100 WorldItem::Function(function) => {
101 result.push(AnalysedExport::Function(self.analyse_function(function)?));
102 }
103 WorldItem::Type(_) => {}
104 }
105 }
106
107 Ok(result)
108 }
109 }
110
111 pub fn linear_memories(&self) -> &[wasmparser::MemoryType] {
113 &self.linear_memories
114 }
115
116 pub fn producers(&self) -> &[Producers] {
117 &self.producers
118 }
119
120 pub fn root_package_name(&self) -> Option<PackageName> {
127 self.metadata_name.as_ref().and_then(|name| {
128 let parts = name.split(':').collect::<Vec<_>>();
129
130 if parts.len() == 2 {
131 let namespace = parts[0].to_string();
132 let name = parts[1].to_string();
133 let version = self
134 .metadata_version
135 .as_ref()
136 .and_then(|version| semver::Version::from_str(&version.to_string()).ok());
137
138 Some(PackageName {
139 namespace,
140 name,
141 version,
142 })
143 } else {
144 None
145 }
146 })
147 }
148
149 pub fn serialized_interface_only(&self) -> AnalysisResult<Vec<u8>> {
151 let decoded_package = self.wasm.package();
152 let bytes = wit_component::encode(self.wasm.resolve(), decoded_package).map_err(|err| {
153 AnalysisFailure::failed(format!(
154 "Failed to encode WASM component interface: {err:#}"
155 ))
156 })?;
157 Ok(bytes)
158 }
159
160 pub fn warnings(&self) -> Vec<AnalysisWarning> {
161 self.warnings.borrow().clone()
162 }
163
164 fn warning(&self, warning: AnalysisWarning) {
165 self.warnings.borrow_mut().push(warning);
166 }
167
168 fn analyse_function(&self, function: &Function) -> AnalysisResult<AnalysedFunction> {
169 Ok(AnalysedFunction {
170 name: function.name.clone(),
171 parameters: function
172 .params
173 .iter()
174 .map(|(name, typ)| {
175 typ.to_analysed_type(self.wasm.resolve(), self)
176 .map_err(|err| {
177 AnalysisFailure::failed(format!(
178 "Failed to decode function ({}) parameter ({}) type: {}",
179 function.name, name, err
180 ))
181 })
182 .map(|typ| AnalysedFunctionParameter {
183 name: name.clone(),
184 typ,
185 })
186 })
187 .collect::<Result<Vec<_>, _>>()?,
188 result: function
189 .result
190 .map(|typ| {
191 typ.to_analysed_type(self.wasm.resolve(), self)
192 .map_err(|err| {
193 AnalysisFailure::failed(format!(
194 "Failed to decode function ({}) result type: {}",
195 function.name, err
196 ))
197 })
198 .map(|typ| AnalysedFunctionResult { typ })
199 })
200 .transpose()?,
201 })
202 }
203
204 fn analyse_interface(&self, interface: &Interface) -> AnalysisResult<AnalysedInstance> {
205 let mut functions = Vec::new();
206 for (_, function) in &interface.functions {
207 functions.push(self.analyse_function(function)?);
208 }
209 let interface_name =
210 AnalysisFailure::fail_on_missing(interface.name.clone(), "interface name")?;
211 let package_id = AnalysisFailure::fail_on_missing(interface.package, "interface package")?;
212 let package = AnalysisFailure::fail_on_missing(
213 self.wasm.resolve().packages.get(package_id),
214 "interface package",
215 )?;
216
217 Ok(AnalysedInstance {
218 name: package.name.interface_id(&interface_name),
219 functions,
220 })
221 }
222
223 fn extract_additional_information(
224 component_bytes: &[u8],
225 ) -> AnalysisResult<(Vec<wasmparser::MemoryType>, Vec<Producers>)> {
226 let mut memories = Vec::new();
227 let mut producers = Vec::new();
228
229 for payload in wasmparser::Parser::new(0).parse_all(component_bytes) {
230 let payload = payload.map_err(|err| AnalysisFailure::failed(err.to_string()))?;
231 match payload {
232 wasmparser::Payload::MemorySection(reader) => {
233 for memory_type in reader {
234 memories.push(
235 memory_type.map_err(|err| AnalysisFailure::failed(err.to_string()))?,
236 );
237 }
238 }
239 wasmparser::Payload::CustomSection(reader) => {
240 if let KnownCustom::Producers(_) = reader.as_known() {
241 let parsed_producers = wasm_metadata::Producers::from_bytes(
242 reader.data(),
243 reader.data_offset(),
244 )
245 .map_err(|err| AnalysisFailure::failed(err.to_string()))?;
246 producers.push(parsed_producers.into());
247 }
248 }
249 _ => {}
250 }
251 }
252
253 Ok((memories, producers))
254 }
255}
256
257impl GetResourceId for WitAnalysisContext {
258 fn get_resource_id(&self, type_id: TypeId) -> Option<AnalysedResourceId> {
259 let new_unique_id = self.resource_ids.borrow().len() as u64;
260 let mut resource_ids = self.resource_ids.borrow_mut();
261
262 Some(
263 *resource_ids
264 .entry(type_id)
265 .or_insert_with(|| AnalysedResourceId(new_unique_id)),
266 )
267 }
268}
269
270pub trait GetResourceId {
271 fn get_resource_id(&self, type_id: TypeId) -> Option<AnalysedResourceId>;
272}
273
274pub struct ResourcesNotSupported;
275
276impl GetResourceId for ResourcesNotSupported {
277 fn get_resource_id(&self, _type_id: TypeId) -> Option<AnalysedResourceId> {
278 None
279 }
280}
281
282pub trait ToAnalysedType {
290 fn to_analysed_type(
291 &self,
292 resolve: &Resolve,
293 resource_map: &impl GetResourceId,
294 ) -> Result<AnalysedType, String>;
295}
296
297impl ToAnalysedType for TypeDef {
298 fn to_analysed_type(
299 &self,
300 resolve: &Resolve,
301 resource_map: &impl GetResourceId,
302 ) -> Result<AnalysedType, String> {
303 match &self.kind {
304 TypeDefKind::Record(record) => Ok(analysed_type::record(
305 record
306 .fields
307 .iter()
308 .map(|field| {
309 field
310 .ty
311 .to_analysed_type(resolve, resource_map)
312 .map(|typ| NameTypePair {
313 name: field.name.clone(),
314 typ,
315 })
316 })
317 .collect::<Result<_, _>>()?,
318 )
319 .with_optional_name(self.name.clone())
320 .with_optional_owner(get_owner_name(resolve, &self.owner))),
321 TypeDefKind::Resource => {
322 Err("to_analysed_type not implemented for resource type".to_string())
323 }
324
325 TypeDefKind::Handle(handle) => match handle {
326 Handle::Own(type_id) => match resource_map.get_resource_id(*type_id) {
327 Some(resource_id) => Ok(AnalysedType::Handle(TypeHandle {
328 resource_id,
329 mode: AnalysedResourceMode::Owned,
330 name: self.name.clone(),
331 owner: None,
332 })
333 .with_optional_name(get_type_name(resolve, type_id))
334 .with_optional_owner(get_type_owner(resolve, type_id))),
335 None => Err("to_analysed_type not implemented for handle type".to_string()),
336 },
337 Handle::Borrow(type_id) => match resource_map.get_resource_id(*type_id) {
338 Some(resource_id) => Ok(AnalysedType::Handle(TypeHandle {
339 resource_id,
340 mode: AnalysedResourceMode::Borrowed,
341 name: self.name.clone(),
342 owner: None,
343 })
344 .with_optional_name(get_type_name(resolve, type_id))
345 .with_optional_owner(get_type_owner(resolve, type_id))),
346 None => Err("to_analysed_type not implemented for handle type".to_string()),
347 },
348 },
349 TypeDefKind::Flags(flag) => Ok(analysed_type::flags(
350 &flag
351 .flags
352 .iter()
353 .map(|flag| flag.name.as_str())
354 .collect::<Vec<_>>(),
355 )
356 .with_optional_name(self.name.clone())
357 .with_optional_owner(get_owner_name(resolve, &self.owner))),
358 TypeDefKind::Tuple(tuple) => Ok(analysed_type::tuple(
359 tuple
360 .types
361 .iter()
362 .map(|typ| typ.to_analysed_type(resolve, resource_map))
363 .collect::<Result<_, _>>()?,
364 )
365 .with_optional_name(self.name.clone())
366 .with_optional_owner(get_owner_name(resolve, &self.owner))),
367 TypeDefKind::Variant(variant) => Ok(analysed_type::variant(
368 variant
369 .cases
370 .iter()
371 .map(|case| {
372 case.ty
373 .map(|ty| ty.to_analysed_type(resolve, resource_map))
374 .transpose()
375 .map(|ty| NameOptionTypePair {
376 name: case.name.clone(),
377 typ: ty,
378 })
379 })
380 .collect::<Result<_, _>>()?,
381 )
382 .with_optional_name(self.name.clone())
383 .with_optional_owner(get_owner_name(resolve, &self.owner))),
384 TypeDefKind::Enum(enum_) => Ok(analysed_type::r#enum(
385 &enum_
386 .cases
387 .iter()
388 .map(|case| case.name.as_str())
389 .collect::<Vec<_>>(),
390 )
391 .with_optional_name(self.name.clone())
392 .with_optional_owner(get_owner_name(resolve, &self.owner))),
393 TypeDefKind::Option(inner) => Ok(analysed_type::option(
394 inner.to_analysed_type(resolve, resource_map)?,
395 )
396 .with_optional_name(self.name.clone())
397 .with_optional_owner(get_owner_name(resolve, &self.owner))),
398 TypeDefKind::Result(result) => match (result.ok, result.err) {
399 (Some(ok), Some(err)) => Ok(analysed_type::result(
400 ok.to_analysed_type(resolve, resource_map)?,
401 err.to_analysed_type(resolve, resource_map)?,
402 )
403 .with_optional_name(self.name.clone())
404 .with_optional_owner(get_owner_name(resolve, &self.owner))),
405 (Some(ok), None) => Ok(analysed_type::result_ok(
406 ok.to_analysed_type(resolve, resource_map)?,
407 )
408 .with_optional_name(self.name.clone())
409 .with_optional_owner(get_owner_name(resolve, &self.owner))),
410 (None, Some(err)) => Ok(analysed_type::result_err(
411 err.to_analysed_type(resolve, resource_map)?,
412 )
413 .with_optional_name(self.name.clone())
414 .with_optional_owner(get_owner_name(resolve, &self.owner))),
415 (None, None) => Err("result type with no ok or err case".to_string()),
416 },
417 TypeDefKind::List(ty) => Ok(analysed_type::list(
418 ty.to_analysed_type(resolve, resource_map)?,
419 )
420 .with_optional_name(self.name.clone())
421 .with_optional_owner(get_owner_name(resolve, &self.owner))),
422 TypeDefKind::FixedSizeList(ty, _) => Ok(analysed_type::list(
423 ty.to_analysed_type(resolve, resource_map)?,
424 )
425 .with_optional_name(self.name.clone())
426 .with_optional_owner(get_owner_name(resolve, &self.owner))),
427 TypeDefKind::Future(_) => {
428 Err("to_analysed_type not implemented for future type".to_string())
429 }
430 TypeDefKind::Stream(_) => {
431 Err("to_analysed_type not implemented for stream type".to_string())
432 }
433 TypeDefKind::Type(typ) => typ.to_analysed_type(resolve, resource_map),
434 TypeDefKind::Unknown => Err("to_analysed_type unknown type".to_string()),
435 }
436 }
437}
438
439impl ToAnalysedType for Type {
440 fn to_analysed_type(
441 &self,
442 resolve: &Resolve,
443 resource_map: &impl GetResourceId,
444 ) -> Result<AnalysedType, String> {
445 match self {
446 Type::Bool => Ok(analysed_type::bool()),
447 Type::U8 => Ok(analysed_type::u8()),
448 Type::U16 => Ok(analysed_type::u16()),
449 Type::U32 => Ok(analysed_type::u32()),
450 Type::U64 => Ok(analysed_type::u64()),
451 Type::S8 => Ok(analysed_type::s8()),
452 Type::S16 => Ok(analysed_type::s16()),
453 Type::S32 => Ok(analysed_type::s32()),
454 Type::S64 => Ok(analysed_type::s64()),
455 Type::F32 => Ok(analysed_type::f32()),
456 Type::F64 => Ok(analysed_type::f64()),
457 Type::Char => Ok(analysed_type::chr()),
458 Type::String => Ok(analysed_type::str()),
459 Type::Id(id) => resolve
460 .types
461 .get(*id)
462 .ok_or_else(|| format!("Type not found by id: {id:?}"))?
463 .to_analysed_type(resolve, resource_map),
464 Type::ErrorContext => Err("ErrorContext not supported".to_string()),
465 }
466 }
467}
468
469fn follow_aliases(resolve: &Resolve, type_id: &TypeId) -> TypeId {
470 let mut current_id = *type_id;
471 while let Some(type_def) = resolve.types.get(current_id) {
472 if let TypeDefKind::Type(Type::Id(alias_type_id)) = &type_def.kind {
473 current_id = *alias_type_id;
474 } else {
475 break;
476 }
477 }
478 current_id
479}
480
481fn get_type_name(resolve: &Resolve, type_id: &TypeId) -> Option<String> {
482 resolve
483 .types
484 .get(follow_aliases(resolve, type_id))
485 .and_then(|type_def| type_def.name.clone())
486}
487
488fn get_type_owner(resolve: &Resolve, type_id: &TypeId) -> Option<String> {
489 resolve
490 .types
491 .get(follow_aliases(resolve, type_id))
492 .and_then(|type_def| get_owner_name(resolve, &type_def.owner))
493}
494
495fn get_owner_name(resolve: &Resolve, owner: &wit_parser::TypeOwner) -> Option<String> {
496 match owner {
497 wit_parser::TypeOwner::World(world_id) => resolve
498 .worlds
499 .get(*world_id)
500 .map(|world| world.name.clone()),
501 wit_parser::TypeOwner::Interface(iface_id) => resolve
502 .interfaces
503 .get(*iface_id)
504 .and_then(|iface| iface.name.clone().map(|name| (iface.package, name)))
505 .and_then(|(package_id, name)| {
506 if let Some(package_id) = package_id {
507 resolve
508 .packages
509 .get(package_id)
510 .map(|package| format!("{}/{}", package.name, name))
511 } else {
512 Some(name)
513 }
514 }),
515 wit_parser::TypeOwner::None => None,
516 }
517}
518
519#[derive(Debug, Hash, PartialEq, Eq)]
520pub enum TypeOwner {
521 World(String),
522 Interface(String),
523 InlineInterface,
524 None,
525}
526
527#[derive(Debug, Hash, PartialEq, Eq)]
528pub struct TypeName {
529 pub package: Option<String>,
530 pub owner: TypeOwner,
531 pub name: Option<String>,
532}
533
534pub struct AnalysedTypeResolve {
535 resolve: Resolve,
536 type_name_to_id: HashMap<TypeName, TypeId>,
537 id_to_analysed_type: HashMap<TypeId, AnalysedType>,
538}
539
540impl Debug for AnalysedTypeResolve {
541 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
542 write!(f, "AnalysedTypeResolve")
543 }
544}
545
546impl AnalysedTypeResolve {
547 pub fn new(resolve: Resolve) -> Self {
548 let type_name_to_id = resolve
549 .types
550 .iter()
551 .map(|(type_id, type_def)| {
552 (
553 match &type_def.owner {
554 WitParserTypeOwner::World(world_id) => {
555 let world = resolve.worlds.get(*world_id).unwrap();
556 TypeName {
557 package: world
558 .package
559 .and_then(|package_id| resolve.packages.get(package_id))
560 .map(|package| package.name.to_string()),
561 owner: TypeOwner::World(world.name.clone()),
562 name: type_def.name.clone(),
563 }
564 }
565 WitParserTypeOwner::Interface(interface_id) => {
566 let interface = resolve.interfaces.get(*interface_id).unwrap();
567 TypeName {
568 package: interface
569 .package
570 .and_then(|package_id| resolve.packages.get(package_id))
571 .map(|package| package.name.to_string()),
572 owner: {
573 match &interface.name {
574 Some(name) => TypeOwner::Interface(name.clone()),
575 None => TypeOwner::InlineInterface,
576 }
577 },
578 name: type_def.name.clone(),
579 }
580 }
581 WitParserTypeOwner::None => TypeName {
582 package: None,
583 owner: TypeOwner::None,
584 name: type_def.name.clone(),
585 },
586 },
587 type_id,
588 )
589 })
590 .collect::<HashMap<_, _>>();
591
592 AnalysedTypeResolve {
593 resolve,
594 type_name_to_id,
595 id_to_analysed_type: HashMap::new(),
596 }
597 }
598
599 pub fn from_wit_directory(directory: &Path) -> Result<Self, String> {
600 let mut resolve = Resolve::new();
601 resolve.push_dir(directory).map_err(|e| e.to_string())?;
602 Ok(Self::new(resolve))
603 }
604
605 pub fn from_wit_str(wit: &str) -> Result<Self, String> {
606 let mut resolve = Resolve::new();
607 resolve
608 .push_str(wit, "wit.wit")
609 .map_err(|e| e.to_string())?;
610 Ok(Self::new(resolve))
611 }
612
613 pub fn analysed_type(&mut self, name: &TypeName) -> Result<AnalysedType, String> {
614 match self.type_name_to_id.get(name) {
615 Some(type_id) => match self.id_to_analysed_type.get(type_id) {
616 Some(typ) => Ok(typ.clone()),
617 None => {
618 let typ = self
619 .resolve
620 .types
621 .get(*type_id)
622 .unwrap()
623 .to_analysed_type(&self.resolve, &ResourcesNotSupported)?;
624 self.id_to_analysed_type.insert(*type_id, typ.clone());
625 Ok(typ)
626 }
627 },
628 None => Err(format!(
629 "Type not found by name: {:?}, available types: {}",
630 name,
631 {
632 self.type_name_to_id
633 .keys()
634 .map(|type_id| format!("{type_id:?}"))
635 .join("\n")
636 }
637 )),
638 }
639 }
640}
641
642#[derive(Clone, Debug)]
643pub struct SharedAnalysedTypeResolve {
644 resolve: Arc<Mutex<AnalysedTypeResolve>>,
645}
646
647impl SharedAnalysedTypeResolve {
648 pub fn new(resolve: AnalysedTypeResolve) -> Self {
649 Self {
650 resolve: Arc::new(Mutex::new(resolve)),
651 }
652 }
653
654 pub fn analysed_type(&mut self, name: &TypeName) -> Result<AnalysedType, String> {
655 self.resolve.lock().unwrap().analysed_type(name)
656 }
657}