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> {
/// 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<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
/// so what class it's constructing.
constructor: Option<String>,
@ -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<TypescriptArg>,
/// 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<String>,
}
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<String>,
pub ts_ret_ty: Option<String>,
}
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<Vec<String>>,
) -> Result<String, Error> {
) -> Result<JsFunction, Error> {
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,79 @@ 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<String>, Option<String>) {
// 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>,
) -> 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 {
if ts != "void" {
ret.push_str(&format!("@returns {{{}}}", ts));
}
}
ret
}
@ -299,7 +319,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
tmp: 0,
finally: String::new(),
prelude: String::new(),
typescript: Vec::new(),
stack: Vec::new(),
}
}
@ -308,31 +327,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 +420,6 @@ impl<'a, 'b> JsBuilder<'a, 'b> {
malloc: walrus::FunctionId,
realloc: Option<walrus::FunctionId>,
) -> 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 +503,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 +516,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 +529,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 +586,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 +593,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 +612,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 +622,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 +629,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 +643,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 +665,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 +689,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 +704,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 +711,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 +718,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 +727,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 +734,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 +742,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 +763,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 +786,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 +809,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 +846,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 +885,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 +901,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 +927,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 +937,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 +982,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 +999,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 +1019,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 +1026,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 +1038,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 +1054,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 +1067,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 +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.
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");
}
}

View File

@ -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

View File

@ -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],
);

View File

@ -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()],
);
}
}

View File

@ -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<AdapterType>),
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 {