Allow web-sys to emit correct typescript declarations from webidl (#1998)

* Update to emit typescript names

* Update to use NamedAnyref

* Update incoming / outgoing

* Remove added space

* Remove comment

* Add basic typescript tests for web-sys
This commit is contained in:
Bennett Hardwick 2020-02-20 01:14:32 +10:00 committed by GitHub
parent 9d55978af5
commit ec1b9453c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 140 additions and 6 deletions

View File

@ -181,6 +181,7 @@ pub struct ImportType {
pub rust_name: Ident, pub rust_name: Ident,
pub js_name: String, pub js_name: String,
pub attrs: Vec<syn::Attribute>, pub attrs: Vec<syn::Attribute>,
pub typescript_name: Option<String>,
pub doc_comment: Option<String>, pub doc_comment: Option<String>,
pub instanceof_shim: String, pub instanceof_shim: String,
pub is_type_of: Option<syn::Expr>, pub is_type_of: Option<syn::Expr>,

View File

@ -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| { let is_type_of = self.is_type_of.as_ref().map(|is_type_of| {
quote! { quote! {
#[inline] #[inline]
@ -611,7 +626,7 @@ impl ToTokens for ast::ImportType {
impl WasmDescribe for #rust_name { impl WasmDescribe for #rust_name {
fn describe() { fn describe() {
JsValue::describe(); #description
} }
} }

View File

@ -31,6 +31,7 @@ tys! {
SLICE SLICE
VECTOR VECTOR
ANYREF ANYREF
NAMED_ANYREF
ENUM ENUM
RUST_STRUCT RUST_STRUCT
CHAR CHAR
@ -62,6 +63,7 @@ pub enum Descriptor {
CachedString, CachedString,
String, String,
Anyref, Anyref,
NamedAnyref(String),
Enum { hole: u32 }, Enum { hole: u32 },
RustStruct(String), RustStruct(String),
Char, Char,
@ -134,11 +136,13 @@ impl Descriptor {
ANYREF => Descriptor::Anyref, ANYREF => Descriptor::Anyref,
ENUM => Descriptor::Enum { hole: get(data) }, ENUM => Descriptor::Enum { hole: get(data) },
RUST_STRUCT => { RUST_STRUCT => {
let name = (0..get(data)) let name = get_string(data);
.map(|_| char::from_u32(get(data)).unwrap())
.collect();
Descriptor::RustStruct(name) Descriptor::RustStruct(name)
} }
NAMED_ANYREF => {
let name = get_string(data);
Descriptor::NamedAnyref(name)
}
CHAR => Descriptor::Char, CHAR => Descriptor::Char,
UNIT => Descriptor::Unit, UNIT => Descriptor::Unit,
CLAMPED => Descriptor::_decode(data, true), CLAMPED => Descriptor::_decode(data, true),
@ -200,6 +204,12 @@ fn get(a: &mut &[u32]) -> u32 {
ret ret
} }
fn get_string(data: &mut &[u32]) -> String {
(0..get(data))
.map(|_| char::from_u32(get(data)).unwrap())
.collect()
}
impl Closure { impl Closure {
fn decode(data: &mut &[u32]) -> Closure { fn decode(data: &mut &[u32]) -> Closure {
let shim_idx = get(data); let shim_idx = get(data);

View File

@ -1238,6 +1238,7 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String) {
adapter2ts(ty, dst); adapter2ts(ty, dst);
dst.push_str(" | undefined"); dst.push_str(" | undefined");
} }
AdapterType::NamedAnyref(name) => dst.push_str(name),
AdapterType::Struct(name) => dst.push_str(name), AdapterType::Struct(name) => dst.push_str(name),
AdapterType::Function => dst.push_str("any"), AdapterType::Function => dst.push_str("any"),
} }

View File

@ -67,6 +67,13 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::I32], &[AdapterType::I32],
); );
} }
Descriptor::NamedAnyref(name) => {
self.instruction(
&[AdapterType::NamedAnyref(name.clone())],
Instruction::I32FromAnyrefOwned,
&[AdapterType::I32]
)
}
Descriptor::RustStruct(class) => { Descriptor::RustStruct(class) => {
self.instruction( self.instruction(
&[AdapterType::Struct(class.clone())], &[AdapterType::Struct(class.clone())],
@ -161,6 +168,13 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::I32], &[AdapterType::I32],
); );
} }
Descriptor::NamedAnyref(name) => {
self.instruction(
&[AdapterType::NamedAnyref(name.clone())],
Instruction::I32FromAnyrefBorrow,
&[AdapterType::I32],
);
}
Descriptor::String | Descriptor::CachedString => { Descriptor::String | Descriptor::CachedString => {
// This allocation is cleaned up once it's received in Rust. // This allocation is cleaned up once it's received in Rust.
self.instruction( self.instruction(
@ -224,6 +238,15 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::I32], &[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::I8 => self.in_option_sentinel(AdapterType::S8),
Descriptor::U8 => self.in_option_sentinel(AdapterType::U8), Descriptor::U8 => self.in_option_sentinel(AdapterType::U8),
Descriptor::I16 => self.in_option_sentinel(AdapterType::S16), Descriptor::I16 => self.in_option_sentinel(AdapterType::S16),

View File

@ -39,6 +39,13 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::Anyref], &[AdapterType::Anyref],
); );
} }
Descriptor::NamedAnyref(name) => {
self.instruction(
&[AdapterType::I32],
Instruction::AnyrefLoadOwned,
&[AdapterType::NamedAnyref(name.clone())],
);
}
Descriptor::I8 => self.outgoing_i32(AdapterType::S8), Descriptor::I8 => self.outgoing_i32(AdapterType::S8),
Descriptor::U8 => self.outgoing_i32(AdapterType::U8), Descriptor::U8 => self.outgoing_i32(AdapterType::U8),
Descriptor::I16 => self.outgoing_i32(AdapterType::S16), Descriptor::I16 => self.outgoing_i32(AdapterType::S16),
@ -162,6 +169,13 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::Anyref], &[AdapterType::Anyref],
); );
} }
Descriptor::NamedAnyref(name) => {
self.instruction(
&[AdapterType::I32],
Instruction::TableGet,
&[AdapterType::NamedAnyref(name.clone())],
);
}
Descriptor::CachedString => self.cached_string(false, false)?, Descriptor::CachedString => self.cached_string(false, false)?,
Descriptor::String => { Descriptor::String => {
@ -227,6 +241,13 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::Anyref.option()], &[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::I8 => self.out_option_sentinel(AdapterType::S8),
Descriptor::U8 => self.out_option_sentinel(AdapterType::U8), Descriptor::U8 => self.out_option_sentinel(AdapterType::U8),
Descriptor::I16 => self.out_option_sentinel(AdapterType::S16), Descriptor::I16 => self.out_option_sentinel(AdapterType::S16),
@ -316,6 +337,13 @@ impl InstructionBuilder<'_, '_> {
&[AdapterType::Anyref.option()], &[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::CachedString => self.cached_string(true, false)?,
Descriptor::String | Descriptor::Slice(_) => { Descriptor::String | Descriptor::Slice(_) => {
let kind = arg.vector_kind().ok_or_else(|| { let kind = arg.vector_kind().ok_or_else(|| {

View File

@ -84,6 +84,7 @@ pub enum AdapterType {
Vector(VectorKind), Vector(VectorKind),
Option(Box<AdapterType>), Option(Box<AdapterType>),
Struct(String), Struct(String),
NamedAnyref(String),
Function, Function,
} }
@ -322,7 +323,7 @@ impl AdapterType {
AdapterType::I64 => walrus::ValType::I64, AdapterType::I64 => walrus::ValType::I64,
AdapterType::F32 => walrus::ValType::F32, AdapterType::F32 => walrus::ValType::F32,
AdapterType::F64 => walrus::ValType::F64, AdapterType::F64 => walrus::ValType::F64,
AdapterType::Anyref => walrus::ValType::Anyref, AdapterType::Anyref | AdapterType::NamedAnyref(_) => walrus::ValType::Anyref,
_ => return None, _ => return None,
}) })
} }
@ -340,7 +341,7 @@ impl AdapterType {
AdapterType::F32 => wit_walrus::ValType::F32, AdapterType::F32 => wit_walrus::ValType::F32,
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 | AdapterType::NamedAnyref(_) => 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,

View File

@ -554,6 +554,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
instanceof_shim: shim, instanceof_shim: shim,
is_type_of, is_type_of,
rust_name: self.ident, rust_name: self.ident,
typescript_name: None,
js_name, js_name,
extends, extends,
vendor_prefixes, vendor_prefixes,

View File

@ -6,6 +6,7 @@ edition = "2018"
[dependencies] [dependencies]
wasm-bindgen = { path = '../..' } wasm-bindgen = { path = '../..' }
web-sys = { path = '../web-sys', features = [ 'HtmlElement', 'Node', 'Document' ] }
[lib] [lib]
crate-type = ['cdylib'] crate-type = ['cdylib']

View File

@ -4,3 +4,4 @@ pub mod opt_args_and_ret;
pub mod optional_fields; pub mod optional_fields;
pub mod simple_fn; pub mod simple_fn;
pub mod simple_struct; pub mod simple_struct;
pub mod web_sys;

View File

@ -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) {}
}

View File

@ -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<typeof wbg["DocStruct"]["new"]>[0]["addEventListener"]
>[1] = event => console.log(event);
newDoc.addEventListener("load", listener);

View File

@ -568,6 +568,7 @@ impl<'src> FirstPassRecord<'src> {
} else { } else {
Some(syn::parse_quote! { |_| false }) Some(syn::parse_quote! { |_| false })
}, },
typescript_name: Some(name.to_string()),
extends: Vec::new(), extends: Vec::new(),
vendor_prefixes: Vec::new(), vendor_prefixes: Vec::new(),
}; };

View File

@ -37,6 +37,7 @@ tys! {
SLICE SLICE
VECTOR VECTOR
ANYREF ANYREF
NAMED_ANYREF
ENUM ENUM
RUST_STRUCT RUST_STRUCT
CHAR CHAR