Skip to content

Commit

Permalink
Store richer adapter types, don't use instructions for TypeScript
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 93fedf8 commit 8e5c8ed
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 209 deletions.
242 changes: 106 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 8e5c8ed

Please sign in to comment.