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 pub fn outgoing(&mut self, arg: &Descriptor) -> Result<(), Error> {
16 if let Descriptor::Unit = arg {
17 return Ok(());
18 }
19 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 self.get(AdapterType::I32);
120 self.get(AdapterType::I32);
121
122 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 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 Descriptor::Unit => {}
171
172 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 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 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 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 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 let len = self.instructions.len();
463 self._outgoing(arg)?;
464
465 assert!(!self.instructions[len..]
469 .iter()
470 .any(|idata| matches!(idata.instr, Instruction::DeferFree { .. })));
471
472 self.get(AdapterType::I32);
480 self.get(AdapterType::I32);
481 }
482 Descriptor::String => {
483 self.get(AdapterType::I32);
485 self.get(AdapterType::I32);
486 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 popped: 4,
497 pushed: 2,
499 },
500 });
501
502 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 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 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}