Skip to content

Commit

Permalink
Store richer adapter types, don't use instructions for TypeScript (#1945
Browse files Browse the repository at this point in the history
)

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
  • Loading branch information
alexcrichton committed Jan 7, 2020
1 parent 6c27376 commit f66d83f
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 209 deletions.
244 changes: 108 additions & 136 deletions crates/cli-support/src/js/binding.rs

Large diffs are not rendered by default.

32 changes: 18 additions & 14 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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.
Expand All @@ -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) => {
Expand All @@ -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");
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/cli-support/src/multivalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
51 changes: 26 additions & 25 deletions crates/cli-support/src/wit/incoming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl InstructionBuilder<'_, '_> {
}
Descriptor::RustStruct(class) => {
self.instruction(
&[AdapterType::Anyref],
&[AdapterType::Struct(class.clone())],
Instruction::I32FromAnyrefRustOwned {
class: class.clone(),
},
Expand Down Expand Up @@ -147,7 +147,7 @@ impl InstructionBuilder<'_, '_> {
match arg {
Descriptor::RustStruct(class) => {
self.instruction(
&[AdapterType::Anyref],
&[AdapterType::Struct(class.clone())],
Instruction::I32FromAnyrefRustBorrow {
class: class.clone(),
},
Expand Down Expand Up @@ -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(),
},
Expand All @@ -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],
);
}

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

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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],
);
Expand Down
Loading

0 comments on commit f66d83f

Please sign in to comment.