/src/wasm-tools/crates/wit-component/src/targets.rs
Line | Count | Source |
1 | | use crate::encoding::encode_world; |
2 | | use anyhow::{Context, Result, bail}; |
3 | | use wasm_encoder::{ComponentBuilder, ComponentExportKind, ComponentTypeRef}; |
4 | | use wasmparser::{ComponentTypeRef as ImportTypeRef, Parser, Payload, Validator, WasmFeatures}; |
5 | | use wit_parser::decoding::{DecodedWasm, decode}; |
6 | | use wit_parser::{Resolve, WorldId}; |
7 | | |
8 | | /// This function checks whether `component_to_test` correctly conforms to the world specified. |
9 | | /// It does so by instantiating a generated component that imports a component instance with |
10 | | /// the component type as described by the "target" world. |
11 | | /// |
12 | | /// When `semver_compatible` is set, interfaces are matched by their canonical |
13 | | /// names, so semver-compatible versions (e.g. `wasi:cli@0.2.3` and |
14 | | /// `wasi:cli@0.2.0`) are matched with each other and then type-checked |
15 | | /// structurally. |
16 | 0 | pub fn targets( |
17 | 0 | resolve: &Resolve, |
18 | 0 | world: WorldId, |
19 | 0 | component_to_test: &[u8], |
20 | 0 | semver_compatible: bool, |
21 | 0 | ) -> Result<()> { |
22 | 0 | let mut root_component = ComponentBuilder::default(); |
23 | | |
24 | | // (1) Embed the component to test. With `semver_compatible`, the names in |
25 | | // the component itself may not be canonical, so we decode its world, |
26 | | // merge semver-compatible imports, and import a component whose type is |
27 | | // that world encoded with canonical names. |
28 | 0 | let component_to_test_idx = if semver_compatible { |
29 | 0 | check_imports_are_wit(component_to_test)?; |
30 | 0 | let (mut test_resolve, test_world) = match decode(component_to_test) |
31 | 0 | .context("failed to decode the WIT world of the component to test")? |
32 | | { |
33 | 0 | DecodedWasm::Component(resolve, world) => (resolve, world), |
34 | 0 | DecodedWasm::WitPackage(..) => bail!("expected a component, found a WIT package"), |
35 | | }; |
36 | 0 | test_resolve.merge_world_imports_based_on_semver(test_world)?; |
37 | 0 | let component_ty = encode_world(&test_resolve, test_world, true)?; |
38 | 0 | let component_ty_idx = root_component.type_component(None, &component_ty); |
39 | 0 | root_component.import( |
40 | | "component-to-test", |
41 | 0 | ComponentTypeRef::Component(component_ty_idx), |
42 | | ) |
43 | | } else { |
44 | 0 | root_component.component_raw(None, component_to_test) |
45 | | }; |
46 | | |
47 | | // (2) Encode the world to a component type and embed a new component which |
48 | | // imports the encoded component type. |
49 | 0 | let test_component_idx = { |
50 | 0 | let component_ty = if semver_compatible { |
51 | 0 | let mut resolve = resolve.clone(); |
52 | 0 | resolve.merge_world_imports_based_on_semver(world)?; |
53 | 0 | encode_world(&resolve, world, true)? |
54 | | } else { |
55 | 0 | encode_world(resolve, world, false)? |
56 | | }; |
57 | 0 | let mut component = ComponentBuilder::default(); |
58 | 0 | let component_ty_idx = component.type_component(None, &component_ty); |
59 | 0 | component.import( |
60 | 0 | &resolve.worlds[world].name, |
61 | 0 | ComponentTypeRef::Component(component_ty_idx), |
62 | | ); |
63 | 0 | root_component.component(None, component) |
64 | | }; |
65 | | |
66 | | // (3) Instantiate the component from (2) with the component to test from (1). |
67 | 0 | let args: Vec<(String, ComponentExportKind, u32)> = vec![( |
68 | 0 | resolve.worlds[world].name.clone(), |
69 | 0 | ComponentExportKind::Component, |
70 | 0 | component_to_test_idx, |
71 | 0 | )]; |
72 | 0 | root_component.instantiate(None, test_component_idx, args); |
73 | | |
74 | 0 | let bytes = root_component.finish(); |
75 | | |
76 | 0 | Validator::new_with_features(WasmFeatures::all()) |
77 | 0 | .validate_all(&bytes) |
78 | 0 | .context("failed to validate encoded bytes")?; |
79 | | |
80 | 0 | Ok(()) |
81 | 0 | } |
82 | | |
83 | | /// Decoding a component to WIT skips imports that aren't representable in WIT. |
84 | | /// For example, importing a component, a core module, or a value. |
85 | 0 | fn check_imports_are_wit(component: &[u8]) -> Result<()> { |
86 | 0 | let mut depth = 0; |
87 | 0 | for payload in Parser::new(0).parse_all(component) { |
88 | 0 | match payload? { |
89 | 0 | Payload::ModuleSection { .. } | Payload::ComponentSection { .. } => depth += 1, |
90 | 0 | Payload::End(_) => depth -= 1, |
91 | 0 | Payload::ComponentImportSection(imports) if depth == 0 => { |
92 | 0 | for import in imports { |
93 | 0 | let import = import?; |
94 | 0 | match import.ty { |
95 | | ImportTypeRef::Instance(_) |
96 | | | ImportTypeRef::Func(_) |
97 | 0 | | ImportTypeRef::Type(_) => {} |
98 | 0 | _ => bail!( |
99 | | "import `{}` of the component to test cannot be represented in WIT", |
100 | | import.name.name |
101 | | ), |
102 | | } |
103 | | } |
104 | | } |
105 | 0 | _ => {} |
106 | | } |
107 | | } |
108 | 0 | Ok(()) |
109 | 0 | } |