Store richer adapter types, don't use instructions for TypeScript (#1945)

This commit updates how TypeScript signature are generated from adapters
in wasm-bindgen. A richer set of `AdapterType` types are now stored
which record information about optional types and such. These direct
`AdapterType` values are then used to calculate the TypeScript
signature, rather than following the instructions in an adapter function
(which only works anyway for wasm-bindgen generated adapters).

This should be more robust since it reads the actual true signature of
the adapter to generate the TypeScript signature, rather than attempting
to ad-hoc-ly infer it from the various instructions, which was already
broken.

A number of refactorings were involved here, but the main pieces are:

* The `AdapterType` type is a bit more rich now to describe more
  Rust-like types.
* The `TypescriptArg` structure is now gone and instead return values
  are directly inferred from type signatures of adapters.
* The `typescript_{required,optional}` methods are no longer needed.
* The return of `JsBuilder::process` was enhanced to return more values,
  rather than storing some return values on the structure itself.

Closes #1926
This commit is contained in:
Alex Crichton
2020-01-07 11:34:02 -06:00
committed by GitHub
parent 6c27376ac2
commit f66d83ff70
8 changed files with 206 additions and 209 deletions

View File

@ -15,10 +15,6 @@ use walrus::Module;
pub struct Builder<'a, 'b> { pub struct Builder<'a, 'b> {
/// Parent context used to expose helper functions and such. /// Parent context used to expose helper functions and such.
pub cx: &'a mut Context<'b>, pub cx: &'a mut Context<'b>,
/// The TypeScript definition for each argument to this function.
pub ts_args: Vec<TypescriptArg>,
/// The TypeScript return value for this function.
pub ts_ret: Option<TypescriptArg>,
/// Whether or not this is building a constructor for a Rust class, and if /// Whether or not this is building a constructor for a Rust class, and if
/// so what class it's constructing. /// so what class it's constructing.
constructor: Option<String>, constructor: Option<String>,
@ -39,10 +35,6 @@ pub struct JsBuilder<'a, 'b> {
/// JS functions, etc. /// JS functions, etc.
cx: &'a mut Context<'b>, cx: &'a mut Context<'b>,
/// The list of typescript arguments that we're going to have to this
/// function.
typescript: Vec<TypescriptArg>,
/// The "prelude" of the function, or largely just the JS function we've /// The "prelude" of the function, or largely just the JS function we've
/// built so far. /// built so far.
prelude: String, prelude: String,
@ -66,10 +58,12 @@ pub struct JsBuilder<'a, 'b> {
stack: Vec<String>, stack: Vec<String>,
} }
pub struct TypescriptArg { pub struct JsFunction {
pub ty: String, pub code: String,
pub name: String, pub ts_sig: String,
pub optional: bool, pub js_doc: String,
pub ts_arg_tys: Vec<String>,
pub ts_ret_ty: Option<String>,
} }
impl<'a, 'b> Builder<'a, 'b> { impl<'a, 'b> Builder<'a, 'b> {
@ -77,8 +71,6 @@ impl<'a, 'b> Builder<'a, 'b> {
Builder { Builder {
log_error: false, log_error: false,
cx, cx,
ts_args: Vec::new(),
ts_ret: None,
constructor: None, constructor: None,
method: None, method: None,
catch: false, catch: false,
@ -106,7 +98,7 @@ impl<'a, 'b> Builder<'a, 'b> {
adapter: &Adapter, adapter: &Adapter,
instructions: &[InstructionData], instructions: &[InstructionData],
explicit_arg_names: &Option<Vec<String>>, explicit_arg_names: &Option<Vec<String>>,
) -> Result<String, Error> { ) -> Result<JsFunction, Error> {
if self if self
.cx .cx
.aux .aux
@ -118,6 +110,7 @@ impl<'a, 'b> Builder<'a, 'b> {
let mut params = adapter.params.iter(); let mut params = adapter.params.iter();
let mut function_args = Vec::new(); 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 // 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 // method, so the leading parameter is the this pointer stored on
@ -141,13 +134,14 @@ impl<'a, 'b> Builder<'a, 'b> {
} }
None => {} None => {}
} }
for (i, _param) in params.enumerate() { for (i, param) in params.enumerate() {
let arg = match explicit_arg_names { let arg = match explicit_arg_names {
Some(list) => list[i].clone(), Some(list) => list[i].clone(),
None => format!("arg{}", i), None => format!("arg{}", i),
}; };
js.args.push(arg.clone()); js.args.push(arg.clone());
function_args.push(arg); function_args.push(arg);
arg_tys.push(param);
} }
// Translate all instructions, the fun loop! // Translate all instructions, the fun loop!
@ -170,7 +164,6 @@ impl<'a, 'b> Builder<'a, 'b> {
match js.stack.len() { match js.stack.len() {
0 => {} 0 => {}
1 => { 1 => {
self.ts_ret = js.typescript.pop();
let val = js.pop(); let val = js.pop();
js.prelude(&format!("return {};", val)); js.prelude(&format!("return {};", val));
} }
@ -188,18 +181,17 @@ impl<'a, 'b> Builder<'a, 'b> {
// } // }
} }
assert!(js.stack.is_empty()); assert!(js.stack.is_empty());
self.ts_args = js.typescript;
// Remove extraneous typescript args which were synthesized and aren't // // Remove extraneous typescript args which were synthesized and aren't
// part of our function shim. // // part of our function shim.
while self.ts_args.len() > function_args.len() { // while self.ts_args.len() > function_args.len() {
self.ts_args.remove(0); // self.ts_args.remove(0);
} // }
let mut ret = String::new(); let mut code = String::new();
ret.push_str("("); code.push_str("(");
ret.push_str(&function_args.join(", ")); code.push_str(&function_args.join(", "));
ret.push_str(") {\n"); code.push_str(") {\n");
let mut call = js.prelude; let mut call = js.prelude;
if js.finally.len() != 0 { 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); call = format!("try {{\n{}}} catch (e) {{\n logError(e)\n}}\n", call);
} }
ret.push_str(&call); code.push_str(&call);
ret.push_str("}"); 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. /// Returns the typescript signature of the binding that this has described.
@ -231,61 +233,79 @@ impl<'a, 'b> Builder<'a, 'b> {
/// ///
/// Note that the TypeScript returned here is just the argument list and the /// 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. /// 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<String>, Option<String>) {
// Build up the typescript signature as well // Build up the typescript signature as well
let mut omittable = true; let mut omittable = true;
let mut ts_args = Vec::new(); 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 // In TypeScript, we can mark optional parameters as omittable
// using the `?` suffix, but only if they're not followed by // using the `?` suffix, but only if they're not followed by
// non-omittable parameters. Therefore iterate the parameter list // non-omittable parameters. Therefore iterate the parameter list
// in reverse and stop using the `?` suffix for optional params as // in reverse and stop using the `?` suffix for optional params as
// soon as a non-optional parameter is encountered. // soon as a non-optional parameter is encountered.
if arg.optional { let mut arg = name.to_string();
if omittable { let mut ts = String::new();
ts_args.push(format!("{}?: {}", arg.name, arg.ty)); match ty {
} else { AdapterType::Option(ty) if omittable => {
ts_args.push(format!("{}: {} | undefined", arg.name, arg.ty)); arg.push_str("?: ");
adapter2ts(ty, &mut ts);
} }
} else { ty => {
omittable = false; omittable = false;
ts_args.push(format!("{}: {}", arg.name, arg.ty)); arg.push_str(": ");
adapter2ts(ty, &mut ts);
} }
} }
arg.push_str(&ts);
ts_arg_tys.push(ts);
ts_args.push(arg);
}
ts_args.reverse(); ts_args.reverse();
ts_arg_tys.reverse();
let mut ts = format!("({})", ts_args.join(", ")); let mut ts = format!("({})", ts_args.join(", "));
// Constructors have no listed return type in typescript // Constructors have no listed return type in typescript
let mut ts_ret = None;
if self.constructor.is_none() { if self.constructor.is_none() {
ts.push_str(": "); ts.push_str(": ");
if let Some(ty) = &self.ts_ret { let mut ret = String::new();
ts.push_str(&ty.ty); match result_tys.len() {
if ty.optional { 0 => ret.push_str("void"),
ts.push_str(" | undefined"); 1 => adapter2ts(&result_tys[0], &mut ret),
_ => ret.push_str("[any]"),
} }
} else { ts.push_str(&ret);
ts.push_str("void"); ts_ret = Some(ret);
} }
} return (ts, ts_arg_tys, ts_ret);
return ts;
} }
/// Returns a helpful JS doc comment which lists types for all parameters /// Returns a helpful JS doc comment which lists types for all parameters
/// and the return value. /// and the return value.
pub fn js_doc_comments(&self) -> String { fn js_doc_comments(
let mut ret: String = self &self,
.ts_args arg_names: &[String],
.iter() arg_tys: &[&AdapterType],
.map(|a| { ts_ret: &Option<String>,
if a.optional { ) -> String {
format!("@param {{{} | undefined}} {}\n", a.ty, a.name) let mut ret = String::new();
} else { for (name, ty) in arg_names.iter().zip(arg_tys) {
format!("@param {{{}}} {}\n", a.ty, a.name) 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 {
if ts != "void" {
ret.push_str(&format!("@returns {{{}}}", ts));
} }
})
.collect();
if let Some(ts) = &self.ts_ret {
ret.push_str(&format!("@returns {{{}}}", ts.ty));
} }
ret ret
} }
@ -299,7 +319,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
tmp: 0, tmp: 0,
finally: String::new(), finally: String::new(),
prelude: String::new(), prelude: String::new(),
typescript: Vec::new(),
stack: Vec::new(), stack: Vec::new(),
} }
} }
@ -308,31 +327,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
&self.args[idx as usize] &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) { pub fn prelude(&mut self, prelude: &str) {
for line in prelude.trim().lines().map(|l| l.trim()) { for line in prelude.trim().lines().map(|l| l.trim()) {
if !line.is_empty() { if !line.is_empty() {
@ -426,7 +420,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
malloc: walrus::FunctionId, malloc: walrus::FunctionId,
realloc: Option<walrus::FunctionId>, realloc: Option<walrus::FunctionId>,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.typescript_required("string");
let pass = self.cx.expose_pass_string_to_wasm(mem)?; let pass = self.cx.expose_pass_string_to_wasm(mem)?;
let val = self.pop(); let val = self.pop();
let malloc = self.cx.export_name_of(malloc); let malloc = self.cx.export_name_of(malloc);
@ -510,7 +503,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::Standard(wit_walrus::Instruction::IntToWasm { trap: false, .. }) => { Instruction::Standard(wit_walrus::Instruction::IntToWasm { trap: false, .. }) => {
js.typescript_required("number");
let val = js.pop(); let val = js.pop();
js.assert_number(&val); js.assert_number(&val);
js.push(val); js.push(val);
@ -524,7 +516,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
output, output,
.. ..
}) => { }) => {
js.typescript_required("number");
let val = js.pop(); let val = js.pop();
match output { match output {
wit_walrus::ValType::U32 => js.push(format!("{} >>> 0", val)), wit_walrus::ValType::U32 => js.push(format!("{} >>> 0", val)),
@ -538,7 +529,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::Standard(wit_walrus::Instruction::MemoryToString(mem)) => { Instruction::Standard(wit_walrus::Instruction::MemoryToString(mem)) => {
js.typescript_required("string");
let len = js.pop(); let len = js.pop();
let ptr = js.pop(); let ptr = js.pop();
let get = js.cx.expose_get_string_from_wasm(*mem)?; let get = js.cx.expose_get_string_from_wasm(*mem)?;
@ -596,7 +586,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromBool => { Instruction::I32FromBool => {
js.typescript_required("boolean");
let val = js.pop(); let val = js.pop();
js.assert_bool(&val); js.assert_bool(&val);
// JS will already coerce booleans into numbers for us // JS will already coerce booleans into numbers for us
@ -604,20 +593,17 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromStringFirstChar => { Instruction::I32FromStringFirstChar => {
js.typescript_required("string");
let val = js.pop(); let val = js.pop();
js.push(format!("{}.codePointAt(0)", val)); js.push(format!("{}.codePointAt(0)", val));
} }
Instruction::I32FromAnyrefOwned => { Instruction::I32FromAnyrefOwned => {
js.typescript_required("any");
js.cx.expose_add_heap_object(); js.cx.expose_add_heap_object();
let val = js.pop(); let val = js.pop();
js.push(format!("addHeapObject({})", val)); js.push(format!("addHeapObject({})", val));
} }
Instruction::I32FromAnyrefBorrow => { Instruction::I32FromAnyrefBorrow => {
js.typescript_required("any");
js.cx.expose_borrowed_objects(); js.cx.expose_borrowed_objects();
js.cx.expose_global_stack_pointer(); js.cx.expose_global_stack_pointer();
let val = js.pop(); let val = js.pop();
@ -626,7 +612,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromAnyrefRustOwned { class } => { Instruction::I32FromAnyrefRustOwned { class } => {
js.typescript_required(class);
let val = js.pop(); let val = js.pop();
js.assert_class(&val, &class); js.assert_class(&val, &class);
js.assert_not_moved(&val); js.assert_not_moved(&val);
@ -637,7 +622,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromAnyrefRustBorrow { class } => { Instruction::I32FromAnyrefRustBorrow { class } => {
js.typescript_required(class);
let val = js.pop(); let val = js.pop();
js.assert_class(&val, &class); js.assert_class(&val, &class);
js.assert_not_moved(&val); js.assert_not_moved(&val);
@ -645,7 +629,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromOptionRust { class } => { Instruction::I32FromOptionRust { class } => {
js.typescript_optional(class);
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
let i = js.tmp(); let i = js.tmp();
@ -660,7 +643,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32Split64 { signed } => { Instruction::I32Split64 { signed } => {
js.typescript_required("BigInt");
let val = js.pop(); let val = js.pop();
let f = if *signed { let f = if *signed {
js.cx.expose_int64_cvt_shim() js.cx.expose_int64_cvt_shim()
@ -683,7 +665,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32SplitOption64 { signed } => { Instruction::I32SplitOption64 { signed } => {
js.typescript_optional("BigInt");
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
let f = if *signed { let f = if *signed {
@ -708,7 +689,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromOptionAnyref { table_and_alloc } => { Instruction::I32FromOptionAnyref { table_and_alloc } => {
js.typescript_optional("any");
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
match table_and_alloc { match table_and_alloc {
@ -724,7 +704,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromOptionU32Sentinel => { Instruction::I32FromOptionU32Sentinel => {
js.typescript_optional("number");
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
js.assert_optional_number(&val); js.assert_optional_number(&val);
@ -732,7 +711,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromOptionBool => { Instruction::I32FromOptionBool => {
js.typescript_optional("boolean");
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
js.assert_optional_bool(&val); js.assert_optional_bool(&val);
@ -740,7 +718,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromOptionChar => { Instruction::I32FromOptionChar => {
js.typescript_optional("string");
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
js.push(format!( js.push(format!(
@ -750,7 +727,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::I32FromOptionEnum { hole } => { Instruction::I32FromOptionEnum { hole } => {
js.typescript_optional("number");
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
js.assert_optional_number(&val); js.assert_optional_number(&val);
@ -758,7 +734,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::FromOptionNative { .. } => { Instruction::FromOptionNative { .. } => {
js.typescript_optional("number");
let val = js.pop(); let val = js.pop();
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
js.assert_optional_number(&val); js.assert_optional_number(&val);
@ -767,7 +742,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::VectorToMemory { kind, malloc, mem } => { Instruction::VectorToMemory { kind, malloc, mem } => {
js.typescript_required(kind.js_ty());
let val = js.pop(); let val = js.pop();
let func = js.cx.pass_to_wasm_function(*kind, *mem)?; let func = js.cx.pass_to_wasm_function(*kind, *mem)?;
let malloc = js.cx.export_name_of(*malloc); let malloc = js.cx.export_name_of(*malloc);
@ -789,7 +763,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
malloc, malloc,
realloc, realloc,
} => { } => {
js.typescript_optional("string");
let func = js.cx.expose_pass_string_to_wasm(*mem)?; let func = js.cx.expose_pass_string_to_wasm(*mem)?;
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
let i = js.tmp(); let i = js.tmp();
@ -813,7 +786,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::OptionVector { kind, mem, malloc } => { Instruction::OptionVector { kind, mem, malloc } => {
js.typescript_optional(kind.js_ty());
let func = js.cx.pass_to_wasm_function(*kind, *mem)?; let func = js.cx.pass_to_wasm_function(*kind, *mem)?;
js.cx.expose_is_like_none(); js.cx.expose_is_like_none();
let i = js.tmp(); let i = js.tmp();
@ -837,7 +809,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
mem, mem,
free, free,
} => { } => {
js.typescript_required(kind.js_ty());
// First up, pass the JS value into wasm, getting out a pointer and // First up, pass the JS value into wasm, getting out a pointer and
// a length. These two pointer/length values get pushed onto the // a length. These two pointer/length values get pushed onto the
// value stack. // value stack.
@ -875,26 +846,22 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::BoolFromI32 => { Instruction::BoolFromI32 => {
js.typescript_required("boolean");
let val = js.pop(); let val = js.pop();
js.push(format!("{} !== 0", val)); js.push(format!("{} !== 0", val));
} }
Instruction::AnyrefLoadOwned => { Instruction::AnyrefLoadOwned => {
js.typescript_required("any");
js.cx.expose_take_object(); js.cx.expose_take_object();
let val = js.pop(); let val = js.pop();
js.push(format!("takeObject({})", val)); js.push(format!("takeObject({})", val));
} }
Instruction::StringFromChar => { Instruction::StringFromChar => {
js.typescript_required("string");
let val = js.pop(); let val = js.pop();
js.push(format!("String.fromCodePoint({})", val)); js.push(format!("String.fromCodePoint({})", val));
} }
Instruction::I64FromLoHi { signed } => { Instruction::I64FromLoHi { signed } => {
js.typescript_required("BigInt");
let f = if *signed { let f = if *signed {
js.cx.expose_int64_cvt_shim() js.cx.expose_int64_cvt_shim()
} else { } else {
@ -918,14 +885,12 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::RustFromI32 { class } => { Instruction::RustFromI32 { class } => {
js.typescript_required(class);
js.cx.require_class_wrap(class); js.cx.require_class_wrap(class);
let val = js.pop(); let val = js.pop();
js.push(format!("{}.__wrap({})", class, val)); js.push(format!("{}.__wrap({})", class, val));
} }
Instruction::OptionRustFromI32 { class } => { Instruction::OptionRustFromI32 { class } => {
js.typescript_optional(class);
js.cx.require_class_wrap(class); js.cx.require_class_wrap(class);
let val = js.pop(); let val = js.pop();
js.push(format!( js.push(format!(
@ -936,16 +901,10 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
Instruction::CachedStringLoad { Instruction::CachedStringLoad {
owned, owned,
optional, optional: _,
mem, mem,
free, free,
} => { } => {
if *optional {
js.typescript_optional("string");
} else {
js.typescript_required("string");
}
let len = js.pop(); let len = js.pop();
let ptr = js.pop(); let ptr = js.pop();
let tmp = js.tmp(); let tmp = js.tmp();
@ -968,7 +927,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::TableGet => { Instruction::TableGet => {
js.typescript_required("any");
let val = js.pop(); let val = js.pop();
js.cx.expose_get_object(); js.cx.expose_get_object();
js.push(format!("getObject({})", val)); js.push(format!("getObject({})", val));
@ -979,7 +937,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
nargs, nargs,
mutable, mutable,
} => { } => {
js.typescript_optional("any");
let i = js.tmp(); let i = js.tmp();
let b = js.pop(); let b = js.pop();
let a = js.pop(); let a = js.pop();
@ -1025,7 +982,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::VectorLoad { kind, mem, free } => { Instruction::VectorLoad { kind, mem, free } => {
js.typescript_required(kind.js_ty());
let len = js.pop(); let len = js.pop();
let ptr = js.pop(); let ptr = js.pop();
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
@ -1043,7 +999,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::OptionVectorLoad { kind, mem, free } => { Instruction::OptionVectorLoad { kind, mem, free } => {
js.typescript_optional(kind.js_ty());
let len = js.pop(); let len = js.pop();
let ptr = js.pop(); let ptr = js.pop();
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
@ -1064,7 +1019,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::View { kind, mem } => { Instruction::View { kind, mem } => {
js.typescript_required(kind.js_ty());
let len = js.pop(); let len = js.pop();
let ptr = js.pop(); let ptr = js.pop();
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
@ -1072,7 +1026,6 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::OptionView { kind, mem } => { Instruction::OptionView { kind, mem } => {
js.typescript_optional(kind.js_ty());
let len = js.pop(); let len = js.pop();
let ptr = js.pop(); let ptr = js.pop();
let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?; let f = js.cx.expose_get_vector_from_wasm(*kind, *mem)?;
@ -1085,13 +1038,11 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::OptionU32Sentinel => { Instruction::OptionU32Sentinel => {
js.typescript_optional("number");
let val = js.pop(); let val = js.pop();
js.push(format!("{0} === 0xFFFFFF ? undefined : {0}", val)); js.push(format!("{0} === 0xFFFFFF ? undefined : {0}", val));
} }
Instruction::ToOptionNative { ty: _, signed } => { Instruction::ToOptionNative { ty: _, signed } => {
js.typescript_optional("number");
let val = js.pop(); let val = js.pop();
let present = js.pop(); let present = js.pop();
js.push(format!( js.push(format!(
@ -1103,13 +1054,11 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::OptionBoolFromI32 => { Instruction::OptionBoolFromI32 => {
js.typescript_optional("boolean");
let val = js.pop(); let val = js.pop();
js.push(format!("{0} === 0xFFFFFF ? undefined : {0} !== 0", val)); js.push(format!("{0} === 0xFFFFFF ? undefined : {0} !== 0", val));
} }
Instruction::OptionCharFromI32 => { Instruction::OptionCharFromI32 => {
js.typescript_optional("string");
let val = js.pop(); let val = js.pop();
js.push(format!( js.push(format!(
"{0} === 0xFFFFFF ? undefined : String.fromCodePoint({0})", "{0} === 0xFFFFFF ? undefined : String.fromCodePoint({0})",
@ -1118,13 +1067,11 @@ fn instruction(js: &mut JsBuilder, instr: &Instruction, log_error: &mut bool) ->
} }
Instruction::OptionEnumFromI32 { hole } => { Instruction::OptionEnumFromI32 { hole } => {
js.typescript_optional("number");
let val = js.pop(); let val = js.pop();
js.push(format!("{0} === {1} ? undefined : {0}", val, hole)); js.push(format!("{0} === {1} ? undefined : {0}", val, hole));
} }
Instruction::Option64FromI32 { signed } => { Instruction::Option64FromI32 { signed } => {
js.typescript_optional("BigInt");
let f = if *signed { let f = if *signed {
js.cx.expose_int64_cvt_shim() js.cx.expose_int64_cvt_shim()
} else { } else {
@ -1253,3 +1200,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"),
}
}

View File

@ -1950,7 +1950,13 @@ impl<'a> Context<'a> {
} }
// Process the `binding` and generate a bunch of JS/TypeScript/etc. // 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) .process(&adapter, instrs, arg_names)
.with_context(|| match kind { .with_context(|| match kind {
Kind::Export(e) => format!("failed to generate bindings for `{}`", e.debug_name), 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"), 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 // Once we've got all the JS then put it in the right location depending
// on what's being exported. // on what's being exported.
@ -1973,11 +1977,11 @@ impl<'a> Context<'a> {
let docs = format_doc_comments(&export.comments, Some(js_doc)); let docs = format_doc_comments(&export.comments, Some(js_doc));
match &export.kind { match &export.kind {
AuxExportKind::Function(name) => { 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.globals.push_str("\n");
self.typescript.push_str("export function "); self.typescript.push_str("export function ");
self.typescript.push_str(&name); self.typescript.push_str(&name);
self.typescript.push_str(&ts); self.typescript.push_str(&ts_sig);
self.typescript.push_str(";\n"); self.typescript.push_str(";\n");
} }
AuxExportKind::Constructor(class) => { AuxExportKind::Constructor(class) => {
@ -1986,36 +1990,36 @@ impl<'a> Context<'a> {
bail!("found duplicate constructor for class `{}`", class); bail!("found duplicate constructor for class `{}`", class);
} }
exported.has_constructor = true; exported.has_constructor = true;
exported.push(&docs, "constructor", "", &js, &ts); exported.push(&docs, "constructor", "", &code, &ts_sig);
} }
AuxExportKind::Getter { class, field } => { 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); 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 } => { 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); 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 } => { AuxExportKind::StaticFunction { class, name } => {
let exported = require_class(&mut self.exported_classes, class); 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, .. } => { AuxExportKind::Method { class, name, .. } => {
let exported = require_class(&mut self.exported_classes, class); 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) => { Kind::Import(core) => {
self.wasm_import_definitions self.wasm_import_definitions
.insert(core, format!("function{}", js)); .insert(core, format!("function{}", code));
} }
Kind::Adapter => { Kind::Adapter => {
self.globals.push_str("function "); self.globals.push_str("function ");
self.globals.push_str(&self.adapter_name(id)); self.globals.push_str(&self.adapter_name(id));
self.globals.push_str(&js); self.globals.push_str(&code);
self.globals.push_str("\n\n"); self.globals.push_str("\n\n");
} }
} }

View File

@ -67,7 +67,7 @@ fn extract_xform<'a>(
if let Some(Instruction::Retptr) = instructions.first().map(|e| &e.instr) { if let Some(Instruction::Retptr) = instructions.first().map(|e| &e.instr) {
instructions.remove(0); instructions.remove(0);
let mut types = Vec::new(); let mut types = Vec::new();
instructions.retain(|instruction| match instruction.instr { instructions.retain(|instruction| match &instruction.instr {
Instruction::LoadRetptr { ty, .. } => { Instruction::LoadRetptr { ty, .. } => {
types.push(ty.to_wasm().unwrap()); types.push(ty.to_wasm().unwrap());
false false

View File

@ -69,7 +69,7 @@ impl InstructionBuilder<'_, '_> {
} }
Descriptor::RustStruct(class) => { Descriptor::RustStruct(class) => {
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::Struct(class.clone())],
Instruction::I32FromAnyrefRustOwned { Instruction::I32FromAnyrefRustOwned {
class: class.clone(), class: class.clone(),
}, },
@ -147,7 +147,7 @@ impl InstructionBuilder<'_, '_> {
match arg { match arg {
Descriptor::RustStruct(class) => { Descriptor::RustStruct(class) => {
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::Struct(class.clone())],
Instruction::I32FromAnyrefRustBorrow { Instruction::I32FromAnyrefRustBorrow {
class: class.clone(), class: class.clone(),
}, },
@ -217,56 +217,56 @@ impl InstructionBuilder<'_, '_> {
match arg { match arg {
Descriptor::Anyref => { Descriptor::Anyref => {
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::Anyref.option()],
Instruction::I32FromOptionAnyref { Instruction::I32FromOptionAnyref {
table_and_alloc: None, table_and_alloc: None,
}, },
&[AdapterType::I32], &[AdapterType::I32],
); );
} }
Descriptor::I8 => self.in_option_sentinel(), Descriptor::I8 => self.in_option_sentinel(AdapterType::S8),
Descriptor::U8 => self.in_option_sentinel(), Descriptor::U8 => self.in_option_sentinel(AdapterType::U8),
Descriptor::I16 => self.in_option_sentinel(), Descriptor::I16 => self.in_option_sentinel(AdapterType::S16),
Descriptor::U16 => self.in_option_sentinel(), Descriptor::U16 => self.in_option_sentinel(AdapterType::U16),
Descriptor::I32 => self.in_option_native(ValType::I32), Descriptor::I32 => self.in_option_native(ValType::I32),
Descriptor::U32 => self.in_option_native(ValType::I32), Descriptor::U32 => self.in_option_native(ValType::I32),
Descriptor::F32 => self.in_option_native(ValType::F32), Descriptor::F32 => self.in_option_native(ValType::F32),
Descriptor::F64 => self.in_option_native(ValType::F64), Descriptor::F64 => self.in_option_native(ValType::F64),
Descriptor::I64 | Descriptor::U64 => { Descriptor::I64 | Descriptor::U64 => {
let signed = match arg { let (signed, ty) = match arg {
Descriptor::I64 => true, Descriptor::I64 => (true, AdapterType::S64.option()),
_ => false, _ => (false, AdapterType::U64.option()),
}; };
self.instruction( self.instruction(
&[AdapterType::Anyref], &[ty],
Instruction::I32SplitOption64 { signed }, Instruction::I32SplitOption64 { signed },
&[AdapterType::I32; 3], &[AdapterType::I32, AdapterType::I32, AdapterType::I32],
); );
} }
Descriptor::Boolean => { Descriptor::Boolean => {
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::Bool.option()],
Instruction::I32FromOptionBool, Instruction::I32FromOptionBool,
&[AdapterType::I32], &[AdapterType::I32],
); );
} }
Descriptor::Char => { Descriptor::Char => {
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::String.option()],
Instruction::I32FromOptionChar, Instruction::I32FromOptionChar,
&[AdapterType::I32], &[AdapterType::I32],
); );
} }
Descriptor::Enum { hole } => { Descriptor::Enum { hole } => {
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::U32.option()],
Instruction::I32FromOptionEnum { hole: *hole }, Instruction::I32FromOptionEnum { hole: *hole },
&[AdapterType::I32], &[AdapterType::I32],
); );
} }
Descriptor::RustStruct(name) => { Descriptor::RustStruct(name) => {
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::Struct(name.clone()).option()],
Instruction::I32FromOptionRust { Instruction::I32FromOptionRust {
class: name.to_string(), class: name.to_string(),
}, },
@ -279,13 +279,13 @@ impl InstructionBuilder<'_, '_> {
let mem = self.cx.memory()?; let mem = self.cx.memory()?;
let realloc = self.cx.realloc(); let realloc = self.cx.realloc();
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::String.option()],
Instruction::OptionString { Instruction::OptionString {
malloc, malloc,
mem, mem,
realloc, realloc,
}, },
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
); );
} }
@ -299,9 +299,9 @@ impl InstructionBuilder<'_, '_> {
let malloc = self.cx.malloc()?; let malloc = self.cx.malloc()?;
let mem = self.cx.memory()?; let mem = self.cx.memory()?;
self.instruction( self.instruction(
&[AdapterType::Anyref], &[AdapterType::Vector(kind).option()],
Instruction::OptionVector { kind, malloc, mem }, Instruction::OptionVector { kind, malloc, mem },
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
); );
} }
@ -343,7 +343,7 @@ impl InstructionBuilder<'_, '_> {
// fetch them from the parameters. // fetch them from the parameters.
if !self.return_position { if !self.return_position {
for input in inputs { for input in inputs {
self.get(*input); self.get(input.clone());
} }
} else { } else {
self.input.extend_from_slice(inputs); self.input.extend_from_slice(inputs);
@ -385,16 +385,17 @@ impl InstructionBuilder<'_, '_> {
} }
fn in_option_native(&mut self, wasm: ValType) { fn in_option_native(&mut self, wasm: ValType) {
let ty = AdapterType::from_wasm(wasm).unwrap();
self.instruction( self.instruction(
&[AdapterType::Anyref], &[ty.clone().option()],
Instruction::FromOptionNative { ty: wasm }, 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( self.instruction(
&[AdapterType::Anyref], &[ty.option()],
Instruction::I32FromOptionU32Sentinel, Instruction::I32FromOptionU32Sentinel,
&[AdapterType::I32], &[AdapterType::I32],
); );

View File

@ -69,7 +69,7 @@ impl InstructionBuilder<'_, '_> {
_ => false, _ => false,
}; };
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::I64FromLoHi { signed }, Instruction::I64FromLoHi { signed },
&[if signed { &[if signed {
AdapterType::S64 AdapterType::S64
@ -85,7 +85,7 @@ impl InstructionBuilder<'_, '_> {
Instruction::RustFromI32 { Instruction::RustFromI32 {
class: class.to_string(), class: class.to_string(),
}, },
&[AdapterType::Anyref], &[AdapterType::Struct(class.clone())],
); );
} }
Descriptor::Ref(d) => self.outgoing_ref(false, d)?, Descriptor::Ref(d) => self.outgoing_ref(false, d)?,
@ -131,7 +131,7 @@ impl InstructionBuilder<'_, '_> {
let mem = self.cx.memory()?; let mem = self.cx.memory()?;
let free = self.cx.free()?; let free = self.cx.free()?;
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::VectorLoad { kind, mem, free }, Instruction::VectorLoad { kind, mem, free },
&[AdapterType::Vector(kind)], &[AdapterType::Vector(kind)],
); );
@ -167,7 +167,7 @@ impl InstructionBuilder<'_, '_> {
Descriptor::String => { Descriptor::String => {
let std = wit_walrus::Instruction::MemoryToString(self.cx.memory()?); let std = wit_walrus::Instruction::MemoryToString(self.cx.memory()?);
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::Standard(std), Instruction::Standard(std),
&[AdapterType::String], &[AdapterType::String],
); );
@ -181,7 +181,7 @@ impl InstructionBuilder<'_, '_> {
})?; })?;
let mem = self.cx.memory()?; let mem = self.cx.memory()?;
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::View { kind, mem }, Instruction::View { kind, mem },
&[AdapterType::Vector(kind)], &[AdapterType::Vector(kind)],
); );
@ -198,13 +198,13 @@ impl InstructionBuilder<'_, '_> {
.cx .cx
.table_element_adapter(descriptor.shim_idx, descriptor)?; .table_element_adapter(descriptor.shim_idx, descriptor)?;
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::StackClosure { Instruction::StackClosure {
adapter, adapter,
nargs, nargs,
mutable, mutable,
}, },
&[AdapterType::Anyref], &[AdapterType::Function],
); );
} }
@ -224,47 +224,47 @@ impl InstructionBuilder<'_, '_> {
self.instruction( self.instruction(
&[AdapterType::I32], &[AdapterType::I32],
Instruction::AnyrefLoadOwned, Instruction::AnyrefLoadOwned,
&[AdapterType::Anyref], &[AdapterType::Anyref.option()],
); );
} }
Descriptor::I8 => self.out_option_sentinel(), Descriptor::I8 => self.out_option_sentinel(AdapterType::S8),
Descriptor::U8 => self.out_option_sentinel(), Descriptor::U8 => self.out_option_sentinel(AdapterType::U8),
Descriptor::I16 => self.out_option_sentinel(), Descriptor::I16 => self.out_option_sentinel(AdapterType::S16),
Descriptor::U16 => self.out_option_sentinel(), Descriptor::U16 => self.out_option_sentinel(AdapterType::U16),
Descriptor::I32 => self.option_native(true, ValType::I32), Descriptor::I32 => self.option_native(true, ValType::I32),
Descriptor::U32 => self.option_native(false, ValType::I32), Descriptor::U32 => self.option_native(false, ValType::I32),
Descriptor::F32 => self.option_native(true, ValType::F32), Descriptor::F32 => self.option_native(true, ValType::F32),
Descriptor::F64 => self.option_native(true, ValType::F64), Descriptor::F64 => self.option_native(true, ValType::F64),
Descriptor::I64 | Descriptor::U64 => { Descriptor::I64 | Descriptor::U64 => {
let signed = match arg { let (signed, ty) = match arg {
Descriptor::I64 => true, Descriptor::I64 => (true, AdapterType::S64.option()),
_ => false, _ => (false, AdapterType::U64.option()),
}; };
self.instruction( self.instruction(
&[AdapterType::I32; 3], &[AdapterType::I32, AdapterType::I32, AdapterType::I32],
Instruction::Option64FromI32 { signed }, Instruction::Option64FromI32 { signed },
&[AdapterType::Anyref], &[ty],
); );
} }
Descriptor::Boolean => { Descriptor::Boolean => {
self.instruction( self.instruction(
&[AdapterType::I32], &[AdapterType::I32],
Instruction::OptionBoolFromI32, Instruction::OptionBoolFromI32,
&[AdapterType::Anyref], &[AdapterType::Bool.option()],
); );
} }
Descriptor::Char => { Descriptor::Char => {
self.instruction( self.instruction(
&[AdapterType::I32], &[AdapterType::I32],
Instruction::OptionCharFromI32, Instruction::OptionCharFromI32,
&[AdapterType::Anyref], &[AdapterType::String.option()],
); );
} }
Descriptor::Enum { hole } => { Descriptor::Enum { hole } => {
self.instruction( self.instruction(
&[AdapterType::I32], &[AdapterType::I32],
Instruction::OptionEnumFromI32 { hole: *hole }, Instruction::OptionEnumFromI32 { hole: *hole },
&[AdapterType::Anyref], &[AdapterType::U32.option()],
); );
} }
Descriptor::RustStruct(name) => { Descriptor::RustStruct(name) => {
@ -273,7 +273,7 @@ impl InstructionBuilder<'_, '_> {
Instruction::OptionRustFromI32 { Instruction::OptionRustFromI32 {
class: name.to_string(), class: name.to_string(),
}, },
&[AdapterType::Anyref], &[AdapterType::Struct(name.clone()).option()],
); );
} }
Descriptor::Ref(d) => self.outgoing_option_ref(false, d)?, Descriptor::Ref(d) => self.outgoing_option_ref(false, d)?,
@ -291,9 +291,9 @@ impl InstructionBuilder<'_, '_> {
let mem = self.cx.memory()?; let mem = self.cx.memory()?;
let free = self.cx.free()?; let free = self.cx.free()?;
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::OptionVectorLoad { kind, mem, free }, Instruction::OptionVectorLoad { kind, mem, free },
&[AdapterType::Anyref], &[AdapterType::Vector(kind).option()],
); );
} }
@ -313,7 +313,7 @@ impl InstructionBuilder<'_, '_> {
self.instruction( self.instruction(
&[AdapterType::I32], &[AdapterType::I32],
Instruction::TableGet, Instruction::TableGet,
&[AdapterType::Anyref], &[AdapterType::Anyref.option()],
); );
} }
Descriptor::CachedString => self.cached_string(true, false)?, Descriptor::CachedString => self.cached_string(true, false)?,
@ -326,9 +326,9 @@ impl InstructionBuilder<'_, '_> {
})?; })?;
let mem = self.cx.memory()?; let mem = self.cx.memory()?;
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::OptionView { kind, mem }, Instruction::OptionView { kind, mem },
&[AdapterType::Anyref], &[AdapterType::Vector(kind).option()],
); );
} }
_ => bail!( _ => bail!(
@ -352,7 +352,7 @@ impl InstructionBuilder<'_, '_> {
let mem = self.cx.memory()?; let mem = self.cx.memory()?;
let free = self.cx.free()?; let free = self.cx.free()?;
self.instruction( self.instruction(
&[AdapterType::I32; 2], &[AdapterType::I32, AdapterType::I32],
Instruction::CachedStringLoad { Instruction::CachedStringLoad {
owned, owned,
optional, optional,
@ -365,18 +365,19 @@ impl InstructionBuilder<'_, '_> {
} }
fn option_native(&mut self, signed: bool, ty: ValType) { fn option_native(&mut self, signed: bool, ty: ValType) {
let adapter_ty = AdapterType::from_wasm(ty).unwrap();
self.instruction( self.instruction(
&[AdapterType::I32, AdapterType::from_wasm(ty).unwrap()], &[AdapterType::I32, adapter_ty.clone()],
Instruction::ToOptionNative { signed, ty }, 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( self.instruction(
&[AdapterType::I32], &[AdapterType::I32],
Instruction::OptionU32Sentinel, Instruction::OptionU32Sentinel,
&[AdapterType::Anyref], &[ty.option()],
); );
} }
} }

View File

@ -64,7 +64,7 @@ pub enum AdapterJsImportKind {
Normal, Normal,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub enum AdapterType { pub enum AdapterType {
S8, S8,
S16, S16,
@ -82,6 +82,9 @@ pub enum AdapterType {
I32, I32,
I64, I64,
Vector(VectorKind), Vector(VectorKind),
Option(Box<AdapterType>),
Struct(String),
Function,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -338,11 +341,20 @@ impl AdapterType {
AdapterType::F64 => wit_walrus::ValType::F64, AdapterType::F64 => wit_walrus::ValType::F64,
AdapterType::String => wit_walrus::ValType::String, AdapterType::String => wit_walrus::ValType::String,
AdapterType::Anyref => wit_walrus::ValType::Anyref, AdapterType::Anyref => wit_walrus::ValType::Anyref,
AdapterType::I32 => wit_walrus::ValType::I32, AdapterType::I32 => wit_walrus::ValType::I32,
AdapterType::I64 => wit_walrus::ValType::I64, 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 { impl NonstandardWitSection {

View File

@ -13,4 +13,8 @@ impl A {
pub fn other() {} pub fn other() {}
pub fn foo(&self) {} 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) {}
} }

View File

@ -4,3 +4,6 @@ const a = new wbg.A();
wbg.A.other(); wbg.A.other();
a.foo(); a.foo();
a.free(); a.free();
const b: boolean = a.ret_bool()
a.take_bool(b);
a.take_many(b, 1, 2);