Skip to main content

wit_component/
dummy.rs

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