wit_component/
dummy.rs

1use wit_parser::abi::{AbiVariant, WasmType};
2use wit_parser::{
3    Function, LiftLowerAbi, ManglingAndAbi, Resolve, ResourceIntrinsic, TypeDefKind, TypeId,
4    WasmExport, WasmExportKind, WasmImport, WorldId, WorldItem, WorldKey,
5};
6
7/// Generate a dummy implementation core Wasm module for a given WIT document
8pub fn dummy_module(resolve: &Resolve, world: WorldId, mangling: ManglingAndAbi) -> Vec<u8> {
9    let world = &resolve.worlds[world];
10    let mut wat = String::new();
11    wat.push_str("(module\n");
12    for (name, import) in world.imports.iter() {
13        match import {
14            WorldItem::Function(func) => {
15                push_imported_func(&mut wat, resolve, None, func, mangling);
16            }
17            WorldItem::Interface { id: import, .. } => {
18                for (_, func) in resolve.interfaces[*import].functions.iter() {
19                    push_imported_func(&mut wat, resolve, Some(name), func, mangling);
20                }
21                for (_, ty) in resolve.interfaces[*import].types.iter() {
22                    push_imported_type_intrinsics(&mut wat, resolve, Some(name), *ty, mangling);
23                }
24            }
25            WorldItem::Type(id) => {
26                push_imported_type_intrinsics(&mut wat, resolve, None, *id, mangling);
27            }
28        }
29    }
30
31    if mangling.is_async() {
32        push_root_async_intrinsics(&mut wat);
33    }
34
35    // Append any intrinsics which are imported but used in exported items
36    // (e.g. resources)
37    for (name, export) in world.exports.iter() {
38        match export {
39            WorldItem::Function(func) => {
40                push_exported_func_intrinsics(&mut wat, resolve, None, func, mangling);
41            }
42            WorldItem::Interface { id: export, .. } => {
43                for (_, func) in resolve.interfaces[*export].functions.iter() {
44                    push_exported_func_intrinsics(&mut wat, resolve, Some(name), func, mangling);
45                }
46                for (_, ty) in resolve.interfaces[*export].types.iter() {
47                    push_exported_type_intrinsics(&mut wat, resolve, Some(name), *ty, mangling);
48                }
49            }
50            WorldItem::Type(_) => {}
51        }
52    }
53
54    for (name, export) in world.exports.iter() {
55        match export {
56            WorldItem::Function(func) => {
57                push_func_export(&mut wat, resolve, None, func, mangling);
58            }
59            WorldItem::Interface { id: export, .. } => {
60                for (_, func) in resolve.interfaces[*export].functions.iter() {
61                    push_func_export(&mut wat, resolve, Some(name), func, mangling);
62                }
63                for (_, ty) in resolve.interfaces[*export].types.iter() {
64                    push_exported_resource_functions(&mut wat, resolve, name, *ty, mangling);
65                }
66            }
67            WorldItem::Type(_) => {}
68        }
69    }
70
71    let memory = resolve.wasm_export_name(mangling, WasmExport::Memory);
72    wat.push_str(&format!("(memory (export {memory:?}) 0)\n"));
73    let realloc = resolve.wasm_export_name(mangling, WasmExport::Realloc);
74    wat.push_str(&format!(
75        "(func (export {realloc:?}) (param i32 i32 i32 i32) (result i32) unreachable)\n"
76    ));
77
78    let initialize = resolve.wasm_export_name(mangling, WasmExport::Initialize);
79    wat.push_str(&format!("(func (export {initialize:?}))"));
80    wat.push_str(")\n");
81
82    return wat::parse_str(&wat).unwrap();
83}
84
85fn push_imported_func(
86    wat: &mut String,
87    resolve: &Resolve,
88    interface: Option<&WorldKey>,
89    func: &Function,
90    mangling: ManglingAndAbi,
91) {
92    let sig = resolve.wasm_signature(mangling.import_variant(), func);
93
94    let (module, name) = resolve.wasm_import_name(mangling, WasmImport::Func { interface, func });
95    wat.push_str(&format!("(import {module:?} {name:?} (func"));
96    push_tys(wat, "param", &sig.params);
97    push_tys(wat, "result", &sig.results);
98    wat.push_str("))\n");
99
100    if mangling.is_async() {
101        push_imported_future_and_stream_intrinsics(wat, resolve, "", interface, func);
102    }
103}
104
105fn push_imported_type_intrinsics(
106    wat: &mut String,
107    resolve: &Resolve,
108    interface: Option<&WorldKey>,
109    resource: TypeId,
110    mangling: ManglingAndAbi,
111) {
112    let ty = &resolve.types[resource];
113    match ty.kind {
114        TypeDefKind::Resource => {
115            let (module, name) = resolve.wasm_import_name(
116                // Force using a sync ABI here at this time as support for async
117                // resource drop isn't implemented yet.
118                mangling.sync(),
119                WasmImport::ResourceIntrinsic {
120                    interface,
121                    resource,
122                    intrinsic: ResourceIntrinsic::ImportedDrop,
123                },
124            );
125            wat.push_str(&format!("(import {module:?} {name:?} (func (param i32)))"));
126
127            if mangling.is_async() {
128                // TODO: when wit-component supports async resource drop,
129                // implement it here too.
130                // let name = format!("[async-lower]{name}");
131                // wat.push_str(&format!("(import {module:?} {name:?} (func (param i32)))"));
132            }
133        }
134
135        // No other types with intrinsics at this time (futures/streams are
136        // relative to where they show up in function types.
137        _ => {}
138    }
139}
140
141fn push_exported_func_intrinsics(
142    wat: &mut String,
143    resolve: &Resolve,
144    interface: Option<&WorldKey>,
145    func: &Function,
146    mangling: ManglingAndAbi,
147) {
148    if !mangling.is_async() {
149        return;
150    }
151
152    // For exported async functions, generate a `task.return` intrinsic.
153    let module = match interface {
154        Some(key) => format!("[export]{}", resolve.name_world_key(key)),
155        None => "[export]$root".to_string(),
156    };
157    let name = format!("[task-return]{}", func.name);
158    let mut func_tmp = func.clone();
159    func_tmp.params = Vec::new();
160    func_tmp.result = None;
161    if let Some(ty) = func.result {
162        func_tmp.params.push(("x".to_string(), ty));
163    }
164    let sig = resolve.wasm_signature(AbiVariant::GuestImport, &func_tmp);
165    wat.push_str(&format!("(import {module:?} {name:?} (func"));
166    push_tys(wat, "param", &sig.params);
167    push_tys(wat, "result", &sig.results);
168    wat.push_str("))\n");
169
170    push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func);
171}
172
173fn push_imported_future_and_stream_intrinsics(
174    wat: &mut String,
175    resolve: &Resolve,
176    module_prefix: &str,
177    interface: Option<&WorldKey>,
178    func: &Function,
179) {
180    let module = match interface {
181        Some(key) => format!("{module_prefix}{}", resolve.name_world_key(key)),
182        None => format!("{module_prefix}$root"),
183    };
184    let name = &func.name;
185
186    for (i, id) in func
187        .find_futures_and_streams(resolve)
188        .into_iter()
189        .enumerate()
190    {
191        match &resolve.types[id].kind {
192            TypeDefKind::Future(_) => {
193                wat.push_str(&format!(
194                    r#"
195(import {module:?} "[future-new-{i}]{name}" (func (result i32)))
196(import {module:?} "[future-read-{i}]{name}" (func (param i32 i32) (result i32)))
197(import {module:?} "[future-write-{i}]{name}" (func (param i32 i32) (result i32)))
198(import {module:?} "[future-cancel-read-{i}]{name}" (func (param i32) (result i32)))
199(import {module:?} "[future-cancel-write-{i}]{name}" (func (param i32) (result i32)))
200(import {module:?} "[future-close-readable-{i}]{name}" (func (param i32 i32)))
201(import {module:?} "[future-close-writable-{i}]{name}" (func (param i32 i32)))
202(import {module:?} "[async-lower][future-read-{i}]{name}" (func (param i32 i32) (result i32)))
203(import {module:?} "[async-lower][future-write-{i}]{name}" (func (param i32 i32) (result i32)))
204(import {module:?} "[async-lower][future-cancel-read-{i}]{name}" (func (param i32) (result i32)))
205(import {module:?} "[async-lower][future-cancel-write-{i}]{name}" (func (param i32) (result i32)))
206"#
207                ));
208            }
209            TypeDefKind::Stream(_) => {
210                wat.push_str(&format!(
211                    r#"
212(import {module:?} "[stream-new-{i}]{name}" (func (result i32)))
213(import {module:?} "[stream-read-{i}]{name}" (func (param i32 i32 i32) (result i32)))
214(import {module:?} "[stream-write-{i}]{name}" (func (param i32 i32 i32) (result i32)))
215(import {module:?} "[stream-cancel-read-{i}]{name}" (func (param i32) (result i32)))
216(import {module:?} "[stream-cancel-write-{i}]{name}" (func (param i32) (result i32)))
217(import {module:?} "[stream-close-readable-{i}]{name}" (func (param i32 i32)))
218(import {module:?} "[stream-close-writable-{i}]{name}" (func (param i32 i32)))
219(import {module:?} "[async-lower][stream-read-{i}]{name}" (func (param i32 i32 i32) (result i32)))
220(import {module:?} "[async-lower][stream-write-{i}]{name}" (func (param i32 i32 i32) (result i32)))
221(import {module:?} "[async-lower][stream-cancel-read-{i}]{name}" (func (param i32) (result i32)))
222(import {module:?} "[async-lower][stream-cancel-write-{i}]{name}" (func (param i32) (result i32)))
223"#
224                ));
225            }
226            _ => unreachable!(),
227        }
228    }
229}
230
231fn push_exported_type_intrinsics(
232    wat: &mut String,
233    resolve: &Resolve,
234    interface: Option<&WorldKey>,
235    resource: TypeId,
236    mangling: ManglingAndAbi,
237) {
238    let ty = &resolve.types[resource];
239    match ty.kind {
240        TypeDefKind::Resource => {
241            let intrinsics = [
242                (ResourceIntrinsic::ExportedDrop, "(func (param i32))"),
243                (
244                    ResourceIntrinsic::ExportedNew,
245                    "(func (param i32) (result i32))",
246                ),
247                (
248                    ResourceIntrinsic::ExportedRep,
249                    "(func (param i32) (result i32))",
250                ),
251            ];
252            for (intrinsic, sig) in intrinsics {
253                let (module, name) = resolve.wasm_import_name(
254                    mangling.sync(),
255                    WasmImport::ResourceIntrinsic {
256                        interface,
257                        resource,
258                        intrinsic,
259                    },
260                );
261                wat.push_str(&format!("(import {module:?} {name:?} {sig})\n"));
262            }
263        }
264
265        // No other types with intrinsics at this time (futures/streams
266        // relative to where they are in a function).
267        _ => {}
268    }
269}
270
271fn push_exported_resource_functions(
272    wat: &mut String,
273    resolve: &Resolve,
274    interface: &WorldKey,
275    resource: TypeId,
276    mangling: ManglingAndAbi,
277) {
278    let ty = &resolve.types[resource];
279    match ty.kind {
280        TypeDefKind::Resource => {}
281        _ => return,
282    }
283    // Feign destructors for any resource that this interface
284    // exports
285    let name = resolve.wasm_export_name(
286        mangling,
287        WasmExport::ResourceDtor {
288            interface,
289            resource,
290        },
291    );
292    wat.push_str(&format!("(func (export {name:?}) (param i32))"));
293}
294
295fn push_func_export(
296    wat: &mut String,
297    resolve: &Resolve,
298    interface: Option<&WorldKey>,
299    func: &Function,
300    mangling: ManglingAndAbi,
301) {
302    let sig = resolve.wasm_signature(mangling.export_variant(), func);
303    let name = resolve.wasm_export_name(
304        mangling,
305        WasmExport::Func {
306            interface,
307            func,
308            kind: WasmExportKind::Normal,
309        },
310    );
311    wat.push_str(&format!("(func (export \"{name}\")"));
312    push_tys(wat, "param", &sig.params);
313    push_tys(wat, "result", &sig.results);
314    wat.push_str(" unreachable)\n");
315
316    match mangling {
317        ManglingAndAbi::Standard32 | ManglingAndAbi::Legacy(LiftLowerAbi::Sync) => {
318            let name = resolve.wasm_export_name(
319                mangling,
320                WasmExport::Func {
321                    interface,
322                    func,
323                    kind: WasmExportKind::PostReturn,
324                },
325            );
326            wat.push_str(&format!("(func (export \"{name}\")"));
327            push_tys(wat, "param", &sig.results);
328            wat.push_str(")\n");
329        }
330        ManglingAndAbi::Legacy(LiftLowerAbi::AsyncCallback) => {
331            let name = resolve.wasm_export_name(
332                mangling,
333                WasmExport::Func {
334                    interface,
335                    func,
336                    kind: WasmExportKind::Callback,
337                },
338            );
339            wat.push_str(&format!(
340                "(func (export \"{name}\") (param i32 i32 i32 i32) (result i32) unreachable)\n"
341            ));
342        }
343        ManglingAndAbi::Legacy(LiftLowerAbi::AsyncStackful) => {}
344    }
345}
346
347fn push_tys(dst: &mut String, desc: &str, params: &[WasmType]) {
348    if params.is_empty() {
349        return;
350    }
351    dst.push_str(" (");
352    dst.push_str(desc);
353    for ty in params {
354        dst.push(' ');
355        match ty {
356            WasmType::I32 => dst.push_str("i32"),
357            WasmType::I64 => dst.push_str("i64"),
358            WasmType::F32 => dst.push_str("f32"),
359            WasmType::F64 => dst.push_str("f64"),
360            WasmType::Pointer => dst.push_str("i32"),
361            WasmType::PointerOrI64 => dst.push_str("i64"),
362            WasmType::Length => dst.push_str("i32"),
363        }
364    }
365    dst.push(')');
366}
367
368fn push_root_async_intrinsics(dst: &mut String) {
369    dst.push_str(
370        r#"
371(import "$root" "[backpressure-set]" (func (param i32)))
372(import "$root" "[waitable-set-new]" (func (result i32)))
373(import "$root" "[waitable-set-wait]" (func (param i32 i32) (result i32)))
374(import "$root" "[async-lower][waitable-set-wait]" (func (param i32 i32) (result i32)))
375(import "$root" "[waitable-set-poll]" (func (param i32 i32) (result i32)))
376(import "$root" "[async-lower][waitable-set-poll]" (func (param i32 i32) (result i32)))
377(import "$root" "[waitable-set-drop]" (func (param i32)))
378(import "$root" "[waitable-join]" (func (param i32 i32)))
379(import "$root" "[yield]" (func))
380(import "$root" "[async-lower][yield]" (func))
381(import "$root" "[subtask-drop]" (func (param i32)))
382(import "$root" "[error-context-new-utf8]" (func (param i32 i32) (result i32)))
383(import "$root" "[error-context-new-utf16]" (func (param i32 i32) (result i32)))
384(import "$root" "[error-context-new-latin1+utf16]" (func (param i32 i32) (result i32)))
385(import "$root" "[error-context-debug-message-utf8]" (func (param i32 i32)))
386(import "$root" "[error-context-debug-message-utf16]" (func (param i32 i32)))
387(import "$root" "[error-context-debug-message-latin1+utf16]" (func (param i32 i32)))
388(import "$root" "[error-context-drop]" (func (param i32)))
389"#,
390    );
391}