golem_wasm/analysis/
wit_parser.rs

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    /// Get all top-level exports from the component with all the type information gathered from
58    /// the component AST.
59    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    /// Gets all the linear memories defined in the component
112    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    /// Gets the component's root package name
121    ///
122    /// Note that the root package name (from the original WIT) gets lost when encoding the component,
123    /// so here we use the metadata custom section's name and version fields in a Golem specific way
124    /// to recover this information. These fields are set by Golem specific build tooling. If they are not
125    /// present or not in a valid format, this function will return None.
126    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    /// Gets a binary WIT representation of the component's interface
150    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
282/// ToAnalysedType converts a Type or TypeDef from a wit_parser::Resolve.
283///
284/// ToAnalysedType is intended to be used for helping with writing tests where AnalysedType
285/// have to be constructed in the test. For simpler values and types this is usually
286/// not a problem, but creating more complex nested or variant types manually can be convoluted.
287///
288/// Note that resources and handles are not implemented.
289pub 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}