diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index c01754c045b..0e36efedbff 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -15,10 +15,6 @@ use walrus::Module; pub struct Builder<'a, 'b> { /// Parent context used to expose helper functions and such. pub cx: &'a mut Context<'b>, - /// The TypeScript definition for each argument to this function. - pub ts_args: Vec, - /// The TypeScript return value for this function. - pub ts_ret: Option, /// Whether or not this is building a constructor for a Rust class, and if /// so what class it's constructing. constructor: Option, @@ -39,10 +35,6 @@ pub struct JsBuilder<'a, 'b> { /// JS functions, etc. cx: &'a mut Context<'b>, - /// The list of typescript arguments that we're going to have to this - /// function. - typescript: Vec, - /// The "prelude" of the function, or largely just the JS function we've /// built so far. prelude: String, @@ -66,10 +58,12 @@ pub struct JsBuilder<'a, 'b> { stack: Vec, } -pub struct TypescriptArg { - pub ty: String, - pub name: String, - pub optional: bool, +pub struct JsFunction { + pub code: String, + pub ts_sig: String, + pub js_doc: String, + pub ts_arg_tys: Vec, + pub ts_ret_ty: Option, } impl<'a, 'b> Builder<'a, 'b> { @@ -77,8 +71,6 @@ impl<'a, 'b> Builder<'a, 'b> { Builder { log_error: false, cx, - ts_args: Vec::new(), - ts_ret: None, constructor: None, method: None, catch: false, @@ -106,7 +98,7 @@ impl<'a, 'b> Builder<'a, 'b> { adapter: &Adapter, instructions: &[InstructionData], explicit_arg_names: &Option>, - ) -> Result { + ) -> Result { if self .cx .aux @@ -118,6 +110,7 @@ impl<'a, 'b> Builder<'a, 'b> { let mut params = adapter.params.iter(); let mut function_args = Vec::new(); + let mut arg_tys = Vec::new(); // If this is a method then we're generating this as part of a class // method, so the leading parameter is the this pointer stored on @@ -141,13 +134,14 @@ impl<'a, 'b> Builder<'a, 'b> { } None => {} } - for (i, _param) in params.enumerate() { + for (i, param) in params.enumerate() { let arg = match explicit_arg_names { Some(list) => list[i].clone(), None => format!("arg{}", i), }; js.args.push(arg.clone()); function_args.push(arg); + arg_tys.push(param); } // Translate all instructions, the fun loop! @@ -170,7 +164,6 @@ impl<'a, 'b> Builder<'a, 'b> { match js.stack.len() { 0 => {} 1 => { - self.ts_ret = js.typescript.pop(); let val = js.pop(); js.prelude(&format!("return {};", val)); } @@ -188,18 +181,17 @@ impl<'a, 'b> Builder<'a, 'b> { // } } assert!(js.stack.is_empty()); - self.ts_args = js.typescript; - // Remove extraneous typescript args which were synthesized and aren't - // part of our function shim. - while self.ts_args.len() > function_args.len() { - self.ts_args.remove(0); - } + // // Remove extraneous typescript args which were synthesized and aren't + // // part of our function shim. + // while self.ts_args.len() > function_args.len() { + // self.ts_args.remove(0); + // } - let mut ret = String::new(); - ret.push_str("("); - ret.push_str(&function_args.join(", ")); - ret.push_str(") {\n"); + let mut code = String::new(); + code.push_str("("); + code.push_str(&function_args.join(", ")); + code.push_str(") {\n"); let mut call = js.prelude; if js.finally.len() != 0 { @@ -220,10 +212,20 @@ impl<'a, 'b> Builder<'a, 'b> { call = format!("try {{\n{}}} catch (e) {{\n logError(e)\n}}\n", call); } - ret.push_str(&call); - ret.push_str("}"); + code.push_str(&call); + code.push_str("}"); - return Ok(ret); + let (ts_sig, ts_arg_tys, ts_ret_ty) = + self.typescript_signature(&function_args, &arg_tys, &adapter.results); + let js_doc = self.js_doc_comments(&function_args, &arg_tys, &ts_ret_ty); + + Ok(JsFunction { + code, + ts_sig, + js_doc, + ts_arg_tys, + ts_ret_ty, + }) } /// Returns the typescript signature of the binding that this has described. @@ -231,61 +233,77 @@ impl<'a, 'b> Builder<'a, 'b> { /// /// Note that the TypeScript returned here is just the argument list and the /// return value, it doesn't include the function name in any way. - pub fn typescript_signature(&self) -> String { + fn typescript_signature( + &self, + arg_names: &[String], + arg_tys: &[&AdapterType], + result_tys: &[AdapterType], + ) -> (String, Vec, Option) { // Build up the typescript signature as well let mut omittable = true; let mut ts_args = Vec::new(); - for arg in self.ts_args.iter().rev() { + let mut ts_arg_tys = Vec::new(); + for (name, ty) in arg_names.iter().zip(arg_tys).rev() { // In TypeScript, we can mark optional parameters as omittable // using the `?` suffix, but only if they're not followed by // non-omittable parameters. Therefore iterate the parameter list // in reverse and stop using the `?` suffix for optional params as // soon as a non-optional parameter is encountered. - if arg.optional { - if omittable { - ts_args.push(format!("{}?: {}", arg.name, arg.ty)); - } else { - ts_args.push(format!("{}: {} | undefined", arg.name, arg.ty)); + let mut arg = name.to_string(); + let mut ts = String::new(); + match ty { + AdapterType::Option(ty) if omittable => { + arg.push_str("?: "); + adapter2ts(ty, &mut ts); + } + ty => { + omittable = false; + arg.push_str(": "); + adapter2ts(ty, &mut ts); } - } else { - omittable = false; - ts_args.push(format!("{}: {}", arg.name, arg.ty)); } + arg.push_str(&ts); + ts_arg_tys.push(ts); + ts_args.push(arg); } ts_args.reverse(); + ts_arg_tys.reverse(); let mut ts = format!("({})", ts_args.join(", ")); // Constructors have no listed return type in typescript + let mut ts_ret = None; if self.constructor.is_none() { ts.push_str(": "); - if let Some(ty) = &self.ts_ret { - ts.push_str(&ty.ty); - if ty.optional { - ts.push_str(" | undefined"); - } - } else { - ts.push_str("void"); + let mut ret = String::new(); + match result_tys.len() { + 0 => ret.push_str("void"), + 1 => adapter2ts(&result_tys[0], &mut ret), + _ => ret.push_str("[any]"), } + ts.push_str(&ret); + ts_ret = Some(ret); } - return ts; + return (ts, ts_arg_tys, ts_ret); } /// Returns a helpful JS doc comment which lists types for all parameters /// and the return value. - pub fn js_doc_comments(&self) -> String { - let mut ret: String = self - .ts_args - .iter() - .map(|a| { - if a.optional { - format!("@param {{{} | undefined}} {}\n", a.ty, a.name) - } else { - format!("@param {{{}}} {}\n", a.ty, a.name) - } - }) - .collect(); - if let Some(ts) = &self.ts_ret { - ret.push_str(&format!("@returns {{{}}}", ts.ty)); + fn js_doc_comments( + &self, + arg_names: &[String], + arg_tys: &[&AdapterType], + ts_ret: &Option, + ) -> String { + let mut ret = String::new(); + for (name, ty) in arg_names.iter().zip(arg_tys) { + ret.push_str("@param {"); + adapter2ts(ty, &mut ret); + ret.push_str("} "); + ret.push_str(name); + ret.push_str("\n"); + } + if let Some(ts) = ts_ret { + ret.push_str(&format!("@returns {{{}}}", ts)); } ret } @@ -299,7 +317,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> { tmp: 0, finally: String::new(), prelude: String::new(), - typescript: Vec::new(), stack: Vec::new(), } } @@ -308,31 +325,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> { &self.args[idx as usize] } - pub fn typescript_required(&mut self, ty: &str) { - let name = self.arg_name(); - self.typescript.push(TypescriptArg { - ty: ty.to_string(), - optional: false, - name, - }); - } - - pub fn typescript_optional(&mut self, ty: &str) { - let name = self.arg_name(); - self.typescript.push(TypescriptArg { - ty: ty.to_string(), - optional: true, - name, - }); - } - - fn arg_name(&self) -> String { - self.args - .get(self.typescript.len()) - .cloned() - .unwrap_or_else(|| format!("arg{}", self.typescript.len())) - } - pub fn prelude(&mut self, prelude: &str) { for line in prelude.trim().lines().map(|l| l.trim()) { if !line.is_empty() { @@ -426,7 +418,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> { malloc: walrus::FunctionId, realloc: Option, ) -> Result<(), Error> { - self.typescript_required("string"); let pass = self.cx.expose_pass_string_to_wasm(mem)?; let val = self.pop(); let malloc = self.cx.export_name_of(malloc); @@ -510,7 +501,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::Standard(wit_walrus::Instruction::IntToWasm { trap: false, .. }) => { - js.typescript_required("number"); let val = js.pop(); js.assert_number(&val); js.push(val); @@ -524,7 +514,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> output, .. }) => { - js.typescript_required("number"); let val = js.pop(); match output { wit_walrus::ValType::U32 => js.push(format!("{} >>> 0", val)), @@ -538,7 +527,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::Standard(wit_walrus::Instruction::MemoryToString(mem)) => { - js.typescript_required("string"); let len = js.pop(); let ptr = js.pop(); let get = js.cx.expose_get_string_from_wasm(*mem)?; @@ -596,7 +584,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromBool => { - js.typescript_required("boolean"); let val = js.pop(); js.assert_bool(&val); // JS will already coerce booleans into numbers for us @@ -604,20 +591,17 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromStringFirstChar => { - js.typescript_required("string"); let val = js.pop(); js.push(format!("{}.codePointAt(0)", val)); } Instruction::I32FromAnyrefOwned => { - js.typescript_required("any"); js.cx.expose_add_heap_object(); let val = js.pop(); js.push(format!("addHeapObject({})", val)); } Instruction::I32FromAnyrefBorrow => { - js.typescript_required("any"); js.cx.expose_borrowed_objects(); js.cx.expose_global_stack_pointer(); let val = js.pop(); @@ -626,7 +610,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromAnyrefRustOwned { class } => { - js.typescript_required(class); let val = js.pop(); js.assert_class(&val, &class); js.assert_not_moved(&val); @@ -637,7 +620,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromAnyrefRustBorrow { class } => { - js.typescript_required(class); let val = js.pop(); js.assert_class(&val, &class); js.assert_not_moved(&val); @@ -645,7 +627,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromOptionRust { class } => { - js.typescript_optional(class); let val = js.pop(); js.cx.expose_is_like_none(); let i = js.tmp(); @@ -660,7 +641,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32Split64 { signed } => { - js.typescript_required("BigInt"); let val = js.pop(); let f = if *signed { js.cx.expose_int64_cvt_shim() @@ -683,7 +663,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32SplitOption64 { signed } => { - js.typescript_optional("BigInt"); let val = js.pop(); js.cx.expose_is_like_none(); let f = if *signed { @@ -708,7 +687,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromOptionAnyref { table_and_alloc } => { - js.typescript_optional("any"); let val = js.pop(); js.cx.expose_is_like_none(); match table_and_alloc { @@ -724,7 +702,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromOptionU32Sentinel => { - js.typescript_optional("number"); let val = js.pop(); js.cx.expose_is_like_none(); js.assert_optional_number(&val); @@ -732,7 +709,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromOptionBool => { - js.typescript_optional("boolean"); let val = js.pop(); js.cx.expose_is_like_none(); js.assert_optional_bool(&val); @@ -740,7 +716,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromOptionChar => { - js.typescript_optional("string"); let val = js.pop(); js.cx.expose_is_like_none(); js.push(format!( @@ -750,7 +725,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::I32FromOptionEnum { hole } => { - js.typescript_optional("number"); let val = js.pop(); js.cx.expose_is_like_none(); js.assert_optional_number(&val); @@ -758,7 +732,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::FromOptionNative { .. } => { - js.typescript_optional("number"); let val = js.pop(); js.cx.expose_is_like_none(); js.assert_optional_number(&val); @@ -767,7 +740,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::VectorToMemory { kind, malloc, mem } => { - js.typescript_required(kind.js_ty()); let val = js.pop(); let func = js.cx.pass_to_wasm_function(*kind, *mem)?; let malloc = js.cx.export_name_of(*malloc); @@ -789,7 +761,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> malloc, realloc, } => { - js.typescript_optional("string"); let func = js.cx.expose_pass_string_to_wasm(*mem)?; js.cx.expose_is_like_none(); let i = js.tmp(); @@ -813,7 +784,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::OptionVector { kind, mem, malloc } => { - js.typescript_optional(kind.js_ty()); let func = js.cx.pass_to_wasm_function(*kind, *mem)?; js.cx.expose_is_like_none(); let i = js.tmp(); @@ -837,7 +807,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> mem, free, } => { - js.typescript_required(kind.js_ty()); // First up, pass the JS value into wasm, getting out a pointer and // a length. These two pointer/length values get pushed onto the // value stack. @@ -875,26 +844,22 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::BoolFromI32 => { - js.typescript_required("boolean"); let val = js.pop(); js.push(format!("{} !== 0", val)); } Instruction::AnyrefLoadOwned => { - js.typescript_required("any"); js.cx.expose_take_object(); let val = js.pop(); js.push(format!("takeObject({})", val)); } Instruction::StringFromChar => { - js.typescript_required("string"); let val = js.pop(); js.push(format!("String.fromCodePoint({})", val)); } Instruction::I64FromLoHi { signed } => { - js.typescript_required("BigInt"); let f = if *signed { js.cx.expose_int64_cvt_shim() } else { @@ -918,14 +883,12 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::RustFromI32 { class } => { - js.typescript_required(class); js.cx.require_class_wrap(class); let val = js.pop(); js.push(format!("{}.__wrap({})", class, val)); } Instruction::OptionRustFromI32 { class } => { - js.typescript_optional(class); js.cx.require_class_wrap(class); let val = js.pop(); js.push(format!( @@ -936,16 +899,10 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> Instruction::CachedStringLoad { owned, - optional, + optional: _, mem, free, } => { - if *optional { - js.typescript_optional("string"); - } else { - js.typescript_required("string"); - } - let len = js.pop(); let ptr = js.pop(); let tmp = js.tmp(); @@ -968,7 +925,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::TableGet => { - js.typescript_required("any"); let val = js.pop(); js.cx.expose_get_object(); js.push(format!("getObject({})", val)); @@ -979,7 +935,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> nargs, mutable, } => { - js.typescript_optional("any"); let i = js.tmp(); let b = js.pop(); let a = js.pop(); @@ -1025,7 +980,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::VectorLoad { kind, mem, free } => { - js.typescript_required(kind.js_ty()); let len = js.pop(); let ptr = js.pop(); let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; @@ -1043,7 +997,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::OptionVectorLoad { kind, mem, free } => { - js.typescript_optional(kind.js_ty()); let len = js.pop(); let ptr = js.pop(); let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; @@ -1064,7 +1017,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::View { kind, mem } => { - js.typescript_required(kind.js_ty()); let len = js.pop(); let ptr = js.pop(); let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; @@ -1072,7 +1024,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::OptionView { kind, mem } => { - js.typescript_optional(kind.js_ty()); let len = js.pop(); let ptr = js.pop(); let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; @@ -1085,13 +1036,11 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::OptionU32Sentinel => { - js.typescript_optional("number"); let val = js.pop(); js.push(format!("{0} === 0xFFFFFF ? undefined : {0}", val)); } Instruction::ToOptionNative { ty: _, signed } => { - js.typescript_optional("number"); let val = js.pop(); let present = js.pop(); js.push(format!( @@ -1103,13 +1052,11 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::OptionBoolFromI32 => { - js.typescript_optional("boolean"); let val = js.pop(); js.push(format!("{0} === 0xFFFFFF ? undefined : {0} !== 0", val)); } Instruction::OptionCharFromI32 => { - js.typescript_optional("string"); let val = js.pop(); js.push(format!( "{0} === 0xFFFFFF ? undefined : String.fromCodePoint({0})", @@ -1118,13 +1065,11 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) -> } Instruction::OptionEnumFromI32 { hole } => { - js.typescript_optional("number"); let val = js.pop(); js.push(format!("{0} === {1} ? undefined : {0}", val, hole)); } Instruction::Option64FromI32 { signed } => { - js.typescript_optional("BigInt"); let f = if *signed { js.cx.expose_int64_cvt_shim() } else { @@ -1253,3 +1198,28 @@ impl Invocation { } } } + +fn adapter2ts(ty: &AdapterType, dst: &mut String) { + match ty { + AdapterType::I32 + | AdapterType::S8 + | AdapterType::S16 + | AdapterType::S32 + | AdapterType::U8 + | AdapterType::U16 + | AdapterType::U32 + | AdapterType::F32 + | AdapterType::F64 => dst.push_str("number"), + AdapterType::I64 | AdapterType::S64 | AdapterType::U64 => dst.push_str("BigInt"), + AdapterType::String => dst.push_str("string"), + AdapterType::Anyref => dst.push_str("any"), + AdapterType::Bool => dst.push_str("boolean"), + AdapterType::Vector(kind) => dst.push_str(kind.js_ty()), + AdapterType::Option(ty) => { + adapter2ts(ty, dst); + dst.push_str(" | undefined"); + } + AdapterType::Struct(name) => dst.push_str(name), + AdapterType::Function => dst.push_str("any"), + } +} diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 5104233dfea..680f42b22ee 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1950,7 +1950,13 @@ impl<'a> Context<'a> { } // Process the `binding` and generate a bunch of JS/TypeScript/etc. - let js = builder + let binding::JsFunction { + ts_sig, + ts_arg_tys, + ts_ret_ty, + js_doc, + code, + } = builder .process(&adapter, instrs, arg_names) .with_context(|| match kind { Kind::Export(e) => format!("failed to generate bindings for `{}`", e.debug_name), @@ -1963,8 +1969,6 @@ impl<'a> Context<'a> { } Kind::Adapter => format!("failed to generates bindings for adapter"), })?; - let ts = builder.typescript_signature(); - let js_doc = builder.js_doc_comments(); // Once we've got all the JS then put it in the right location depending // on what's being exported. @@ -1973,11 +1977,11 @@ impl<'a> Context<'a> { let docs = format_doc_comments(&export.comments, Some(js_doc)); match &export.kind { AuxExportKind::Function(name) => { - self.export(&name, &format!("function{}", js), Some(docs))?; + self.export(&name, &format!("function{}", code), Some(docs))?; self.globals.push_str("\n"); self.typescript.push_str("export function "); self.typescript.push_str(&name); - self.typescript.push_str(&ts); + self.typescript.push_str(&ts_sig); self.typescript.push_str(";\n"); } AuxExportKind::Constructor(class) => { @@ -1986,36 +1990,36 @@ impl<'a> Context<'a> { bail!("found duplicate constructor for class `{}`", class); } exported.has_constructor = true; - exported.push(&docs, "constructor", "", &js, &ts); + exported.push(&docs, "constructor", "", &code, &ts_sig); } AuxExportKind::Getter { class, field } => { - let ret_ty = builder.ts_ret.as_ref().unwrap().ty.clone(); + let ret_ty = ts_ret_ty.unwrap(); let exported = require_class(&mut self.exported_classes, class); - exported.push_getter(&docs, field, &js, &ret_ty); + exported.push_getter(&docs, field, &code, &ret_ty); } AuxExportKind::Setter { class, field } => { - let arg_ty = builder.ts_args[0].ty.clone(); + let arg_ty = ts_arg_tys[0].clone(); let exported = require_class(&mut self.exported_classes, class); - exported.push_setter(&docs, field, &js, &arg_ty); + exported.push_setter(&docs, field, &code, &arg_ty); } AuxExportKind::StaticFunction { class, name } => { let exported = require_class(&mut self.exported_classes, class); - exported.push(&docs, name, "static ", &js, &ts); + exported.push(&docs, name, "static ", &code, &ts_sig); } AuxExportKind::Method { class, name, .. } => { let exported = require_class(&mut self.exported_classes, class); - exported.push(&docs, name, "", &js, &ts); + exported.push(&docs, name, "", &code, &ts_sig); } } } Kind::Import(core) => { self.wasm_import_definitions - .insert(core, format!("function{}", js)); + .insert(core, format!("function{}", code)); } Kind::Adapter => { self.globals.push_str("function "); self.globals.push_str(&self.adapter_name(id)); - self.globals.push_str(&js); + self.globals.push_str(&code); self.globals.push_str("\n\n"); } } diff --git a/crates/cli-support/src/multivalue.rs b/crates/cli-support/src/multivalue.rs index d2b766336ba..43e5206d379 100644 --- a/crates/cli-support/src/multivalue.rs +++ b/crates/cli-support/src/multivalue.rs @@ -67,7 +67,7 @@ fn extract_xform<'a>( if let Some(Instruction::Retptr) = instructions.first().map(|e| &e.instr) { instructions.remove(0); let mut types = Vec::new(); - instructions.retain(|instruction| match instruction.instr { + instructions.retain(|instruction| match &instruction.instr { Instruction::LoadRetptr { ty, .. } => { types.push(ty.to_wasm().unwrap()); false diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index a93f1ce2001..5638f146006 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -69,7 +69,7 @@ impl InstructionBuilder<'_, '_> { } Descriptor::RustStruct(class) => { self.instruction( - &[AdapterType::Anyref], + &[AdapterType::Struct(class.clone())], Instruction::I32FromAnyrefRustOwned { class: class.clone(), }, @@ -147,7 +147,7 @@ impl InstructionBuilder<'_, '_> { match arg { Descriptor::RustStruct(class) => { self.instruction( - &[AdapterType::Anyref], + &[AdapterType::Struct(class.clone())], Instruction::I32FromAnyrefRustBorrow { class: class.clone(), }, @@ -217,56 +217,56 @@ impl InstructionBuilder<'_, '_> { match arg { Descriptor::Anyref => { self.instruction( - &[AdapterType::Anyref], + &[AdapterType::Anyref.option()], Instruction::I32FromOptionAnyref { table_and_alloc: None, }, &[AdapterType::I32], ); } - Descriptor::I8 => self.in_option_sentinel(), - Descriptor::U8 => self.in_option_sentinel(), - Descriptor::I16 => self.in_option_sentinel(), - Descriptor::U16 => self.in_option_sentinel(), + Descriptor::I8 => self.in_option_sentinel(AdapterType::S8), + Descriptor::U8 => self.in_option_sentinel(AdapterType::U8), + Descriptor::I16 => self.in_option_sentinel(AdapterType::S16), + Descriptor::U16 => self.in_option_sentinel(AdapterType::U16), Descriptor::I32 => self.in_option_native(ValType::I32), Descriptor::U32 => self.in_option_native(ValType::I32), Descriptor::F32 => self.in_option_native(ValType::F32), Descriptor::F64 => self.in_option_native(ValType::F64), Descriptor::I64 | Descriptor::U64 => { - let signed = match arg { - Descriptor::I64 => true, - _ => false, + let (signed, ty) = match arg { + Descriptor::I64 => (true, AdapterType::S64.option()), + _ => (false, AdapterType::U64.option()), }; self.instruction( - &[AdapterType::Anyref], + &[ty], Instruction::I32SplitOption64 { signed }, - &[AdapterType::I32; 3], + &[AdapterType::I32, AdapterType::I32, AdapterType::I32], ); } Descriptor::Boolean => { self.instruction( - &[AdapterType::Anyref], + &[AdapterType::Bool.option()], Instruction::I32FromOptionBool, &[AdapterType::I32], ); } Descriptor::Char => { self.instruction( - &[AdapterType::Anyref], + &[AdapterType::String.option()], Instruction::I32FromOptionChar, &[AdapterType::I32], ); } Descriptor::Enum { hole } => { self.instruction( - &[AdapterType::Anyref], + &[AdapterType::U32.option()], Instruction::I32FromOptionEnum { hole: *hole }, &[AdapterType::I32], ); } Descriptor::RustStruct(name) => { self.instruction( - &[AdapterType::Anyref], + &[AdapterType::Struct(name.clone()).option()], Instruction::I32FromOptionRust { class: name.to_string(), }, @@ -279,13 +279,13 @@ impl InstructionBuilder<'_, '_> { let mem = self.cx.memory()?; let realloc = self.cx.realloc(); self.instruction( - &[AdapterType::Anyref], + &[AdapterType::String.option()], Instruction::OptionString { malloc, mem, realloc, }, - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], ); } @@ -299,9 +299,9 @@ impl InstructionBuilder<'_, '_> { let malloc = self.cx.malloc()?; let mem = self.cx.memory()?; self.instruction( - &[AdapterType::Anyref], + &[AdapterType::Vector(kind).option()], Instruction::OptionVector { kind, malloc, mem }, - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], ); } @@ -343,7 +343,7 @@ impl InstructionBuilder<'_, '_> { // fetch them from the parameters. if !self.return_position { for input in inputs { - self.get(*input); + self.get(input.clone()); } } else { self.input.extend_from_slice(inputs); @@ -385,16 +385,17 @@ impl InstructionBuilder<'_, '_> { } fn in_option_native(&mut self, wasm: ValType) { + let ty = AdapterType::from_wasm(wasm).unwrap(); self.instruction( - &[AdapterType::Anyref], + &[ty.clone().option()], Instruction::FromOptionNative { ty: wasm }, - &[AdapterType::I32, AdapterType::from_wasm(wasm).unwrap()], + &[AdapterType::I32, ty], ); } - fn in_option_sentinel(&mut self) { + fn in_option_sentinel(&mut self, ty: AdapterType) { self.instruction( - &[AdapterType::Anyref], + &[ty.option()], Instruction::I32FromOptionU32Sentinel, &[AdapterType::I32], ); diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 364731937f2..649a6b077b7 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -69,7 +69,7 @@ impl InstructionBuilder<'_, '_> { _ => false, }; self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::I64FromLoHi { signed }, &[if signed { AdapterType::S64 @@ -85,7 +85,7 @@ impl InstructionBuilder<'_, '_> { Instruction::RustFromI32 { class: class.to_string(), }, - &[AdapterType::Anyref], + &[AdapterType::Struct(class.clone())], ); } Descriptor::Ref(d) => self.outgoing_ref(false, d)?, @@ -131,7 +131,7 @@ impl InstructionBuilder<'_, '_> { let mem = self.cx.memory()?; let free = self.cx.free()?; self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::VectorLoad { kind, mem, free }, &[AdapterType::Vector(kind)], ); @@ -167,7 +167,7 @@ impl InstructionBuilder<'_, '_> { Descriptor::String => { let std = wit_walrus::Instruction::MemoryToString(self.cx.memory()?); self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::Standard(std), &[AdapterType::String], ); @@ -181,7 +181,7 @@ impl InstructionBuilder<'_, '_> { })?; let mem = self.cx.memory()?; self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::View { kind, mem }, &[AdapterType::Vector(kind)], ); @@ -198,13 +198,13 @@ impl InstructionBuilder<'_, '_> { .cx .table_element_adapter(descriptor.shim_idx, descriptor)?; self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::StackClosure { adapter, nargs, mutable, }, - &[AdapterType::Anyref], + &[AdapterType::Function], ); } @@ -224,47 +224,47 @@ impl InstructionBuilder<'_, '_> { self.instruction( &[AdapterType::I32], Instruction::AnyrefLoadOwned, - &[AdapterType::Anyref], + &[AdapterType::Anyref.option()], ); } - Descriptor::I8 => self.out_option_sentinel(), - Descriptor::U8 => self.out_option_sentinel(), - Descriptor::I16 => self.out_option_sentinel(), - Descriptor::U16 => self.out_option_sentinel(), + Descriptor::I8 => self.out_option_sentinel(AdapterType::S8), + Descriptor::U8 => self.out_option_sentinel(AdapterType::U8), + Descriptor::I16 => self.out_option_sentinel(AdapterType::S16), + Descriptor::U16 => self.out_option_sentinel(AdapterType::U16), Descriptor::I32 => self.option_native(true, ValType::I32), Descriptor::U32 => self.option_native(false, ValType::I32), Descriptor::F32 => self.option_native(true, ValType::F32), Descriptor::F64 => self.option_native(true, ValType::F64), Descriptor::I64 | Descriptor::U64 => { - let signed = match arg { - Descriptor::I64 => true, - _ => false, + let (signed, ty) = match arg { + Descriptor::I64 => (true, AdapterType::S64.option()), + _ => (false, AdapterType::U64.option()), }; self.instruction( - &[AdapterType::I32; 3], + &[AdapterType::I32, AdapterType::I32, AdapterType::I32], Instruction::Option64FromI32 { signed }, - &[AdapterType::Anyref], + &[ty], ); } Descriptor::Boolean => { self.instruction( &[AdapterType::I32], Instruction::OptionBoolFromI32, - &[AdapterType::Anyref], + &[AdapterType::Bool.option()], ); } Descriptor::Char => { self.instruction( &[AdapterType::I32], Instruction::OptionCharFromI32, - &[AdapterType::Anyref], + &[AdapterType::String.option()], ); } Descriptor::Enum { hole } => { self.instruction( &[AdapterType::I32], Instruction::OptionEnumFromI32 { hole: *hole }, - &[AdapterType::Anyref], + &[AdapterType::U32.option()], ); } Descriptor::RustStruct(name) => { @@ -273,7 +273,7 @@ impl InstructionBuilder<'_, '_> { Instruction::OptionRustFromI32 { class: name.to_string(), }, - &[AdapterType::Anyref], + &[AdapterType::Struct(name.clone()).option()], ); } Descriptor::Ref(d) => self.outgoing_option_ref(false, d)?, @@ -291,9 +291,9 @@ impl InstructionBuilder<'_, '_> { let mem = self.cx.memory()?; let free = self.cx.free()?; self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::OptionVectorLoad { kind, mem, free }, - &[AdapterType::Anyref], + &[AdapterType::Vector(kind).option()], ); } @@ -313,7 +313,7 @@ impl InstructionBuilder<'_, '_> { self.instruction( &[AdapterType::I32], Instruction::TableGet, - &[AdapterType::Anyref], + &[AdapterType::Anyref.option()], ); } Descriptor::CachedString => self.cached_string(true, false)?, @@ -326,9 +326,9 @@ impl InstructionBuilder<'_, '_> { })?; let mem = self.cx.memory()?; self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::OptionView { kind, mem }, - &[AdapterType::Anyref], + &[AdapterType::Vector(kind).option()], ); } _ => bail!( @@ -352,7 +352,7 @@ impl InstructionBuilder<'_, '_> { let mem = self.cx.memory()?; let free = self.cx.free()?; self.instruction( - &[AdapterType::I32; 2], + &[AdapterType::I32, AdapterType::I32], Instruction::CachedStringLoad { owned, optional, @@ -365,18 +365,19 @@ impl InstructionBuilder<'_, '_> { } fn option_native(&mut self, signed: bool, ty: ValType) { + let adapter_ty = AdapterType::from_wasm(ty).unwrap(); self.instruction( - &[AdapterType::I32, AdapterType::from_wasm(ty).unwrap()], + &[AdapterType::I32, adapter_ty.clone()], Instruction::ToOptionNative { signed, ty }, - &[AdapterType::Anyref], + &[adapter_ty.option()], ); } - fn out_option_sentinel(&mut self) { + fn out_option_sentinel(&mut self, ty: AdapterType) { self.instruction( &[AdapterType::I32], Instruction::OptionU32Sentinel, - &[AdapterType::Anyref], + &[ty.option()], ); } } diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index ed4972552b9..fdbfa55de33 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -64,7 +64,7 @@ pub enum AdapterJsImportKind { Normal, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum AdapterType { S8, S16, @@ -82,6 +82,9 @@ pub enum AdapterType { I32, I64, Vector(VectorKind), + Option(Box), + Struct(String), + Function, } #[derive(Debug, Clone)] @@ -338,11 +341,20 @@ impl AdapterType { AdapterType::F64 => wit_walrus::ValType::F64, AdapterType::String => wit_walrus::ValType::String, AdapterType::Anyref => wit_walrus::ValType::Anyref, + AdapterType::I32 => wit_walrus::ValType::I32, AdapterType::I64 => wit_walrus::ValType::I64, - AdapterType::Bool | AdapterType::Vector(_) => return None, + AdapterType::Option(_) + | AdapterType::Function + | AdapterType::Struct(_) + | AdapterType::Bool + | AdapterType::Vector(_) => return None, }) } + + pub fn option(self) -> AdapterType { + AdapterType::Option(Box::new(self)) + } } impl NonstandardWitSection { diff --git a/crates/typescript-tests/src/simple_struct.rs b/crates/typescript-tests/src/simple_struct.rs index 7474719a322..a1a44d2c11d 100644 --- a/crates/typescript-tests/src/simple_struct.rs +++ b/crates/typescript-tests/src/simple_struct.rs @@ -13,4 +13,8 @@ impl A { pub fn other() {} pub fn foo(&self) {} + + pub fn ret_bool(&self) -> bool { true } + pub fn take_bool(&self, _: bool) {} + pub fn take_many(&self, _: bool, _: f64, _: u32) {} } diff --git a/crates/typescript-tests/src/simple_struct.ts b/crates/typescript-tests/src/simple_struct.ts index 9583039950f..4927cd1a4a4 100644 --- a/crates/typescript-tests/src/simple_struct.ts +++ b/crates/typescript-tests/src/simple_struct.ts @@ -4,3 +4,6 @@ const a = new wbg.A(); wbg.A.other(); a.foo(); a.free(); +const b: boolean = a.ret_bool() +a.take_bool(b); +a.take_many(b, 1, 2);