diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 1f09d9b0..047dfe1a 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -181,6 +181,7 @@ pub struct ImportType { pub rust_name: Ident, pub js_name: String, pub attrs: Vec, + pub typescript_name: Option, pub doc_comment: Option, pub instanceof_shim: String, pub is_type_of: Option, diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 5a0f4f51..5d864009 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -579,6 +579,21 @@ impl ToTokens for ast::ImportType { } }; + let description = if let Some(typescript_name) = &self.typescript_name { + let typescript_name_len = typescript_name.len() as u32; + let typescript_name_chars = typescript_name.chars().map(|c| c as u32); + quote! { + use wasm_bindgen::describe::*; + inform(NAMED_ANYREF); + inform(#typescript_name_len); + #(inform(#typescript_name_chars);)* + } + } else { + quote! { + JsValue::describe() + } + }; + let is_type_of = self.is_type_of.as_ref().map(|is_type_of| { quote! { #[inline] @@ -611,7 +626,7 @@ impl ToTokens for ast::ImportType { impl WasmDescribe for #rust_name { fn describe() { - JsValue::describe(); + #description } } diff --git a/crates/cli-support/src/descriptor.rs b/crates/cli-support/src/descriptor.rs index e83bc731..2d1f151b 100644 --- a/crates/cli-support/src/descriptor.rs +++ b/crates/cli-support/src/descriptor.rs @@ -31,6 +31,7 @@ tys! { SLICE VECTOR ANYREF + NAMED_ANYREF ENUM RUST_STRUCT CHAR @@ -62,6 +63,7 @@ pub enum Descriptor { CachedString, String, Anyref, + NamedAnyref(String), Enum { hole: u32 }, RustStruct(String), Char, @@ -134,11 +136,13 @@ impl Descriptor { ANYREF => Descriptor::Anyref, ENUM => Descriptor::Enum { hole: get(data) }, RUST_STRUCT => { - let name = (0..get(data)) - .map(|_| char::from_u32(get(data)).unwrap()) - .collect(); + let name = get_string(data); Descriptor::RustStruct(name) } + NAMED_ANYREF => { + let name = get_string(data); + Descriptor::NamedAnyref(name) + } CHAR => Descriptor::Char, UNIT => Descriptor::Unit, CLAMPED => Descriptor::_decode(data, true), @@ -200,6 +204,12 @@ fn get(a: &mut &[u32]) -> u32 { ret } +fn get_string(data: &mut &[u32]) -> String { + (0..get(data)) + .map(|_| char::from_u32(get(data)).unwrap()) + .collect() +} + impl Closure { fn decode(data: &mut &[u32]) -> Closure { let shim_idx = get(data); diff --git a/crates/cli-support/src/js/binding.rs b/crates/cli-support/src/js/binding.rs index d5b47b2e..9af2a469 100644 --- a/crates/cli-support/src/js/binding.rs +++ b/crates/cli-support/src/js/binding.rs @@ -1238,6 +1238,7 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) { adapter2ts(ty, dst); dst.push_str(" | undefined"); } + AdapterType::NamedAnyref(name) => dst.push_str(name), AdapterType::Struct(name) => dst.push_str(name), AdapterType::Function => dst.push_str("any"), } diff --git a/crates/cli-support/src/wit/incoming.rs b/crates/cli-support/src/wit/incoming.rs index 5638f146..515027bb 100644 --- a/crates/cli-support/src/wit/incoming.rs +++ b/crates/cli-support/src/wit/incoming.rs @@ -67,6 +67,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); } + Descriptor::NamedAnyref(name) => { + self.instruction( + &[AdapterType::NamedAnyref(name.clone())], + Instruction::I32FromAnyrefOwned, + &[AdapterType::I32] + ) + } Descriptor::RustStruct(class) => { self.instruction( &[AdapterType::Struct(class.clone())], @@ -161,6 +168,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); } + Descriptor::NamedAnyref(name) => { + self.instruction( + &[AdapterType::NamedAnyref(name.clone())], + Instruction::I32FromAnyrefBorrow, + &[AdapterType::I32], + ); + } Descriptor::String | Descriptor::CachedString => { // This allocation is cleaned up once it's received in Rust. self.instruction( @@ -224,6 +238,15 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::I32], ); } + Descriptor::NamedAnyref(name) => { + self.instruction( + &[AdapterType::NamedAnyref(name.clone()).option()], + Instruction::I32FromOptionAnyref { + table_and_alloc: None, + }, + &[AdapterType::I32], + ); + } Descriptor::I8 => self.in_option_sentinel(AdapterType::S8), Descriptor::U8 => self.in_option_sentinel(AdapterType::U8), Descriptor::I16 => self.in_option_sentinel(AdapterType::S16), diff --git a/crates/cli-support/src/wit/outgoing.rs b/crates/cli-support/src/wit/outgoing.rs index 649a6b07..69d15c31 100644 --- a/crates/cli-support/src/wit/outgoing.rs +++ b/crates/cli-support/src/wit/outgoing.rs @@ -39,6 +39,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::Anyref], ); } + Descriptor::NamedAnyref(name) => { + self.instruction( + &[AdapterType::I32], + Instruction::AnyrefLoadOwned, + &[AdapterType::NamedAnyref(name.clone())], + ); + } Descriptor::I8 => self.outgoing_i32(AdapterType::S8), Descriptor::U8 => self.outgoing_i32(AdapterType::U8), Descriptor::I16 => self.outgoing_i32(AdapterType::S16), @@ -162,6 +169,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::Anyref], ); } + Descriptor::NamedAnyref(name) => { + self.instruction( + &[AdapterType::I32], + Instruction::TableGet, + &[AdapterType::NamedAnyref(name.clone())], + ); + } Descriptor::CachedString => self.cached_string(false, false)?, Descriptor::String => { @@ -227,6 +241,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::Anyref.option()], ); } + Descriptor::NamedAnyref(name) => { + self.instruction( + &[AdapterType::I32], + Instruction::AnyrefLoadOwned, + &[AdapterType::NamedAnyref(name.clone()).option()], + ); + } Descriptor::I8 => self.out_option_sentinel(AdapterType::S8), Descriptor::U8 => self.out_option_sentinel(AdapterType::U8), Descriptor::I16 => self.out_option_sentinel(AdapterType::S16), @@ -316,6 +337,13 @@ impl InstructionBuilder<'_, '_> { &[AdapterType::Anyref.option()], ); } + Descriptor::NamedAnyref(name) => { + self.instruction( + &[AdapterType::I32], + Instruction::TableGet, + &[AdapterType::NamedAnyref(name.clone()).option()], + ); + } Descriptor::CachedString => self.cached_string(true, false)?, Descriptor::String | Descriptor::Slice(_) => { let kind = arg.vector_kind().ok_or_else(|| { diff --git a/crates/cli-support/src/wit/standard.rs b/crates/cli-support/src/wit/standard.rs index fdbfa55d..37ca43f1 100644 --- a/crates/cli-support/src/wit/standard.rs +++ b/crates/cli-support/src/wit/standard.rs @@ -84,6 +84,7 @@ pub enum AdapterType { Vector(VectorKind), Option(Box), Struct(String), + NamedAnyref(String), Function, } @@ -322,7 +323,7 @@ impl AdapterType { AdapterType::I64 => walrus::ValType::I64, AdapterType::F32 => walrus::ValType::F32, AdapterType::F64 => walrus::ValType::F64, - AdapterType::Anyref => walrus::ValType::Anyref, + AdapterType::Anyref | AdapterType::NamedAnyref(_) => walrus::ValType::Anyref, _ => return None, }) } @@ -340,7 +341,7 @@ impl AdapterType { AdapterType::F32 => wit_walrus::ValType::F32, AdapterType::F64 => wit_walrus::ValType::F64, AdapterType::String => wit_walrus::ValType::String, - AdapterType::Anyref => wit_walrus::ValType::Anyref, + AdapterType::Anyref | AdapterType::NamedAnyref(_) => wit_walrus::ValType::Anyref, AdapterType::I32 => wit_walrus::ValType::I32, AdapterType::I64 => wit_walrus::ValType::I64, diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 38edd2d4..2be71c16 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -554,6 +554,7 @@ impl ConvertToAst for syn::ForeignItemType { instanceof_shim: shim, is_type_of, rust_name: self.ident, + typescript_name: None, js_name, extends, vendor_prefixes, diff --git a/crates/typescript-tests/Cargo.toml b/crates/typescript-tests/Cargo.toml index 505c8621..bf617379 100644 --- a/crates/typescript-tests/Cargo.toml +++ b/crates/typescript-tests/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] wasm-bindgen = { path = '../..' } +web-sys = { path = '../web-sys', features = [ 'HtmlElement', 'Node', 'Document' ] } [lib] crate-type = ['cdylib'] diff --git a/crates/typescript-tests/src/lib.rs b/crates/typescript-tests/src/lib.rs index 3198524a..6b2fa26c 100644 --- a/crates/typescript-tests/src/lib.rs +++ b/crates/typescript-tests/src/lib.rs @@ -4,3 +4,4 @@ pub mod opt_args_and_ret; pub mod optional_fields; pub mod simple_fn; pub mod simple_struct; +pub mod web_sys; diff --git a/crates/typescript-tests/src/web_sys.rs b/crates/typescript-tests/src/web_sys.rs new file mode 100644 index 00000000..36a469c2 --- /dev/null +++ b/crates/typescript-tests/src/web_sys.rs @@ -0,0 +1,24 @@ +use wasm_bindgen::prelude::*; +use web_sys::*; + +#[wasm_bindgen] +pub struct DocStruct { + doc: Document, +} + +#[wasm_bindgen] +impl DocStruct { + pub fn new(doc: Document) -> Self { + Self { doc } + } + + pub fn get_doc(&self) -> Document { + self.doc.clone() + } + + pub fn append_element(&self, element: &HtmlElement) { + self.doc.body().unwrap().append_child(element).unwrap(); + } + + pub fn append_many(&self, _: &HtmlElement, _: &HtmlElement, _: &HtmlElement) {} +} diff --git a/crates/typescript-tests/src/web_sys.ts b/crates/typescript-tests/src/web_sys.ts new file mode 100644 index 00000000..52ad04dc --- /dev/null +++ b/crates/typescript-tests/src/web_sys.ts @@ -0,0 +1,26 @@ +import * as wbg from "../pkg/typescript_tests"; + +const doc: Document = document; + +const docStruct = wbg.DocStruct.new(doc); + +const el: HTMLElement = document.createElement("a"); + +docStruct.append_element(el); +docStruct.append_many(el, el, el); + +const newDoc = docStruct.get_doc(); + +// This test ensures that the correct Typescript types are +// used. If "newDoc" is "any", then "event" will cause the +// compilation to fail because of noImplicitAny. +newDoc.addEventListener("load", event => { + console.log(event); +}); + +// Same as above, but testing that the param is a document. +const listener: Parameters< + Parameters[0]["addEventListener"] +>[1] = event => console.log(event); + +newDoc.addEventListener("load", listener); diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 07eb2c44..15dc3a9b 100644 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -568,6 +568,7 @@ impl<'src> FirstPassRecord<'src> { } else { Some(syn::parse_quote! { |_| false }) }, + typescript_name: Some(name.to_string()), extends: Vec::new(), vendor_prefixes: Vec::new(), }; diff --git a/src/describe.rs b/src/describe.rs index b1cfeaaf..eec05bd4 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -37,6 +37,7 @@ tys! { SLICE VECTOR ANYREF + NAMED_ANYREF ENUM RUST_STRUCT CHAR