Skip to main content

wai_bindgen_gen_wasmtime_py/
lib.rs

1use heck::*;
2use std::collections::{BTreeMap, BTreeSet, HashMap};
3use std::mem;
4use wai_bindgen_gen_core::wai_parser::abi::{
5    AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType,
6};
7use wai_bindgen_gen_core::{wai_parser::*, Direction, Files, Generator, Ns};
8
9pub mod dependencies;
10pub mod source;
11
12use dependencies::Dependencies;
13use source::Source;
14
15#[derive(Default)]
16pub struct WasmtimePy {
17    src: Source,
18    in_import: bool,
19    opts: Opts,
20    guest_imports: HashMap<String, Imports>,
21    guest_exports: HashMap<String, Exports>,
22    sizes: SizeAlign,
23    /// Tracks the intrinsics and Python imports needed
24    deps: Dependencies,
25    /// Whether the Python Union being emited will wrap its cases with dataclasses
26    union_representation: HashMap<String, PyUnionRepresentation>,
27}
28
29#[derive(Debug, Clone, Copy)]
30enum PyUnionRepresentation {
31    /// A union whose inner types are used directly
32    Raw,
33    /// A union whose inner types have been wrapped in dataclasses
34    Wrapped,
35}
36
37#[derive(Default)]
38struct Imports {
39    freestanding_funcs: Vec<Import>,
40    resource_funcs: BTreeMap<ResourceId, Vec<Import>>,
41}
42
43struct Import {
44    name: String,
45    src: Source,
46    wasm_ty: String,
47    pysig: String,
48}
49
50#[derive(Default)]
51struct Exports {
52    freestanding_funcs: Vec<Source>,
53    resource_funcs: BTreeMap<ResourceId, Vec<Source>>,
54    fields: BTreeMap<String, &'static str>,
55}
56
57#[derive(Default, Debug, Clone)]
58#[cfg_attr(feature = "structopt", derive(structopt::StructOpt))]
59pub struct Opts {
60    #[cfg_attr(feature = "structopt", structopt(long = "no-typescript"))]
61    pub no_typescript: bool,
62}
63
64impl Opts {
65    pub fn build(self) -> WasmtimePy {
66        let mut r = WasmtimePy::new();
67        r.opts = self;
68        r
69    }
70}
71
72impl WasmtimePy {
73    pub fn new() -> WasmtimePy {
74        WasmtimePy::default()
75    }
76
77    fn abi_variant(dir: Direction) -> AbiVariant {
78        // This generator uses a reversed mapping! In the Wasmtime-py host-side
79        // bindings, we don't use any extra adapter layer between guest wasm
80        // modules and the host. When the guest imports functions using the
81        // `GuestImport` ABI, the host directly implements the `GuestImport`
82        // ABI, even though the host is *exporting* functions. Similarly, when
83        // the guest exports functions using the `GuestExport` ABI, the host
84        // directly imports them with the `GuestExport` ABI, even though the
85        // host is *importing* functions.
86        match dir {
87            Direction::Import => AbiVariant::GuestExport,
88            Direction::Export => AbiVariant::GuestImport,
89        }
90    }
91
92    /// Creates a `Source` with all of the required intrinsics
93    fn intrinsics(&mut self, iface: &Interface) -> Source {
94        if iface.resources.len() > 0 {
95            self.deps.needs_resources = true;
96            self.deps.pyimport("typing", "runtime_checkable");
97        }
98        self.deps.intrinsics()
99    }
100}
101
102fn array_ty(iface: &Interface, ty: &Type) -> Option<&'static str> {
103    match ty {
104        Type::Unit | Type::Bool => None,
105        Type::U8 => Some("c_uint8"),
106        Type::S8 => Some("c_int8"),
107        Type::U16 => Some("c_uint16"),
108        Type::S16 => Some("c_int16"),
109        Type::U32 => Some("c_uint32"),
110        Type::S32 => Some("c_int32"),
111        Type::U64 => Some("c_uint64"),
112        Type::S64 => Some("c_int64"),
113        Type::Float32 => Some("c_float"),
114        Type::Float64 => Some("c_double"),
115        Type::Char => None,
116        Type::Handle(_) => None,
117        Type::String => None,
118        Type::Id(id) => match &iface.types[*id].kind {
119            TypeDefKind::Type(t) => array_ty(iface, t),
120            _ => None,
121        },
122    }
123}
124
125impl Generator for WasmtimePy {
126    fn preprocess_one(&mut self, iface: &Interface, dir: Direction) {
127        let variant = Self::abi_variant(dir);
128        self.sizes.fill(iface);
129        self.in_import = variant == AbiVariant::GuestImport;
130    }
131
132    fn type_record(
133        &mut self,
134        iface: &Interface,
135        _id: TypeId,
136        name: &str,
137        record: &Record,
138        docs: &Docs,
139    ) {
140        let mut builder = self.src.builder(&mut self.deps, iface);
141        builder.pyimport("dataclasses", "dataclass");
142        builder.push_str("@dataclass\n");
143        builder.push_str(&format!("class {}:\n", name.to_camel_case()));
144        builder.indent();
145        builder.docstring(docs);
146        for field in record.fields.iter() {
147            builder.comment(&field.docs);
148            let field_name = field.name.to_snake_case();
149            builder.push_str(&format!("{field_name}: "));
150            builder.print_ty(&field.ty, true);
151            builder.push_str("\n");
152        }
153        if record.fields.is_empty() {
154            builder.push_str("pass\n");
155        }
156        builder.dedent();
157        builder.push_str("\n");
158    }
159
160    fn type_tuple(
161        &mut self,
162        iface: &Interface,
163        _id: TypeId,
164        name: &str,
165        tuple: &Tuple,
166        docs: &Docs,
167    ) {
168        let mut builder = self.src.builder(&mut self.deps, iface);
169        builder.comment(docs);
170        builder.push_str(&format!("{} = ", name.to_camel_case()));
171        builder.print_tuple(tuple);
172        builder.push_str("\n");
173    }
174
175    fn type_flags(
176        &mut self,
177        iface: &Interface,
178        _id: TypeId,
179        name: &str,
180        flags: &Flags,
181        docs: &Docs,
182    ) {
183        let mut builder = self.src.builder(&mut self.deps, iface);
184        builder.pyimport("enum", "Flag");
185        builder.pyimport("enum", "auto");
186        builder.push_str(&format!("class {}(Flag):\n", name.to_camel_case()));
187        builder.indent();
188        builder.docstring(docs);
189        for flag in flags.flags.iter() {
190            let flag_name = flag.name.to_shouty_snake_case();
191            builder.comment(&flag.docs);
192            builder.push_str(&format!("{flag_name} = auto()\n"));
193        }
194        if flags.flags.is_empty() {
195            builder.push_str("pass\n");
196        }
197        builder.dedent();
198        builder.push_str("\n");
199    }
200
201    fn type_variant(
202        &mut self,
203        iface: &Interface,
204        _id: TypeId,
205        name: &str,
206        variant: &Variant,
207        docs: &Docs,
208    ) {
209        let mut builder = self.src.builder(&mut self.deps, iface);
210        builder.pyimport("dataclasses", "dataclass");
211        let mut cases = Vec::new();
212        for case in variant.cases.iter() {
213            builder.docstring(&case.docs);
214            builder.push_str("@dataclass\n");
215            let case_name = format!("{}{}", name.to_camel_case(), case.name.to_camel_case());
216            builder.push_str(&format!("class {case_name}:\n"));
217            builder.indent();
218            builder.push_str("value: ");
219            builder.print_ty(&case.ty, true);
220            builder.push_str("\n");
221            builder.dedent();
222            builder.push_str("\n");
223            cases.push(case_name);
224        }
225
226        builder.deps.pyimport("typing", "Union");
227        builder.comment(docs);
228        builder.push_str(&format!(
229            "{} = Union[{}]\n",
230            name.to_camel_case(),
231            cases.join(", "),
232        ));
233        builder.push_str("\n");
234    }
235
236    /// Appends a Python definition for the provided Union to the current `Source`.
237    /// e.g. `MyUnion = Union[float, str, int]`
238    fn type_union(
239        &mut self,
240        iface: &Interface,
241        _id: TypeId,
242        name: &str,
243        union: &Union,
244        docs: &Docs,
245    ) {
246        let mut py_type_classes = BTreeSet::new();
247        for case in union.cases.iter() {
248            py_type_classes.insert(py_type_class_of(&case.ty));
249        }
250
251        let mut builder = self.src.builder(&mut self.deps, iface);
252        if py_type_classes.len() != union.cases.len() {
253            // Some of the cases are not distinguishable
254            self.union_representation
255                .insert(name.to_string(), PyUnionRepresentation::Wrapped);
256            builder.print_union_wrapped(name, union, docs);
257        } else {
258            // All of the cases are distinguishable
259            self.union_representation
260                .insert(name.to_string(), PyUnionRepresentation::Raw);
261            builder.print_union_raw(name, union, docs);
262        }
263    }
264
265    fn type_option(
266        &mut self,
267        iface: &Interface,
268        _id: TypeId,
269        name: &str,
270        payload: &Type,
271        docs: &Docs,
272    ) {
273        let mut builder = self.src.builder(&mut self.deps, iface);
274        builder.pyimport("typing", "Optional");
275        builder.comment(docs);
276        builder.push_str(&name.to_camel_case());
277        builder.push_str(" = Optional[");
278        builder.print_ty(payload, true);
279        builder.push_str("]\n\n");
280    }
281
282    fn type_expected(
283        &mut self,
284        iface: &Interface,
285        _id: TypeId,
286        name: &str,
287        expected: &Expected,
288        docs: &Docs,
289    ) {
290        self.deps.needs_expected = true;
291
292        let mut builder = self.src.builder(&mut self.deps, iface);
293        builder.comment(docs);
294        builder.push_str(&format!("{} = Expected[", name.to_camel_case()));
295        builder.print_ty(&expected.ok, true);
296        builder.push_str(", ");
297        builder.print_ty(&expected.err, true);
298        builder.push_str("]\n\n");
299    }
300
301    fn type_enum(&mut self, iface: &Interface, _id: TypeId, name: &str, enum_: &Enum, docs: &Docs) {
302        let mut builder = self.src.builder(&mut self.deps, iface);
303        builder.pyimport("enum", "Enum");
304        builder.push_str(&format!("class {}(Enum):\n", name.to_camel_case()));
305        builder.indent();
306        builder.docstring(docs);
307        for (i, case) in enum_.cases.iter().enumerate() {
308            builder.comment(&case.docs);
309
310            // TODO this handling of digits should be more general and
311            // shouldn't be here just to fix the one case in wasi where an
312            // enum variant is "2big" and doesn't generate valid Python. We
313            // should probably apply this to all generated Python
314            // identifiers.
315            let mut name = case.name.to_shouty_snake_case();
316            if name.chars().next().unwrap().is_ascii_digit() {
317                name = format!("_{}", name);
318            }
319            builder.push_str(&format!("{} = {}\n", name, i));
320        }
321        builder.dedent();
322        builder.push_str("\n");
323    }
324
325    fn type_resource(&mut self, _iface: &Interface, _ty: ResourceId) {
326        // if !self.in_import {
327        //     self.exported_resources.insert(ty);
328        // }
329    }
330
331    fn type_alias(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
332        let mut builder = self.src.builder(&mut self.deps, iface);
333        builder.comment(docs);
334        builder.push_str(&format!("{} = ", name.to_camel_case()));
335        builder.print_ty(ty, false);
336        builder.push_str("\n");
337    }
338
339    fn type_list(&mut self, iface: &Interface, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
340        let mut builder = self.src.builder(&mut self.deps, iface);
341        builder.comment(docs);
342        builder.push_str(&format!("{} = ", name.to_camel_case()));
343        builder.print_list(ty);
344        builder.push_str("\n");
345    }
346
347    fn type_builtin(&mut self, iface: &Interface, id: TypeId, name: &str, ty: &Type, docs: &Docs) {
348        self.type_alias(iface, id, name, ty, docs);
349    }
350
351    // As with `abi_variant` above, we're generating host-side bindings here
352    // so a user "export" uses the "guest import" ABI variant on the inside of
353    // this `Generator` implementation.
354    fn export(&mut self, iface: &Interface, func: &Function) {
355        assert!(!func.is_async, "async not supported yet");
356        let mut pysig = Source::default();
357        let mut builder = pysig.builder(&mut self.deps, iface);
358        builder.print_sig(func, self.in_import);
359        let pysig = pysig.to_string();
360
361        let mut func_body = Source::default();
362        let mut builder = func_body.builder(&mut self.deps, iface);
363
364        let sig = iface.wasm_signature(AbiVariant::GuestImport, func);
365        builder.push_str(&format!(
366            "def {}(caller: wasmtime.Caller",
367            func.name.to_snake_case(),
368        ));
369        let mut params = Vec::new();
370        for (i, param) in sig.params.iter().enumerate() {
371            builder.push_str(", ");
372            let name = format!("arg{}", i);
373            builder.push_str(&name);
374            builder.push_str(": ");
375            builder.push_str(wasm_ty_typing(*param));
376            params.push(name);
377        }
378        builder.push_str(") -> ");
379        match sig.results.len() {
380            0 => builder.push_str("None"),
381            1 => builder.push_str(wasm_ty_typing(sig.results[0])),
382            _ => unimplemented!(),
383        }
384        builder.push_str(":\n");
385        builder.indent();
386        drop(builder);
387
388        let mut f = FunctionBindgen::new(self, params);
389        iface.call(
390            AbiVariant::GuestImport,
391            LiftLower::LiftArgsLowerResults,
392            func,
393            &mut f,
394        );
395
396        let FunctionBindgen {
397            src,
398            needs_memory,
399            needs_realloc,
400            needs_free,
401            mut locals,
402            ..
403        } = f;
404
405        let mut builder = func_body.builder(&mut self.deps, iface);
406        if needs_memory {
407            // TODO: hardcoding "memory"
408            builder.push_str("m = caller[\"memory\"]\n");
409            builder.push_str("assert(isinstance(m, wasmtime.Memory))\n");
410            builder.deps.pyimport("typing", "cast");
411            builder.push_str("memory = cast(wasmtime.Memory, m)\n");
412            locals.insert("memory").unwrap();
413        }
414
415        if let Some(name) = needs_realloc {
416            builder.push_str(&format!("realloc = caller[\"{}\"]\n", name));
417            builder.push_str("assert(isinstance(realloc, wasmtime.Func))\n");
418            locals.insert("realloc").unwrap();
419        }
420
421        if let Some(name) = needs_free {
422            builder.push_str(&format!("free = caller[\"{}\"]\n", name));
423            builder.push_str("assert(isinstance(free, wasmtime.Func))\n");
424            locals.insert("free").unwrap();
425        }
426        builder.push_str(&src);
427        builder.dedent();
428
429        let mut wasm_ty = String::from("wasmtime.FuncType([");
430        wasm_ty.push_str(
431            &sig.params
432                .iter()
433                .map(|t| wasm_ty_ctor(*t))
434                .collect::<Vec<_>>()
435                .join(", "),
436        );
437        wasm_ty.push_str("], [");
438        wasm_ty.push_str(
439            &sig.results
440                .iter()
441                .map(|t| wasm_ty_ctor(*t))
442                .collect::<Vec<_>>()
443                .join(", "),
444        );
445        wasm_ty.push_str("])");
446        let import = Import {
447            name: func.name.clone(),
448            src: func_body,
449            wasm_ty,
450            pysig,
451        };
452        let imports = self
453            .guest_imports
454            .entry(iface.name.to_string())
455            .or_default();
456        let dst = match &func.kind {
457            FunctionKind::Freestanding | FunctionKind::Static { .. } => {
458                &mut imports.freestanding_funcs
459            }
460            FunctionKind::Method { resource, .. } => {
461                imports.resource_funcs.entry(*resource).or_default()
462            }
463        };
464        dst.push(import);
465    }
466
467    // As with `abi_variant` above, we're generating host-side bindings here
468    // so a user "import" uses the "export" ABI variant on the inside of
469    // this `Generator` implementation.
470    fn import(&mut self, iface: &Interface, func: &Function) {
471        assert!(!func.is_async, "async not supported yet");
472        let mut func_body = Source::default();
473        let mut builder = func_body.builder(&mut self.deps, iface);
474
475        // Print the function signature
476        let params = builder.print_sig(func, self.in_import);
477        builder.push_str(":\n");
478        builder.indent();
479        drop(builder);
480
481        // Use FunctionBindgen call
482        let src_object = match &func.kind {
483            FunctionKind::Freestanding => "self".to_string(),
484            FunctionKind::Static { .. } => "obj".to_string(),
485            FunctionKind::Method { .. } => "self._obj".to_string(),
486        };
487        let mut f = FunctionBindgen::new(self, params);
488        f.src_object = src_object;
489        iface.call(
490            AbiVariant::GuestExport,
491            LiftLower::LowerArgsLiftResults,
492            func,
493            &mut f,
494        );
495        let FunctionBindgen {
496            src,
497            needs_memory,
498            needs_realloc,
499            needs_free,
500            src_object,
501            ..
502        } = f;
503        let mut builder = func_body.builder(&mut self.deps, iface);
504        if needs_memory {
505            // TODO: hardcoding "memory"
506            builder.push_str(&format!("memory = {}._memory;\n", src_object));
507        }
508
509        if let Some(name) = &needs_realloc {
510            builder.push_str(&format!(
511                "realloc = {}._{}\n",
512                src_object,
513                name.to_snake_case(),
514            ));
515        }
516
517        if let Some(name) = &needs_free {
518            builder.push_str(&format!(
519                "free = {}._{}\n",
520                src_object,
521                name.to_snake_case(),
522            ));
523        }
524        builder.push_str(&src);
525        builder.dedent();
526
527        let exports = self
528            .guest_exports
529            .entry(iface.name.to_string())
530            .or_insert_with(Exports::default);
531        if needs_memory {
532            exports
533                .fields
534                .insert("memory".to_string(), "wasmtime.Memory");
535        }
536        if let Some(name) = &needs_realloc {
537            exports.fields.insert(name.clone(), "wasmtime.Func");
538        }
539        if let Some(name) = &needs_free {
540            exports.fields.insert(name.clone(), "wasmtime.Func");
541        }
542        exports.fields.insert(func.name.clone(), "wasmtime.Func");
543
544        let dst = match &func.kind {
545            FunctionKind::Freestanding => &mut exports.freestanding_funcs,
546            FunctionKind::Static { resource, .. } | FunctionKind::Method { resource, .. } => {
547                exports.resource_funcs.entry(*resource).or_default()
548            }
549        };
550        dst.push(func_body);
551    }
552
553    fn finish_one(&mut self, iface: &Interface, files: &mut Files) {
554        self.deps.pyimport("typing", "Any");
555        self.deps.pyimport("abc", "abstractmethod");
556
557        let types = mem::take(&mut self.src);
558        let intrinsics = self.intrinsics(iface);
559
560        for (k, v) in self.deps.pyimports.iter() {
561            match v {
562                Some(list) => {
563                    let list = list.iter().cloned().collect::<Vec<_>>().join(", ");
564                    self.src.push_str(&format!("from {} import {}\n", k, list));
565                }
566                None => {
567                    self.src.push_str(&format!("import {}\n", k));
568                }
569            }
570        }
571        self.src.push_str("import wasmtime\n");
572        self.src.push_str(
573            "
574                try:
575                    from typing import Protocol
576                except ImportError:
577                    class Protocol: # type: ignore
578                        pass
579            ",
580        );
581        self.src.push_str("\n");
582
583        if self.deps.needs_t_typevar {
584            self.src.push_str("T = TypeVar('T')\n");
585        }
586
587        self.src.push_str(&intrinsics);
588        for (id, r) in iface.resources.iter() {
589            let name = r.name.to_camel_case();
590            if self.in_import {
591                self.src.push_str("@runtime_checkable\n");
592                self.src.push_str(&format!("class {}(Protocol):\n", name));
593                self.src.indent();
594                self.src.push_str("def drop(self) -> None:\n");
595                self.src.indent();
596                self.src.push_str("pass\n");
597                self.src.dedent();
598
599                for (_, funcs) in self.guest_imports.iter() {
600                    if let Some(funcs) = funcs.resource_funcs.get(&id) {
601                        for func in funcs {
602                            self.src.push_str("@abstractmethod\n");
603                            self.src.push_str(&func.pysig);
604                            self.src.push_str(":\n");
605                            self.src.indent();
606                            self.src.push_str("raise NotImplementedError\n");
607                            self.src.dedent();
608                        }
609                    }
610                }
611                self.src.dedent();
612            } else {
613                self.src.push_str(&format!("class {}:\n", name));
614                self.src.indent();
615                self.src.push_str(&format!(
616                    "
617                        _wasm_val: int
618                        _refcnt: int
619                        _obj: '{iface}'
620                        _destroyed: bool
621
622                        def __init__(self, val: int, obj: '{iface}') -> None:
623                            self._wasm_val = val
624                            self._refcnt = 1
625                            self._obj = obj
626                            self._destroyed = False
627
628                        def clone(self) -> '{name}':
629                            self._refcnt += 1
630                            return self
631
632                        def drop(self, store: wasmtime.Storelike) -> None:
633                            self._refcnt -= 1;
634                            if self._refcnt != 0:
635                                return
636                            assert(not self._destroyed)
637                            self._destroyed = True
638                            self._obj._canonical_abi_drop_{drop}(store, self._wasm_val)
639
640                        def __del__(self) -> None:
641                            if not self._destroyed:
642                                raise RuntimeError('wasm object not dropped')
643                    ",
644                    name = name,
645                    iface = iface.name.to_camel_case(),
646                    drop = name.to_snake_case(),
647                ));
648
649                for (_, exports) in self.guest_exports.iter() {
650                    if let Some(funcs) = exports.resource_funcs.get(&id) {
651                        for func in funcs {
652                            self.src.push_str(func);
653                        }
654                    }
655                }
656
657                self.src.dedent();
658            }
659        }
660        self.src.push_str(&types);
661
662        for (module, funcs) in mem::take(&mut self.guest_imports) {
663            self.src
664                .push_str(&format!("class {}(Protocol):\n", module.to_camel_case()));
665            self.src.indent();
666            for func in funcs.freestanding_funcs.iter() {
667                self.src.push_str("@abstractmethod\n");
668                self.src.push_str(&func.pysig);
669                self.src.push_str(":\n");
670                self.src.indent();
671                self.src.push_str("raise NotImplementedError\n");
672                self.src.dedent();
673            }
674            self.src.dedent();
675            self.src.push_str("\n");
676
677            self.src.push_str(&format!(
678                "def add_{}_to_linker(linker: wasmtime.Linker, store: wasmtime.Store, host: {}) -> None:\n",
679                module.to_snake_case(),
680                module.to_camel_case(),
681            ));
682            self.src.indent();
683
684            for (id, r) in iface.resources.iter() {
685                self.src.push_str(&format!(
686                    "_resources{}: Slab[{}] = Slab()\n",
687                    id.index(),
688                    r.name.to_camel_case()
689                ));
690            }
691
692            for func in funcs
693                .freestanding_funcs
694                .iter()
695                .chain(funcs.resource_funcs.values().flatten())
696            {
697                self.src.push_str(&format!("ty = {}\n", func.wasm_ty));
698                self.src.push_str(&func.src);
699                self.src.push_str(&format!(
700                    "linker.define('{}', '{}', wasmtime.Func(store, ty, {}, access_caller = True))\n",
701                    iface.name,
702                    func.name,
703                    func.name.to_snake_case(),
704                ));
705            }
706
707            for (id, resource) in iface.resources.iter() {
708                let snake = resource.name.to_snake_case();
709
710                self.src.push_str(&format!(
711                    "def resource_drop_{}(i: int) -> None:\n  _resources{}.remove(i).drop()\n",
712                    snake,
713                    id.index(),
714                ));
715                self.src
716                    .push_str("ty = wasmtime.FuncType([wasmtime.ValType.i32()], [])\n");
717                self.src.push_str(&format!(
718                    "linker.define(\
719                        'canonical_abi', \
720                        'resource_drop_{}', \
721                        wasmtime.Func(store, ty, resource_drop_{})\
722                    )\n",
723                    resource.name, snake,
724                ));
725            }
726            self.src.dedent();
727        }
728
729        // This is exculsively here to get mypy to not complain about empty
730        // modules, this probably won't really get triggered much in practice
731        if !self.in_import && self.guest_exports.is_empty() {
732            self.src
733                .push_str(&format!("class {}:\n", iface.name.to_camel_case()));
734            self.src.indent();
735            if iface.resources.len() == 0 {
736                self.src.push_str("pass\n");
737            } else {
738                for (_, r) in iface.resources.iter() {
739                    self.src.push_str(&format!(
740                        "_canonical_abi_drop_{}: wasmtime.Func\n",
741                        r.name.to_snake_case(),
742                    ));
743                }
744            }
745            self.src.dedent();
746        }
747
748        for (module, exports) in mem::take(&mut self.guest_exports) {
749            let module = module.to_camel_case();
750            self.src.push_str(&format!("class {}:\n", module));
751            self.src.indent();
752
753            self.src.push_str("instance: wasmtime.Instance\n");
754            for (name, ty) in exports.fields.iter() {
755                self.src
756                    .push_str(&format!("_{}: {}\n", name.to_snake_case(), ty));
757            }
758            for (id, r) in iface.resources.iter() {
759                self.src.push_str(&format!(
760                    "_resource{}_slab: Slab[{}]\n",
761                    id.index(),
762                    r.name.to_camel_case(),
763                ));
764                self.src.push_str(&format!(
765                    "_canonical_abi_drop_{}: wasmtime.Func\n",
766                    r.name.to_snake_case(),
767                ));
768            }
769
770            self.src.push_str("def __init__(self, store: wasmtime.Store, linker: wasmtime.Linker, module: wasmtime.Module):\n");
771            self.src.indent();
772            for (id, r) in iface.resources.iter() {
773                self.src.push_str(&format!(
774                    "
775                       ty1 = wasmtime.FuncType([wasmtime.ValType.i32()], [])
776                       ty2 = wasmtime.FuncType([wasmtime.ValType.i32()], [wasmtime.ValType.i32()])
777                       def drop_{snake}(caller: wasmtime.Caller, idx: int) -> None:
778                            self._resource{idx}_slab.remove(idx).drop(caller);
779                       linker.define('canonical_abi', 'resource_drop_{name}', wasmtime.Func(store, ty1, drop_{snake}, access_caller = True))
780
781                       def clone_{snake}(idx: int) -> int:
782                            obj = self._resource{idx}_slab.get(idx)
783                            return self._resource{idx}_slab.insert(obj.clone())
784                       linker.define('canonical_abi', 'resource_clone_{name}', wasmtime.Func(store, ty2, clone_{snake}))
785
786                       def get_{snake}(idx: int) -> int:
787                            obj = self._resource{idx}_slab.get(idx)
788                            return obj._wasm_val
789                       linker.define('canonical_abi', 'resource_get_{name}', wasmtime.Func(store, ty2, get_{snake}))
790
791                       def new_{snake}(val: int) -> int:
792                            return self._resource{idx}_slab.insert({camel}(val, self))
793                       linker.define('canonical_abi', 'resource_new_{name}', wasmtime.Func(store, ty2, new_{snake}))
794                   ",
795                    name = r.name,
796                    camel = r.name.to_camel_case(),
797                    snake = r.name.to_snake_case(),
798                    idx = id.index(),
799                ));
800            }
801            self.src
802                .push_str("self.instance = linker.instantiate(store, module)\n");
803            self.src
804                .push_str("exports = self.instance.exports(store)\n");
805            for (name, ty) in exports.fields.iter() {
806                self.src.push_str(&format!(
807                    "
808                        {snake} = exports['{name}']
809                        assert(isinstance({snake}, {ty}))
810                        self._{snake} = {snake}
811                    ",
812                    name = name,
813                    snake = name.to_snake_case(),
814                    ty = ty,
815                ));
816            }
817            for (id, r) in iface.resources.iter() {
818                self.src.push_str(&format!(
819                    "
820                        self._resource{idx}_slab = Slab()
821                        canon_drop_{snake} = exports['canonical_abi_drop_{name}']
822                        assert(isinstance(canon_drop_{snake}, wasmtime.Func))
823                        self._canonical_abi_drop_{snake} = canon_drop_{snake}
824                    ",
825                    idx = id.index(),
826                    name = r.name,
827                    snake = r.name.to_snake_case(),
828                ));
829            }
830            self.src.dedent();
831
832            for func in exports.freestanding_funcs.iter() {
833                self.src.push_str(func);
834            }
835
836            self.src.dedent();
837        }
838
839        files.push("bindings.py", self.src.as_bytes());
840    }
841}
842
843struct FunctionBindgen<'a> {
844    gen: &'a mut WasmtimePy,
845    locals: Ns,
846    src: Source,
847    block_storage: Vec<Source>,
848    blocks: Vec<(String, Vec<String>)>,
849    needs_memory: bool,
850    needs_realloc: Option<String>,
851    needs_free: Option<String>,
852    params: Vec<String>,
853    payloads: Vec<String>,
854    src_object: String,
855}
856
857impl FunctionBindgen<'_> {
858    fn new(gen: &mut WasmtimePy, params: Vec<String>) -> FunctionBindgen<'_> {
859        let mut locals = Ns::default();
860        locals.insert("len").unwrap(); // python built-in
861        locals.insert("base").unwrap(); // may be used as loop var
862        locals.insert("i").unwrap(); // may be used as loop var
863        for param in params.iter() {
864            locals.insert(param).unwrap();
865        }
866        FunctionBindgen {
867            gen,
868            locals,
869            src: Source::default(),
870            block_storage: Vec::new(),
871            blocks: Vec::new(),
872            needs_memory: false,
873            needs_realloc: None,
874            needs_free: None,
875            params,
876            payloads: Vec::new(),
877            src_object: "self".to_string(),
878        }
879    }
880
881    fn clamp<T>(&mut self, results: &mut Vec<String>, operands: &[String], min: T, max: T)
882    where
883        T: std::fmt::Display,
884    {
885        self.gen.deps.needs_clamp = true;
886        results.push(format!("_clamp({}, {}, {})", operands[0], min, max));
887    }
888
889    fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec<String>) {
890        self.needs_memory = true;
891        self.gen.deps.needs_load = true;
892        let tmp = self.locals.tmp("load");
893        self.src.push_str(&format!(
894            "{} = _load(ctypes.{}, memory, caller, {}, {})\n",
895            tmp, ty, operands[0], offset,
896        ));
897        results.push(tmp);
898    }
899
900    fn store(&mut self, ty: &str, offset: i32, operands: &[String]) {
901        self.needs_memory = true;
902        self.gen.deps.needs_store = true;
903        self.src.push_str(&format!(
904            "_store(ctypes.{}, memory, caller, {}, {}, {})\n",
905            ty, operands[1], offset, operands[0]
906        ));
907    }
908}
909
910impl Bindgen for FunctionBindgen<'_> {
911    type Operand = String;
912
913    fn sizes(&self) -> &SizeAlign {
914        &self.gen.sizes
915    }
916
917    fn push_block(&mut self) {
918        let prev = mem::take(&mut self.src);
919        self.block_storage.push(prev);
920    }
921
922    fn finish_block(&mut self, operands: &mut Vec<String>) {
923        let to_restore = self.block_storage.pop().unwrap();
924        let src = mem::replace(&mut self.src, to_restore);
925        self.blocks.push((src.into(), mem::take(operands)));
926    }
927
928    fn return_pointer(&mut self, _iface: &Interface, _size: usize, _align: usize) -> String {
929        unimplemented!()
930    }
931
932    fn is_list_canonical(&self, iface: &Interface, ty: &Type) -> bool {
933        array_ty(iface, ty).is_some()
934    }
935
936    fn emit(
937        &mut self,
938        iface: &Interface,
939        inst: &Instruction<'_>,
940        operands: &mut Vec<String>,
941        results: &mut Vec<String>,
942    ) {
943        let mut builder = self.src.builder(&mut self.gen.deps, iface);
944        match inst {
945            Instruction::GetArg { nth } => results.push(self.params[*nth].clone()),
946            Instruction::I32Const { val } => results.push(val.to_string()),
947            Instruction::ConstZero { tys } => {
948                for t in tys.iter() {
949                    match t {
950                        WasmType::I32 | WasmType::I64 => results.push("0".to_string()),
951                        WasmType::F32 | WasmType::F64 => results.push("0.0".to_string()),
952                    }
953                }
954            }
955
956            // The representation of i32 in Python is a number, so 8/16-bit
957            // values get further clamped to ensure that the upper bits aren't
958            // set when we pass the value, ensuring that only the right number
959            // of bits are transferred.
960            Instruction::U8FromI32 => self.clamp(results, operands, u8::MIN, u8::MAX),
961            Instruction::S8FromI32 => self.clamp(results, operands, i8::MIN, i8::MAX),
962            Instruction::U16FromI32 => self.clamp(results, operands, u16::MIN, u16::MAX),
963            Instruction::S16FromI32 => self.clamp(results, operands, i16::MIN, i16::MAX),
964            // Ensure the bits of the number are treated as unsigned.
965            Instruction::U32FromI32 => {
966                results.push(format!("{} & 0xffffffff", operands[0]));
967            }
968            // All bigints coming from wasm are treated as signed, so convert
969            // it to ensure it's treated as unsigned.
970            Instruction::U64FromI64 => {
971                results.push(format!("{} & 0xffffffffffffffff", operands[0]));
972            }
973            // Nothing to do signed->signed where the representations are the
974            // same.
975            Instruction::S32FromI32 | Instruction::S64FromI64 => {
976                results.push(operands.pop().unwrap())
977            }
978
979            // All values coming from the host and going to wasm need to have
980            // their ranges validated, since the host could give us any value.
981            Instruction::I32FromU8 => self.clamp(results, operands, u8::MIN, u8::MAX),
982            Instruction::I32FromS8 => self.clamp(results, operands, i8::MIN, i8::MAX),
983            Instruction::I32FromU16 => self.clamp(results, operands, u16::MIN, u16::MAX),
984            Instruction::I32FromS16 => self.clamp(results, operands, i16::MIN, i16::MAX),
985            // TODO: need to do something to get this to be represented as signed?
986            Instruction::I32FromU32 => {
987                self.clamp(results, operands, u32::MIN, u32::MAX);
988            }
989            Instruction::I32FromS32 => self.clamp(results, operands, i32::MIN, i32::MAX),
990            // TODO: need to do something to get this to be represented as signed?
991            Instruction::I64FromU64 => self.clamp(results, operands, u64::MIN, u64::MAX),
992            Instruction::I64FromS64 => self.clamp(results, operands, i64::MIN, i64::MAX),
993
994            // Python uses `float` for f32/f64, so everything is equivalent
995            // here.
996            Instruction::Float32FromF32
997            | Instruction::Float64FromF64
998            | Instruction::F32FromFloat32
999            | Instruction::F64FromFloat64 => results.push(operands.pop().unwrap()),
1000
1001            // Validate that i32 values coming from wasm are indeed valid code
1002            // points.
1003            Instruction::CharFromI32 => {
1004                builder.deps.needs_validate_guest_char = true;
1005                results.push(format!("_validate_guest_char({})", operands[0]));
1006            }
1007
1008            Instruction::I32FromChar => {
1009                results.push(format!("ord({})", operands[0]));
1010            }
1011
1012            Instruction::Bitcasts { casts } => {
1013                for (cast, op) in casts.iter().zip(operands) {
1014                    match cast {
1015                        Bitcast::I32ToF32 => {
1016                            builder.deps.needs_i32_to_f32 = true;
1017                            results.push(format!("_i32_to_f32({})", op));
1018                        }
1019                        Bitcast::F32ToI32 => {
1020                            builder.deps.needs_f32_to_i32 = true;
1021                            results.push(format!("_f32_to_i32({})", op));
1022                        }
1023                        Bitcast::I64ToF64 => {
1024                            builder.deps.needs_i64_to_f64 = true;
1025                            results.push(format!("_i64_to_f64({})", op));
1026                        }
1027                        Bitcast::F64ToI64 => {
1028                            builder.deps.needs_f64_to_i64 = true;
1029                            results.push(format!("_f64_to_i64({})", op));
1030                        }
1031                        Bitcast::I64ToF32 => {
1032                            builder.deps.needs_i32_to_f32 = true;
1033                            results.push(format!("_i32_to_f32(({}) & 0xffffffff)", op));
1034                        }
1035                        Bitcast::F32ToI64 => {
1036                            builder.deps.needs_f32_to_i32 = true;
1037                            results.push(format!("_f32_to_i32({})", op));
1038                        }
1039                        Bitcast::I32ToI64 | Bitcast::I64ToI32 | Bitcast::None => {
1040                            results.push(op.clone())
1041                        }
1042                    }
1043                }
1044            }
1045
1046            Instruction::UnitLower => {}
1047            Instruction::UnitLift => {
1048                results.push("None".to_string());
1049            }
1050            Instruction::BoolFromI32 => {
1051                let op = self.locals.tmp("operand");
1052                let ret = self.locals.tmp("boolean");
1053                builder.push_str(&format!(
1054                    "
1055                        {op} = {}
1056                        if {op} == 0:
1057                            {ret} = False
1058                        elif {op} == 1:
1059                            {ret} = True
1060                        else:
1061                            raise TypeError(\"invalid variant discriminant for bool\")
1062                    ",
1063                    operands[0]
1064                ));
1065                results.push(ret);
1066            }
1067            Instruction::I32FromBool => {
1068                results.push(format!("int({})", operands[0]));
1069            }
1070
1071            // These instructions are used with handles when we're implementing
1072            // imports. This means we interact with the `resources` slabs to
1073            // translate the wasm-provided index into a Python value.
1074            Instruction::I32FromOwnedHandle { ty } => {
1075                results.push(format!("_resources{}.insert({})", ty.index(), operands[0]));
1076            }
1077            Instruction::HandleBorrowedFromI32 { ty } => {
1078                results.push(format!("_resources{}.get({})", ty.index(), operands[0]));
1079            }
1080
1081            // These instructions are used for handles to objects owned in wasm.
1082            // This means that they're interacting with a wrapper class defined
1083            // in Python.
1084            Instruction::I32FromBorrowedHandle { ty } => {
1085                let obj = self.locals.tmp("obj");
1086                builder.push_str(&format!("{} = {}\n", obj, operands[0]));
1087
1088                results.push(format!(
1089                    "{}._resource{}_slab.insert({}.clone())",
1090                    self.src_object,
1091                    ty.index(),
1092                    obj,
1093                ));
1094            }
1095            Instruction::HandleOwnedFromI32 { ty } => {
1096                results.push(format!(
1097                    "{}._resource{}_slab.remove({})",
1098                    self.src_object,
1099                    ty.index(),
1100                    operands[0],
1101                ));
1102            }
1103            Instruction::RecordLower { record, .. } => {
1104                if record.fields.is_empty() {
1105                    return;
1106                }
1107                let tmp = self.locals.tmp("record");
1108                builder.push_str(&format!("{} = {}\n", tmp, operands[0]));
1109                for field in record.fields.iter() {
1110                    let name = self.locals.tmp("field");
1111                    builder.push_str(&format!(
1112                        "{} = {}.{}\n",
1113                        name,
1114                        tmp,
1115                        field.name.to_snake_case(),
1116                    ));
1117                    results.push(name);
1118                }
1119            }
1120
1121            Instruction::RecordLift { name, .. } => {
1122                results.push(format!("{}({})", name.to_camel_case(), operands.join(", ")));
1123            }
1124            Instruction::TupleLower { tuple, .. } => {
1125                if tuple.types.is_empty() {
1126                    return;
1127                }
1128                builder.push_str("(");
1129                for _ in 0..tuple.types.len() {
1130                    let name = self.locals.tmp("tuplei");
1131                    builder.push_str(&name);
1132                    builder.push_str(",");
1133                    results.push(name);
1134                }
1135                builder.push_str(") = ");
1136                builder.push_str(&operands[0]);
1137                builder.push_str("\n");
1138            }
1139            Instruction::TupleLift { .. } => {
1140                if operands.is_empty() {
1141                    results.push("None".to_string());
1142                } else {
1143                    results.push(format!("({},)", operands.join(", ")));
1144                }
1145            }
1146            Instruction::FlagsLift { name, .. } => {
1147                let operand = match operands.len() {
1148                    1 => operands[0].clone(),
1149                    _ => {
1150                        let tmp = self.locals.tmp("flags");
1151                        builder.push_str(&format!("{tmp} = 0\n"));
1152                        for (i, op) in operands.iter().enumerate() {
1153                            let i = 32 * i;
1154                            builder.push_str(&format!("{tmp} |= {op} << {i}\n"));
1155                        }
1156                        tmp
1157                    }
1158                };
1159                results.push(format!("{}({})", name.to_camel_case(), operand));
1160            }
1161            Instruction::FlagsLower { flags, .. } => match flags.repr().count() {
1162                1 => results.push(format!("({}).value", operands[0])),
1163                n => {
1164                    let tmp = self.locals.tmp("flags");
1165                    self.src
1166                        .push_str(&format!("{tmp} = ({}).value\n", operands[0]));
1167                    for i in 0..n {
1168                        let i = 32 * i;
1169                        results.push(format!("({tmp} >> {i}) & 0xffffffff"));
1170                    }
1171                }
1172            },
1173
1174            Instruction::VariantPayloadName => {
1175                let name = self.locals.tmp("payload");
1176                results.push(name.clone());
1177                self.payloads.push(name);
1178            }
1179
1180            Instruction::VariantLower {
1181                variant,
1182                results: result_types,
1183                name,
1184                ..
1185            } => {
1186                let blocks = self
1187                    .blocks
1188                    .drain(self.blocks.len() - variant.cases.len()..)
1189                    .collect::<Vec<_>>();
1190                let payloads = self
1191                    .payloads
1192                    .drain(self.payloads.len() - variant.cases.len()..)
1193                    .collect::<Vec<_>>();
1194
1195                for _ in 0..result_types.len() {
1196                    results.push(self.locals.tmp("variant"));
1197                }
1198
1199                for (i, ((case, (block, block_results)), payload)) in
1200                    variant.cases.iter().zip(blocks).zip(payloads).enumerate()
1201                {
1202                    if i == 0 {
1203                        builder.push_str("if ");
1204                    } else {
1205                        builder.push_str("elif ");
1206                    }
1207
1208                    builder.push_str(&format!(
1209                        "isinstance({}, {}{}):\n",
1210                        operands[0],
1211                        name.to_camel_case(),
1212                        case.name.to_camel_case()
1213                    ));
1214                    builder.indent();
1215                    builder.push_str(&format!("{} = {}.value\n", payload, operands[0]));
1216                    builder.push_str(&block);
1217
1218                    for (i, result) in block_results.iter().enumerate() {
1219                        builder.push_str(&format!("{} = {}\n", results[i], result));
1220                    }
1221                    builder.dedent();
1222                }
1223                let variant_name = name.to_camel_case();
1224                builder.push_str("else:\n");
1225                builder.indent();
1226                builder.push_str(&format!(
1227                    "raise TypeError(\"invalid variant specified for {}\")\n",
1228                    variant_name
1229                ));
1230                builder.dedent();
1231            }
1232
1233            Instruction::VariantLift {
1234                variant, name, ty, ..
1235            } => {
1236                let blocks = self
1237                    .blocks
1238                    .drain(self.blocks.len() - variant.cases.len()..)
1239                    .collect::<Vec<_>>();
1240
1241                let result = self.locals.tmp("variant");
1242                builder.print_var_declaration(&result, &Type::Id(*ty));
1243                for (i, (case, (block, block_results))) in
1244                    variant.cases.iter().zip(blocks).enumerate()
1245                {
1246                    if i == 0 {
1247                        builder.push_str("if ");
1248                    } else {
1249                        builder.push_str("elif ");
1250                    }
1251                    builder.push_str(&format!("{} == {}:\n", operands[0], i));
1252                    builder.indent();
1253                    builder.push_str(&block);
1254
1255                    builder.push_str(&format!(
1256                        "{} = {}{}(",
1257                        result,
1258                        name.to_camel_case(),
1259                        case.name.to_camel_case()
1260                    ));
1261                    assert!(block_results.len() == 1);
1262                    builder.push_str(&block_results[0]);
1263                    builder.push_str(")\n");
1264                    builder.dedent();
1265                }
1266                builder.push_str("else:\n");
1267                builder.indent();
1268                let variant_name = name.to_camel_case();
1269                builder.push_str(&format!(
1270                    "raise TypeError(\"invalid variant discriminant for {}\")\n",
1271                    variant_name
1272                ));
1273                builder.dedent();
1274                results.push(result);
1275            }
1276
1277            Instruction::UnionLower {
1278                union,
1279                results: result_types,
1280                name,
1281                ..
1282            } => {
1283                let blocks = self
1284                    .blocks
1285                    .drain(self.blocks.len() - union.cases.len()..)
1286                    .collect::<Vec<_>>();
1287                let payloads = self
1288                    .payloads
1289                    .drain(self.payloads.len() - union.cases.len()..)
1290                    .collect::<Vec<_>>();
1291
1292                for _ in 0..result_types.len() {
1293                    results.push(self.locals.tmp("variant"));
1294                }
1295
1296                // Assumes that type_union has been called for this union
1297                let union_representation = *self
1298                    .gen
1299                    .union_representation
1300                    .get(&name.to_string())
1301                    .unwrap();
1302                let name = name.to_camel_case();
1303                let op0 = &operands[0];
1304                for (i, ((case, (block, block_results)), payload)) in
1305                    union.cases.iter().zip(blocks).zip(payloads).enumerate()
1306                {
1307                    builder.push_str(if i == 0 { "if " } else { "elif " });
1308                    builder.push_str(&format!("isinstance({op0}, "));
1309                    match union_representation {
1310                        // Prints the Python type for this union case
1311                        PyUnionRepresentation::Raw => {
1312                            builder.print_ty(&case.ty, false);
1313                        }
1314                        // Prints the name of this union cases dataclass
1315                        PyUnionRepresentation::Wrapped => {
1316                            builder.push_str(&format!("{name}{i}"));
1317                        }
1318                    }
1319                    builder.push_str("):\n");
1320                    builder.indent();
1321                    match union_representation {
1322                        // Uses the value directly
1323                        PyUnionRepresentation::Raw => {
1324                            builder.push_str(&format!("{payload} = {op0}\n"))
1325                        }
1326                        // Uses this union case dataclass's inner value
1327                        PyUnionRepresentation::Wrapped => {
1328                            builder.push_str(&format!("{payload} = {op0}.value\n"))
1329                        }
1330                    }
1331                    builder.push_str(&block);
1332                    for (i, result) in block_results.iter().enumerate() {
1333                        builder.push_str(&format!("{} = {result}\n", results[i]));
1334                    }
1335                    builder.dedent();
1336                }
1337                builder.push_str("else:\n");
1338                builder.indent();
1339                builder.push_str(&format!(
1340                    "raise TypeError(\"invalid variant specified for {name}\")\n"
1341                ));
1342                builder.dedent();
1343            }
1344
1345            Instruction::UnionLift {
1346                union, name, ty, ..
1347            } => {
1348                let blocks = self
1349                    .blocks
1350                    .drain(self.blocks.len() - union.cases.len()..)
1351                    .collect::<Vec<_>>();
1352
1353                let result = self.locals.tmp("variant");
1354                builder.print_var_declaration(&result, &Type::Id(*ty));
1355                // Assumes that type_union has been called for this union
1356                let union_representation = *self
1357                    .gen
1358                    .union_representation
1359                    .get(&name.to_string())
1360                    .unwrap();
1361                let name = name.to_camel_case();
1362                let op0 = &operands[0];
1363                for (i, (_case, (block, block_results))) in
1364                    union.cases.iter().zip(blocks).enumerate()
1365                {
1366                    builder.push_str(if i == 0 { "if " } else { "elif " });
1367                    builder.push_str(&format!("{op0} == {i}:\n"));
1368                    builder.indent();
1369                    builder.push_str(&block);
1370                    assert!(block_results.len() == 1);
1371                    let block_result = &block_results[0];
1372                    builder.push_str(&format!("{result} = "));
1373                    match union_representation {
1374                        // Uses the passed value directly
1375                        PyUnionRepresentation::Raw => builder.push_str(block_result),
1376                        // Constructs an instance of the union cases dataclass
1377                        PyUnionRepresentation::Wrapped => {
1378                            builder.push_str(&format!("{name}{i}({block_result})"))
1379                        }
1380                    }
1381                    builder.newline();
1382                    builder.dedent();
1383                }
1384                builder.push_str("else:\n");
1385                builder.indent();
1386                builder.push_str(&format!(
1387                    "raise TypeError(\"invalid variant discriminant for {name}\")\n",
1388                ));
1389                builder.dedent();
1390                results.push(result);
1391            }
1392
1393            Instruction::OptionLower {
1394                results: result_types,
1395                ..
1396            } => {
1397                let (some, some_results) = self.blocks.pop().unwrap();
1398                let (none, none_results) = self.blocks.pop().unwrap();
1399                let some_payload = self.payloads.pop().unwrap();
1400                let _none_payload = self.payloads.pop().unwrap();
1401
1402                for _ in 0..result_types.len() {
1403                    results.push(self.locals.tmp("variant"));
1404                }
1405
1406                let op0 = &operands[0];
1407                builder.push_str(&format!("if {op0} is None:\n"));
1408
1409                builder.indent();
1410                builder.push_str(&none);
1411                for (dst, result) in results.iter().zip(&none_results) {
1412                    builder.push_str(&format!("{dst} = {result}\n"));
1413                }
1414                builder.dedent();
1415                builder.push_str("else:\n");
1416                builder.indent();
1417                builder.push_str(&format!("{some_payload} = {op0}\n"));
1418                builder.push_str(&some);
1419                for (dst, result) in results.iter().zip(&some_results) {
1420                    builder.push_str(&format!("{dst} = {result}\n"));
1421                }
1422                builder.dedent();
1423            }
1424
1425            Instruction::OptionLift { ty, .. } => {
1426                let (some, some_results) = self.blocks.pop().unwrap();
1427                let (none, none_results) = self.blocks.pop().unwrap();
1428                assert!(none_results.len() == 1);
1429                assert!(some_results.len() == 1);
1430                let some_result = &some_results[0];
1431                assert_eq!(none_results[0], "None");
1432
1433                let result = self.locals.tmp("option");
1434                builder.print_var_declaration(&result, &Type::Id(*ty));
1435
1436                let op0 = &operands[0];
1437                builder.push_str(&format!("if {op0} == 0:\n"));
1438                builder.indent();
1439                builder.push_str(&none);
1440                builder.push_str(&format!("{result} = None\n"));
1441                builder.dedent();
1442                builder.push_str(&format!("elif {op0} == 1:\n"));
1443                builder.indent();
1444                builder.push_str(&some);
1445                builder.push_str(&format!("{result} = {some_result}\n"));
1446                builder.dedent();
1447
1448                builder.push_str("else:\n");
1449                builder.indent();
1450                builder.push_str("raise TypeError(\"invalid variant discriminant for option\")\n");
1451                builder.dedent();
1452
1453                results.push(result);
1454            }
1455
1456            Instruction::ExpectedLower {
1457                results: result_types,
1458                ..
1459            } => {
1460                let (err, err_results) = self.blocks.pop().unwrap();
1461                let (ok, ok_results) = self.blocks.pop().unwrap();
1462                let err_payload = self.payloads.pop().unwrap();
1463                let ok_payload = self.payloads.pop().unwrap();
1464
1465                for _ in 0..result_types.len() {
1466                    results.push(self.locals.tmp("variant"));
1467                }
1468
1469                let op0 = &operands[0];
1470                builder.push_str(&format!("if isinstance({op0}, Ok):\n"));
1471
1472                builder.indent();
1473                builder.push_str(&format!("{ok_payload} = {op0}.value\n"));
1474                builder.push_str(&ok);
1475                for (dst, result) in results.iter().zip(&ok_results) {
1476                    builder.push_str(&format!("{dst} = {result}\n"));
1477                }
1478                builder.dedent();
1479                builder.push_str(&format!("elif isinstance({op0}, Err):\n"));
1480                builder.indent();
1481                builder.push_str(&format!("{err_payload} = {op0}.value\n"));
1482                builder.push_str(&err);
1483                for (dst, result) in results.iter().zip(&err_results) {
1484                    builder.push_str(&format!("{dst} = {result}\n"));
1485                }
1486                builder.dedent();
1487                builder.push_str("else:\n");
1488                builder.indent();
1489                builder.push_str("raise TypeError(\"invalid variant specified for expected\")\n");
1490                builder.dedent();
1491            }
1492
1493            Instruction::ExpectedLift { ty, .. } => {
1494                let (err, err_results) = self.blocks.pop().unwrap();
1495                let (ok, ok_results) = self.blocks.pop().unwrap();
1496                assert!(err_results.len() == 1);
1497                let err_result = &err_results[0];
1498                assert!(ok_results.len() == 1);
1499                let ok_result = &ok_results[0];
1500
1501                let result = self.locals.tmp("expected");
1502                builder.print_var_declaration(&result, &Type::Id(*ty));
1503
1504                let op0 = &operands[0];
1505                builder.push_str(&format!("if {op0} == 0:\n"));
1506                builder.indent();
1507                builder.push_str(&ok);
1508                builder.push_str(&format!("{result} = Ok({ok_result})\n"));
1509                builder.dedent();
1510                builder.push_str(&format!("elif {op0} == 1:\n"));
1511                builder.indent();
1512                builder.push_str(&err);
1513                builder.push_str(&format!("{result} = Err({err_result})\n"));
1514                builder.dedent();
1515
1516                builder.push_str("else:\n");
1517                builder.indent();
1518                builder
1519                    .push_str("raise TypeError(\"invalid variant discriminant for expected\")\n");
1520                builder.dedent();
1521
1522                results.push(result);
1523            }
1524
1525            Instruction::EnumLower { .. } => results.push(format!("({}).value", operands[0])),
1526
1527            Instruction::EnumLift { name, .. } => {
1528                results.push(format!("{}({})", name.to_camel_case(), operands[0]));
1529            }
1530
1531            Instruction::ListCanonLower { element, realloc } => {
1532                // Lowering only happens when we're passing lists into wasm,
1533                // which forces us to always allocate, so this should always be
1534                // `Some`.
1535                let realloc = realloc.unwrap();
1536                self.needs_memory = true;
1537                self.needs_realloc = Some(realloc.to_string());
1538
1539                let ptr = self.locals.tmp("ptr");
1540                let len = self.locals.tmp("len");
1541                let array_ty = array_ty(iface, element).unwrap();
1542                builder.deps.needs_list_canon_lower = true;
1543                let size = self.gen.sizes.size(element);
1544                let align = self.gen.sizes.align(element);
1545                builder.push_str(&format!(
1546                    "{}, {} = _list_canon_lower({}, ctypes.{}, {}, {}, realloc, memory, caller)\n",
1547                    ptr, len, operands[0], array_ty, size, align,
1548                ));
1549                results.push(ptr);
1550                results.push(len);
1551            }
1552            Instruction::ListCanonLift { element, free, .. } => {
1553                self.needs_memory = true;
1554                let ptr = self.locals.tmp("ptr");
1555                let len = self.locals.tmp("len");
1556                builder.push_str(&format!("{} = {}\n", ptr, operands[0]));
1557                builder.push_str(&format!("{} = {}\n", len, operands[1]));
1558                let array_ty = array_ty(iface, element).unwrap();
1559                builder.deps.needs_list_canon_lift = true;
1560                let lift = format!(
1561                    "_list_canon_lift({}, {}, {}, ctypes.{}, memory, caller)",
1562                    ptr,
1563                    len,
1564                    self.gen.sizes.size(element),
1565                    array_ty,
1566                );
1567                builder.deps.pyimport("typing", "cast");
1568                let align = self.gen.sizes.align(element);
1569                match free {
1570                    Some(free) => {
1571                        self.needs_free = Some(free.to_string());
1572                        let list = self.locals.tmp("list");
1573                        builder.push_str(&list);
1574                        builder.push_str(" = cast(");
1575                        builder.print_list(element);
1576                        builder.push_str(", ");
1577                        builder.push_str(&lift);
1578                        builder.push_str(")\n");
1579                        builder.push_str(&format!("free(caller, {}, {}, {})\n", ptr, len, align));
1580                        results.push(list);
1581                    }
1582                    None => {
1583                        let mut result_src = Source::default();
1584                        drop(builder);
1585                        let mut builder = result_src.builder(&mut self.gen.deps, iface);
1586                        builder.push_str("cast(");
1587                        builder.print_list(element);
1588                        builder.push_str(", ");
1589                        builder.push_str(&lift);
1590                        builder.push_str(")");
1591                        results.push(result_src.to_string());
1592                    }
1593                }
1594            }
1595            Instruction::StringLower { realloc } => {
1596                // Lowering only happens when we're passing strings into wasm,
1597                // which forces us to always allocate, so this should always be
1598                // `Some`.
1599                let realloc = realloc.unwrap();
1600                self.needs_memory = true;
1601                self.needs_realloc = Some(realloc.to_string());
1602
1603                let ptr = self.locals.tmp("ptr");
1604                let len = self.locals.tmp("len");
1605                builder.deps.needs_encode_utf8 = true;
1606                builder.push_str(&format!(
1607                    "{}, {} = _encode_utf8({}, realloc, memory, caller)\n",
1608                    ptr, len, operands[0],
1609                ));
1610                results.push(ptr);
1611                results.push(len);
1612            }
1613            Instruction::StringLift { free, .. } => {
1614                self.needs_memory = true;
1615                let ptr = self.locals.tmp("ptr");
1616                let len = self.locals.tmp("len");
1617                builder.push_str(&format!("{} = {}\n", ptr, operands[0]));
1618                builder.push_str(&format!("{} = {}\n", len, operands[1]));
1619                builder.deps.needs_decode_utf8 = true;
1620                let result = format!("_decode_utf8(memory, caller, {}, {})", ptr, len);
1621                match free {
1622                    Some(free) => {
1623                        self.needs_free = Some(free.to_string());
1624                        let list = self.locals.tmp("list");
1625                        builder.push_str(&format!("{} = {}\n", list, result));
1626                        self.src
1627                            .push_str(&format!("free(caller, {}, {}, 1)\n", ptr, len));
1628                        results.push(list);
1629                    }
1630                    None => results.push(result),
1631                }
1632            }
1633
1634            Instruction::ListLower { element, realloc } => {
1635                let base = self.payloads.pop().unwrap();
1636                let e = self.payloads.pop().unwrap();
1637                let realloc = realloc.unwrap();
1638                let (body, body_results) = self.blocks.pop().unwrap();
1639                assert!(body_results.is_empty());
1640                let vec = self.locals.tmp("vec");
1641                let result = self.locals.tmp("result");
1642                let len = self.locals.tmp("len");
1643                self.needs_realloc = Some(realloc.to_string());
1644                let size = self.gen.sizes.size(element);
1645                let align = self.gen.sizes.align(element);
1646
1647                // first store our vec-to-lower in a temporary since we'll
1648                // reference it multiple times.
1649                builder.push_str(&format!("{} = {}\n", vec, operands[0]));
1650                builder.push_str(&format!("{} = len({})\n", len, vec));
1651
1652                // ... then realloc space for the result in the guest module
1653                builder.push_str(&format!(
1654                    "{} = realloc(caller, 0, 0, {}, {} * {})\n",
1655                    result, align, len, size,
1656                ));
1657                builder.push_str(&format!("assert(isinstance({}, int))\n", result));
1658
1659                // ... then consume the vector and use the block to lower the
1660                // result.
1661                let i = self.locals.tmp("i");
1662                builder.push_str(&format!("for {} in range(0, {}):\n", i, len));
1663                builder.indent();
1664                builder.push_str(&format!("{} = {}[{}]\n", e, vec, i));
1665                builder.push_str(&format!("{} = {} + {} * {}\n", base, result, i, size));
1666                builder.push_str(&body);
1667                builder.dedent();
1668
1669                results.push(result);
1670                results.push(len);
1671            }
1672
1673            Instruction::ListLift { element, free, .. } => {
1674                let (body, body_results) = self.blocks.pop().unwrap();
1675                let base = self.payloads.pop().unwrap();
1676                let size = self.gen.sizes.size(element);
1677                let align = self.gen.sizes.align(element);
1678                let ptr = self.locals.tmp("ptr");
1679                let len = self.locals.tmp("len");
1680                builder.push_str(&format!("{} = {}\n", ptr, operands[0]));
1681                builder.push_str(&format!("{} = {}\n", len, operands[1]));
1682                let result = self.locals.tmp("result");
1683                builder.push_str(&format!("{}: List[", result));
1684                builder.print_ty(element, true);
1685                builder.push_str("] = []\n");
1686
1687                let i = self.locals.tmp("i");
1688                builder.push_str(&format!("for {} in range(0, {}):\n", i, len));
1689                builder.indent();
1690                builder.push_str(&format!("{} = {} + {} * {}\n", base, ptr, i, size));
1691                builder.push_str(&body);
1692                assert_eq!(body_results.len(), 1);
1693                builder.push_str(&format!("{}.append({})\n", result, body_results[0]));
1694                builder.dedent();
1695
1696                if let Some(free) = free {
1697                    self.needs_free = Some(free.to_string());
1698                    builder.push_str(&format!(
1699                        "free(caller, {}, {} * {}, {})\n",
1700                        ptr, len, size, align,
1701                    ));
1702                }
1703                results.push(result);
1704            }
1705
1706            Instruction::IterElem { .. } => {
1707                let name = self.locals.tmp("e");
1708                results.push(name.clone());
1709                self.payloads.push(name);
1710            }
1711            Instruction::IterBasePointer => {
1712                let name = self.locals.tmp("base");
1713                results.push(name.clone());
1714                self.payloads.push(name);
1715            }
1716            Instruction::CallWasm {
1717                iface: _,
1718                name,
1719                sig,
1720            } => {
1721                if !sig.results.is_empty() {
1722                    for i in 0..sig.results.len() {
1723                        if i > 0 {
1724                            builder.push_str(", ");
1725                        }
1726                        let ret = self.locals.tmp("ret");
1727                        builder.push_str(&ret);
1728                        results.push(ret);
1729                    }
1730                    builder.push_str(" = ");
1731                }
1732                builder.push_str(&self.src_object);
1733                builder.push_str("._");
1734                builder.push_str(&name.to_snake_case());
1735                builder.push_str("(caller");
1736                if !operands.is_empty() {
1737                    builder.push_str(", ");
1738                }
1739                builder.push_str(&operands.join(", "));
1740                builder.push_str(")\n");
1741                for (ty, name) in sig.results.iter().zip(results.iter()) {
1742                    let ty = match ty {
1743                        WasmType::I32 | WasmType::I64 => "int",
1744                        WasmType::F32 | WasmType::F64 => "float",
1745                    };
1746                    self.src
1747                        .push_str(&format!("assert(isinstance({}, {}))\n", name, ty));
1748                }
1749            }
1750            Instruction::CallInterface { module: _, func } => {
1751                match &func.result {
1752                    Type::Unit => {
1753                        results.push("".to_string());
1754                    }
1755                    _ => {
1756                        let result = self.locals.tmp("ret");
1757                        builder.push_str(&result);
1758                        results.push(result);
1759                        builder.push_str(" = ");
1760                    }
1761                }
1762                match &func.kind {
1763                    FunctionKind::Freestanding | FunctionKind::Static { .. } => {
1764                        builder.push_str(&format!(
1765                            "host.{}({})",
1766                            func.name.to_snake_case(),
1767                            operands.join(", "),
1768                        ));
1769                    }
1770                    FunctionKind::Method { name, .. } => {
1771                        builder.push_str(&format!(
1772                            "{}.{}({})",
1773                            operands[0],
1774                            name.to_snake_case(),
1775                            operands[1..].join(", "),
1776                        ));
1777                    }
1778                }
1779                builder.push_str("\n");
1780            }
1781
1782            Instruction::Return { amt, .. } => match amt {
1783                0 => {}
1784                1 => builder.push_str(&format!("return {}\n", operands[0])),
1785                _ => {
1786                    self.src
1787                        .push_str(&format!("return ({})\n", operands.join(", ")));
1788                }
1789            },
1790
1791            Instruction::I32Load { offset } => self.load("c_int32", *offset, operands, results),
1792            Instruction::I64Load { offset } => self.load("c_int64", *offset, operands, results),
1793            Instruction::F32Load { offset } => self.load("c_float", *offset, operands, results),
1794            Instruction::F64Load { offset } => self.load("c_double", *offset, operands, results),
1795            Instruction::I32Load8U { offset } => self.load("c_uint8", *offset, operands, results),
1796            Instruction::I32Load8S { offset } => self.load("c_int8", *offset, operands, results),
1797            Instruction::I32Load16U { offset } => self.load("c_uint16", *offset, operands, results),
1798            Instruction::I32Load16S { offset } => self.load("c_int16", *offset, operands, results),
1799            Instruction::I32Store { offset } => self.store("c_uint32", *offset, operands),
1800            Instruction::I64Store { offset } => self.store("c_uint64", *offset, operands),
1801            Instruction::F32Store { offset } => self.store("c_float", *offset, operands),
1802            Instruction::F64Store { offset } => self.store("c_double", *offset, operands),
1803            Instruction::I32Store8 { offset } => self.store("c_uint8", *offset, operands),
1804            Instruction::I32Store16 { offset } => self.store("c_uint16", *offset, operands),
1805
1806            Instruction::Malloc {
1807                realloc,
1808                size,
1809                align,
1810            } => {
1811                self.needs_realloc = Some(realloc.to_string());
1812                let ptr = self.locals.tmp("ptr");
1813                builder.push_str(&format!(
1814                    "
1815                        {ptr} = realloc(caller, 0, 0, {align}, {size})
1816                        assert(isinstance({ptr}, int))
1817                    ",
1818                ));
1819                results.push(ptr);
1820            }
1821
1822            i => unimplemented!("{:?}", i),
1823        }
1824    }
1825}
1826
1827fn py_type_class_of(ty: &Type) -> PyTypeClass {
1828    match ty {
1829        Type::Unit => PyTypeClass::None,
1830        Type::Bool
1831        | Type::U8
1832        | Type::U16
1833        | Type::U32
1834        | Type::U64
1835        | Type::S8
1836        | Type::S16
1837        | Type::S32
1838        | Type::S64 => PyTypeClass::Int,
1839        Type::Float32 | Type::Float64 => PyTypeClass::Float,
1840        Type::Char | Type::String => PyTypeClass::Str,
1841        Type::Handle(_) | Type::Id(_) => PyTypeClass::Custom,
1842    }
1843}
1844
1845#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1846enum PyTypeClass {
1847    None,
1848    Int,
1849    Str,
1850    Float,
1851    Custom,
1852}
1853
1854fn wasm_ty_ctor(ty: WasmType) -> &'static str {
1855    match ty {
1856        WasmType::I32 => "wasmtime.ValType.i32()",
1857        WasmType::I64 => "wasmtime.ValType.i64()",
1858        WasmType::F32 => "wasmtime.ValType.f32()",
1859        WasmType::F64 => "wasmtime.ValType.f64()",
1860    }
1861}
1862
1863fn wasm_ty_typing(ty: WasmType) -> &'static str {
1864    match ty {
1865        WasmType::I32 => "int",
1866        WasmType::I64 => "int",
1867        WasmType::F32 => "float",
1868        WasmType::F64 => "float",
1869    }
1870}