wasm_bindgen_cli_support/wit/
outgoing.rs

1use crate::descriptor::{Descriptor, Function};
2use crate::wasm_conventions::get_function_table_entry;
3use crate::wit::{AdapterType, Instruction, InstructionBuilder};
4use crate::wit::{InstructionData, StackChange};
5use anyhow::{bail, format_err, Error};
6use walrus::{ExportId, ValType};
7use wasm_bindgen_shared::identifier::to_valid_ident;
8
9impl InstructionBuilder<'_, '_> {
10    /// Processes one more `Descriptor` as an argument to a JS function that
11    /// Wasm is calling.
12    ///
13    /// This will internally skip `Unit` and otherwise build up the `bindings`
14    /// map and ensure that it's correctly mapped from Wasm to JS.
15    pub fn outgoing(&mut self, arg: &Descriptor) -> Result<(), Error> {
16        if let Descriptor::Unit = arg {
17            return Ok(());
18        }
19        // Similar rationale to `incoming.rs` around these sanity checks.
20        let input_before = self.input.len();
21        let output_before = self.output.len();
22        self._outgoing(arg)?;
23
24        assert!(input_before < self.input.len());
25        if let Descriptor::Result(arg) = arg {
26            if let Descriptor::Unit = &**arg {
27                assert_eq!(output_before, self.output.len());
28                return Ok(());
29            }
30        }
31        assert_eq!(output_before + 1, self.output.len());
32        Ok(())
33    }
34
35    fn _outgoing(&mut self, arg: &Descriptor) -> Result<(), Error> {
36        match arg {
37            Descriptor::Boolean => {
38                self.instruction(
39                    &[AdapterType::I32],
40                    Instruction::BoolFromI32,
41                    &[AdapterType::Bool],
42                );
43            }
44            Descriptor::Externref => {
45                self.instruction(
46                    &[AdapterType::I32],
47                    Instruction::ExternrefLoadOwned {
48                        table_and_drop: None,
49                    },
50                    &[AdapterType::Externref],
51                );
52            }
53            Descriptor::NamedExternref(name) => {
54                self.instruction(
55                    &[AdapterType::I32],
56                    Instruction::ExternrefLoadOwned {
57                        table_and_drop: None,
58                    },
59                    &[AdapterType::NamedExternref(name.clone())],
60                );
61            }
62            Descriptor::I8 => self.outgoing_i32(AdapterType::S8),
63            Descriptor::U8 => self.outgoing_i32(AdapterType::U8),
64            Descriptor::I16 => self.outgoing_i32(AdapterType::S16),
65            Descriptor::U16 => self.outgoing_i32(AdapterType::U16),
66            Descriptor::I32 => self.outgoing_i32(AdapterType::S32),
67            Descriptor::U32 => self.outgoing_i32(AdapterType::U32),
68            Descriptor::I64 => self.outgoing_i64(AdapterType::I64),
69            Descriptor::U64 => self.outgoing_i64(AdapterType::U64),
70            Descriptor::I128 => {
71                self.instruction(
72                    &[AdapterType::I64, AdapterType::I64],
73                    Instruction::WasmToInt128 { signed: true },
74                    &[AdapterType::S128],
75                );
76            }
77            Descriptor::U128 => {
78                self.instruction(
79                    &[AdapterType::I64, AdapterType::I64],
80                    Instruction::WasmToInt128 { signed: false },
81                    &[AdapterType::U128],
82                );
83            }
84            Descriptor::F32 => {
85                self.get(AdapterType::F32);
86                self.output.push(AdapterType::F32);
87            }
88            Descriptor::F64 => {
89                self.get(AdapterType::F64);
90                self.output.push(AdapterType::F64);
91            }
92            Descriptor::Enum { name, .. } => self.outgoing_i32(AdapterType::Enum(name.clone())),
93            Descriptor::StringEnum { name, .. } => self.outgoing_string_enum(name),
94
95            Descriptor::Char => {
96                self.instruction(
97                    &[AdapterType::I32],
98                    Instruction::StringFromChar,
99                    &[AdapterType::String],
100                );
101            }
102
103            Descriptor::RustStruct(class) => {
104                self.instruction(
105                    &[AdapterType::I32],
106                    Instruction::RustFromI32 {
107                        class: class.to_string(),
108                    },
109                    &[AdapterType::Struct(class.clone())],
110                );
111            }
112            Descriptor::Ref(d) => self.outgoing_ref(false, d)?,
113            Descriptor::RefMut(d) => self.outgoing_ref(true, d)?,
114
115            Descriptor::CachedString => self.cached_string(true)?,
116
117            Descriptor::String => {
118                // fetch the ptr/length ...
119                self.get(AdapterType::I32);
120                self.get(AdapterType::I32);
121
122                // ... then defer a call to `free` to happen later
123                let free = self.cx.free()?;
124                self.instructions.push(InstructionData {
125                    instr: Instruction::DeferFree { free, align: 1 },
126                    stack_change: StackChange::Modified {
127                        popped: 2,
128                        pushed: 2,
129                    },
130                });
131
132                // ... and then convert it to a string type
133                self.instructions.push(InstructionData {
134                    instr: Instruction::MemoryToString(self.cx.memory()?),
135                    stack_change: StackChange::Modified {
136                        popped: 2,
137                        pushed: 1,
138                    },
139                });
140                self.output.push(AdapterType::String);
141            }
142
143            Descriptor::Vector(_) => {
144                let kind = arg.vector_kind().ok_or_else(|| {
145                    format_err!(
146                        "unsupported argument type for calling JS function from Rust {arg:?}"
147                    )
148                })?;
149                let mem = self.cx.memory()?;
150                let free = self.cx.free()?;
151                self.instruction(
152                    &[AdapterType::I32, AdapterType::I32],
153                    Instruction::VectorLoad {
154                        kind: kind.clone(),
155                        mem,
156                        free,
157                    },
158                    &[AdapterType::Vector(kind)],
159                );
160            }
161
162            Descriptor::Option(d) => self.outgoing_option(d)?,
163            Descriptor::Result(d) => self.outgoing_result(d)?,
164
165            Descriptor::Function(_) | Descriptor::Slice(_) => {
166                bail!("unsupported argument type for calling JS function from Rust: {arg:?}")
167            }
168
169            // nothing to do
170            Descriptor::Unit => {}
171
172            // Largely synthetic and can't show up
173            Descriptor::ClampedU8 => unreachable!(),
174
175            Descriptor::NonNull => self.outgoing_i32(AdapterType::NonNull),
176
177            Descriptor::Closure(d) => {
178                self.outgoing_function(d.mutable, &d.function, Some(d.dtor_idx))?
179            }
180        }
181        Ok(())
182    }
183
184    fn outgoing_ref(&mut self, mutable: bool, arg: &Descriptor) -> Result<(), Error> {
185        match arg {
186            Descriptor::Externref => {
187                self.instruction(
188                    &[AdapterType::I32],
189                    Instruction::TableGet,
190                    &[AdapterType::Externref],
191                );
192            }
193            Descriptor::NamedExternref(name) => {
194                self.instruction(
195                    &[AdapterType::I32],
196                    Instruction::TableGet,
197                    &[AdapterType::NamedExternref(name.clone())],
198                );
199            }
200            Descriptor::CachedString => self.cached_string(false)?,
201
202            Descriptor::String => {
203                self.instruction(
204                    &[AdapterType::I32, AdapterType::I32],
205                    Instruction::MemoryToString(self.cx.memory()?),
206                    &[AdapterType::String],
207                );
208            }
209            Descriptor::Slice(_) => {
210                let kind = arg.vector_kind().ok_or_else(|| {
211                    format_err!(
212                        "unsupported argument type for calling JS function from Rust {arg:?}"
213                    )
214                })?;
215                let mem = self.cx.memory()?;
216                self.instruction(
217                    &[AdapterType::I32, AdapterType::I32],
218                    Instruction::View {
219                        kind: kind.clone(),
220                        mem,
221                    },
222                    &[AdapterType::Vector(kind)],
223                );
224            }
225
226            Descriptor::Function(descriptor) => {
227                self.outgoing_function(mutable, descriptor, None)?;
228            }
229
230            _ => bail!(
231                "unsupported reference argument type for calling JS function from Rust: {arg:?}"
232            ),
233        }
234        Ok(())
235    }
236
237    // The function table never changes right now, so we can statically
238    // look up the desired function.
239    fn export_table_element(&mut self, idx: u32) -> ExportId {
240        let module = &mut *self.cx.module;
241        let func_id = get_function_table_entry(module, idx).unwrap();
242        if let Some(export) = module
243            .exports
244            .iter()
245            .find(|e| matches!(e.item, walrus::ExportItem::Function(id) if id == func_id))
246        {
247            return export.id();
248        }
249        let name = match &module.funcs.get(func_id).name {
250            Some(name) => to_valid_ident(name),
251            None => format!("__wasm_bindgen_func_elem_{}", func_id.index()),
252        };
253        module.exports.add(&name, func_id)
254    }
255
256    fn outgoing_function(
257        &mut self,
258        mutable: bool,
259        descriptor: &Function,
260        dtor_if_persistent: Option<u32>,
261    ) -> Result<(), Error> {
262        let mut descriptor = descriptor.clone();
263        // synthesize the a/b arguments that aren't present in the
264        // signature from wasm-bindgen but are present in the Wasm file.
265        let nargs = descriptor.arguments.len();
266        descriptor.arguments.insert(0, Descriptor::I32);
267        descriptor.arguments.insert(0, Descriptor::I32);
268        let shim = self.export_table_element(descriptor.shim_idx);
269        let dtor_if_persistent = dtor_if_persistent.map(|i| self.export_table_element(i));
270        let adapter = self.cx.export_adapter(shim, descriptor)?;
271        self.instruction(
272            &[AdapterType::I32, AdapterType::I32],
273            Instruction::Closure {
274                adapter,
275                nargs,
276                mutable,
277                dtor_if_persistent,
278            },
279            &[AdapterType::Function],
280        );
281        Ok(())
282    }
283
284    fn outgoing_option(&mut self, arg: &Descriptor) -> Result<(), Error> {
285        match arg {
286            Descriptor::Externref => {
287                // This is set to `undefined` in the `None` case and otherwise
288                // is the valid owned index.
289                self.instruction(
290                    &[AdapterType::I32],
291                    Instruction::ExternrefLoadOwned {
292                        table_and_drop: None,
293                    },
294                    &[AdapterType::Externref.option()],
295                );
296            }
297            Descriptor::NamedExternref(name) => {
298                self.instruction(
299                    &[AdapterType::I32],
300                    Instruction::ExternrefLoadOwned {
301                        table_and_drop: None,
302                    },
303                    &[AdapterType::NamedExternref(name.clone()).option()],
304                );
305            }
306            Descriptor::I8 => self.out_option_sentinel32(AdapterType::S8),
307            Descriptor::U8 => self.out_option_sentinel32(AdapterType::U8),
308            Descriptor::I16 => self.out_option_sentinel32(AdapterType::S16),
309            Descriptor::U16 => self.out_option_sentinel32(AdapterType::U16),
310            Descriptor::I32 => self.out_option_sentinel64(AdapterType::S32),
311            Descriptor::U32 => self.out_option_sentinel64(AdapterType::U32),
312            Descriptor::I64 => self.option_native(true, ValType::I64),
313            Descriptor::U64 => self.option_native(false, ValType::I64),
314            Descriptor::F32 => self.out_option_sentinel64(AdapterType::F32),
315            Descriptor::F64 => self.option_native(true, ValType::F64),
316            Descriptor::I128 => {
317                self.instruction(
318                    &[AdapterType::I32, AdapterType::I64, AdapterType::I64],
319                    Instruction::OptionWasmToInt128 { signed: true },
320                    &[AdapterType::S128.option()],
321                );
322            }
323            Descriptor::U128 => {
324                self.instruction(
325                    &[AdapterType::I32, AdapterType::I64, AdapterType::I64],
326                    Instruction::OptionWasmToInt128 { signed: false },
327                    &[AdapterType::U128.option()],
328                );
329            }
330            Descriptor::Boolean => {
331                self.instruction(
332                    &[AdapterType::I32],
333                    Instruction::OptionBoolFromI32,
334                    &[AdapterType::Bool.option()],
335                );
336            }
337            Descriptor::Char => {
338                self.instruction(
339                    &[AdapterType::I32],
340                    Instruction::OptionCharFromI32,
341                    &[AdapterType::String.option()],
342                );
343            }
344            Descriptor::Enum { name, hole } => {
345                self.instruction(
346                    &[AdapterType::I32],
347                    Instruction::OptionEnumFromI32 { hole: *hole },
348                    &[AdapterType::Enum(name.clone()).option()],
349                );
350            }
351            Descriptor::StringEnum { name, .. } => {
352                self.instruction(
353                    &[AdapterType::I32],
354                    Instruction::OptionWasmToStringEnum { name: name.clone() },
355                    &[AdapterType::StringEnum(name.clone()).option()],
356                );
357            }
358            Descriptor::RustStruct(name) => {
359                self.instruction(
360                    &[AdapterType::I32],
361                    Instruction::OptionRustFromI32 {
362                        class: name.to_string(),
363                    },
364                    &[AdapterType::Struct(name.clone()).option()],
365                );
366            }
367            Descriptor::Ref(d) => self.outgoing_option_ref(false, d)?,
368            Descriptor::RefMut(d) => self.outgoing_option_ref(true, d)?,
369
370            Descriptor::CachedString => self.cached_string(true)?,
371
372            Descriptor::String | Descriptor::Vector(_) => {
373                let kind = arg.vector_kind().ok_or_else(|| {
374                    format_err!(
375                        "unsupported optional slice type for calling JS function from Rust {arg:?}"
376                    )
377                })?;
378                let mem = self.cx.memory()?;
379                let free = self.cx.free()?;
380                self.instruction(
381                    &[AdapterType::I32, AdapterType::I32],
382                    Instruction::OptionVectorLoad {
383                        kind: kind.clone(),
384                        mem,
385                        free,
386                    },
387                    &[AdapterType::Vector(kind).option()],
388                );
389            }
390
391            Descriptor::NonNull => self.instruction(
392                &[AdapterType::I32],
393                Instruction::OptionNonNullFromI32,
394                &[AdapterType::NonNull.option()],
395            ),
396
397            _ => bail!(
398                "unsupported optional argument type for calling JS function from Rust: {arg:?}"
399            ),
400        }
401        Ok(())
402    }
403
404    fn outgoing_result(&mut self, arg: &Descriptor) -> Result<(), Error> {
405        match arg {
406            Descriptor::Externref
407            | Descriptor::NamedExternref(_)
408            | Descriptor::I8
409            | Descriptor::U8
410            | Descriptor::I16
411            | Descriptor::U16
412            | Descriptor::I32
413            | Descriptor::U32
414            | Descriptor::F32
415            | Descriptor::F64
416            | Descriptor::I64
417            | Descriptor::U64
418            | Descriptor::I128
419            | Descriptor::U128
420            | Descriptor::Boolean
421            | Descriptor::Char
422            | Descriptor::Enum { .. }
423            | Descriptor::StringEnum { .. }
424            | Descriptor::RustStruct(_)
425            | Descriptor::Ref(_)
426            | Descriptor::RefMut(_)
427            | Descriptor::CachedString
428            | Descriptor::Option(_)
429            | Descriptor::Vector(_)
430            | Descriptor::Unit
431            | Descriptor::NonNull => {
432                // We must throw before reading the Ok type, if there is an error. However, the
433                // structure of ResultAbi is that the Err value + discriminant come last (for
434                // alignment reasons). So the UnwrapResult instruction must come first, but the
435                // inputs must be read last.
436                //
437                // So first, push an UnwrapResult instruction without modifying the inputs list.
438                //
439                //     []
440                //     -------------------------<
441                //     UnwrapResult { popped: 2 }
442                //
443                self.instructions.push(InstructionData {
444                    instr: Instruction::UnwrapResult {
445                        table_and_drop: None,
446                    },
447                    stack_change: StackChange::Modified {
448                        popped: 2,
449                        pushed: 0,
450                    },
451                });
452
453                // Then push whatever else you were going to do, modifying the inputs and
454                // instructions.
455                //
456                //     [f64, u32, u32]
457                //     -------------------------<
458                //     UnwrapResult { popped: 2 }
459                //     SomeOtherInstruction { popped: 3 }
460                //
461                // The popped numbers don't add up yet (3 != 5), but they will.
462                let len = self.instructions.len();
463                self._outgoing(arg)?;
464
465                // check we did not add any deferred calls, because we have undermined the idea of
466                // running them unconditionally in a finally {} block. String does this, but we
467                // special case it.
468                assert!(!self.instructions[len..]
469                    .iter()
470                    .any(|idata| matches!(idata.instr, Instruction::DeferFree { .. })));
471
472                // Finally, we add the two inputs to UnwrapResult, and everything checks out
473                //
474                //     [f64, u32, u32, u32, u32]
475                //     -------------------------<
476                //     UnwrapResult { popped: 2 }
477                //     SomeOtherInstruction { popped: 3 }
478                //
479                self.get(AdapterType::I32);
480                self.get(AdapterType::I32);
481            }
482            Descriptor::String => {
483                // fetch the ptr/length ...
484                self.get(AdapterType::I32);
485                self.get(AdapterType::I32);
486                // fetch the err/is_err
487                self.get(AdapterType::I32);
488                self.get(AdapterType::I32);
489
490                self.instructions.push(InstructionData {
491                    instr: Instruction::UnwrapResultString {
492                        table_and_drop: None,
493                    },
494                    stack_change: StackChange::Modified {
495                        // 2 from UnwrapResult, 2 from ptr/len
496                        popped: 4,
497                        // pushes the ptr/len back on
498                        pushed: 2,
499                    },
500                });
501
502                // ... then defer a call to `free` to happen later
503                // this will run string's DeferCallCore with the length parameter, but if is_err,
504                // then we have never written anything into that, so it is poison. So we'll have to
505                // make sure we call it with length 0, which according to __wbindgen_free's
506                // implementation is always safe. We do this in UnwrapResultString's
507                // implementation.
508                let free = self.cx.free()?;
509                self.instructions.push(InstructionData {
510                    instr: Instruction::DeferFree { free, align: 1 },
511                    stack_change: StackChange::Modified {
512                        popped: 2,
513                        pushed: 2,
514                    },
515                });
516
517                // ... and then convert it to a string type
518                self.instructions.push(InstructionData {
519                    instr: Instruction::MemoryToString(self.cx.memory()?),
520                    stack_change: StackChange::Modified {
521                        popped: 2,
522                        pushed: 1,
523                    },
524                });
525                self.output.push(AdapterType::String);
526            }
527
528            Descriptor::ClampedU8
529            | Descriptor::Function(_)
530            | Descriptor::Closure(_)
531            | Descriptor::Slice(_)
532            | Descriptor::Result(_) => {
533                bail!("unsupported Result type for returning from exported Rust function: {arg:?}")
534            }
535        }
536        Ok(())
537    }
538
539    fn outgoing_option_ref(&mut self, _mutable: bool, arg: &Descriptor) -> Result<(), Error> {
540        match arg {
541            Descriptor::Externref => {
542                // If this is `Some` then it's the index, otherwise if it's
543                // `None` then it's the index pointing to undefined.
544                self.instruction(
545                    &[AdapterType::I32],
546                    Instruction::TableGet,
547                    &[AdapterType::Externref.option()],
548                );
549            }
550            Descriptor::NamedExternref(name) => {
551                self.instruction(
552                    &[AdapterType::I32],
553                    Instruction::TableGet,
554                    &[AdapterType::NamedExternref(name.clone()).option()],
555                );
556            }
557            Descriptor::CachedString => self.cached_string(false)?,
558            Descriptor::String | Descriptor::Slice(_) => {
559                let kind = arg.vector_kind().ok_or_else(|| {
560                    format_err!(
561                        "unsupported optional slice type for calling JS function from Rust {arg:?}"
562                    )
563                })?;
564                let mem = self.cx.memory()?;
565                self.instruction(
566                    &[AdapterType::I32, AdapterType::I32],
567                    Instruction::OptionView {
568                        kind: kind.clone(),
569                        mem,
570                    },
571                    &[AdapterType::Vector(kind).option()],
572                );
573            }
574            _ => bail!(
575                "unsupported optional ref argument type for calling JS function from Rust: {arg:?}"
576            ),
577        }
578        Ok(())
579    }
580
581    fn outgoing_string_enum(&mut self, name: &str) {
582        self.instruction(
583            &[AdapterType::I32],
584            Instruction::WasmToStringEnum {
585                name: name.to_string(),
586            },
587            &[AdapterType::StringEnum(name.to_string())],
588        );
589    }
590
591    fn outgoing_i32(&mut self, output: AdapterType) {
592        let instr = Instruction::WasmToInt32 {
593            unsigned_32: output == AdapterType::U32 || output == AdapterType::NonNull,
594        };
595        self.instruction(&[AdapterType::I32], instr, &[output]);
596    }
597    fn outgoing_i64(&mut self, output: AdapterType) {
598        let instr = Instruction::WasmToInt64 {
599            unsigned: output == AdapterType::U64,
600        };
601        self.instruction(&[AdapterType::I64], instr, &[output]);
602    }
603
604    fn cached_string(&mut self, owned: bool) -> Result<(), Error> {
605        let mem = self.cx.memory()?;
606        let free = self.cx.free()?;
607        self.instruction(
608            &[AdapterType::I32, AdapterType::I32],
609            Instruction::CachedStringLoad {
610                owned,
611                mem,
612                free,
613                table: None,
614            },
615            &[AdapterType::String],
616        );
617        Ok(())
618    }
619
620    fn option_native(&mut self, signed: bool, ty: ValType) {
621        let adapter_ty = AdapterType::from_wasm(ty).unwrap();
622        self.instruction(
623            &[AdapterType::I32, adapter_ty.clone()],
624            Instruction::ToOptionNative { signed, ty },
625            &[adapter_ty.option()],
626        );
627    }
628
629    fn out_option_sentinel32(&mut self, ty: AdapterType) {
630        self.instruction(
631            &[AdapterType::I32],
632            Instruction::OptionU32Sentinel,
633            &[ty.option()],
634        );
635    }
636
637    fn out_option_sentinel64(&mut self, ty: AdapterType) {
638        self.instruction(
639            &[AdapterType::F64],
640            Instruction::OptionF64Sentinel,
641            &[ty.option()],
642        );
643    }
644}