mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-21 08:41:35 +00:00
Dramatically improving the build time of web-sys (#2012)
* Pre-generating web-sys * Fixing build errors * Minor refactor for the unit tests * Changing to generate #[wasm_bindgen} annotations * Fixing code generation * Adding in main bin to wasm-bindgen-webidl * Fixing more problems * Adding in support for unstable APIs * Fixing bug with code generation * More code generation fixes * Improving the webidl program * Removing unnecessary cfg from the generated code * Splitting doc comments onto separate lines * Improving the generation for unstable features * Adding in support for string values in enums * Now runs rustfmt on the mod.rs file * Fixing codegen for constructors * Fixing webidl-tests * Fixing build errors * Another fix for build errors * Renaming typescript_name to typescript_type * Adding in docs for typescript_type * Adding in CI script to verify that web-sys is up to date * Fixing CI script * Fixing CI script * Don't suppress git diff output * Remove duplicate definitions of `Location` Looks to be a preexisting bug in wasm-bindgen? * Regenerate webidl * Try to get the git diff command right * Handle named constructors in WebIDL * Remove stray rustfmt.toml * Add back NamedConstructorBar definition in tests * Run stable rustfmt over everything * Don't run Cargo in a build script Instead refactor things so webidl-tests can use the Rust-code-generation as a library in a build script. Also fixes `cargo fmt` in the repository. * Fixup generated code * Running web-sys checks on stable * Improving the code generation a little * Running rustfmt Co-authored-by: Alex Crichton <alex@alexcrichton.com>
This commit is contained in:
86
crates/webidl/src/constants.rs
Normal file
86
crates/webidl/src/constants.rs
Normal file
@ -0,0 +1,86 @@
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::BTreeSet;
|
||||
use std::iter::FromIterator;
|
||||
|
||||
lazy_static! {
|
||||
pub(crate) static ref BUILTIN_IDENTS: BTreeSet<&'static str> = BTreeSet::from_iter(vec![
|
||||
"str",
|
||||
"char",
|
||||
"bool",
|
||||
"JsValue",
|
||||
"u8",
|
||||
"i8",
|
||||
"u16",
|
||||
"i16",
|
||||
"u32",
|
||||
"i32",
|
||||
"u64",
|
||||
"i64",
|
||||
"usize",
|
||||
"isize",
|
||||
"f32",
|
||||
"f64",
|
||||
"Result",
|
||||
"String",
|
||||
"Vec",
|
||||
"Option",
|
||||
"Array",
|
||||
"ArrayBuffer",
|
||||
"Object",
|
||||
"Promise",
|
||||
"Function",
|
||||
"Clamped",
|
||||
]);
|
||||
|
||||
|
||||
// whitelist a few names that have known polyfills
|
||||
pub(crate) static ref POLYFILL_INTERFACES: BTreeSet<&'static str> = BTreeSet::from_iter(vec![
|
||||
"AudioContext",
|
||||
"OfflineAudioContext",
|
||||
]);
|
||||
|
||||
|
||||
pub(crate) static ref IMMUTABLE_SLICE_WHITELIST: BTreeSet<&'static str> = BTreeSet::from_iter(vec![
|
||||
// WebGlRenderingContext, WebGl2RenderingContext
|
||||
"uniform1fv",
|
||||
"uniform2fv",
|
||||
"uniform3fv",
|
||||
"uniform4fv",
|
||||
"uniform1iv",
|
||||
"uniform2iv",
|
||||
"uniform3iv",
|
||||
"uniform4iv",
|
||||
"uniformMatrix2fv",
|
||||
"uniformMatrix3fv",
|
||||
"uniformMatrix4fv",
|
||||
"uniformMatrix2x3fv",
|
||||
"uniformMatrix2x4fv",
|
||||
"uniformMatrix3x2fv",
|
||||
"uniformMatrix3x4fv",
|
||||
"uniformMatrix4x2fv",
|
||||
"uniformMatrix4x3fv",
|
||||
"vertexAttrib1fv",
|
||||
"vertexAttrib2fv",
|
||||
"vertexAttrib3fv",
|
||||
"vertexAttrib4fv",
|
||||
"bufferData",
|
||||
"bufferSubData",
|
||||
"texImage2D",
|
||||
"texSubImage2D",
|
||||
"compressedTexImage2D",
|
||||
// WebGl2RenderingContext
|
||||
"uniform1uiv",
|
||||
"uniform2uiv",
|
||||
"uniform3uiv",
|
||||
"uniform4uiv",
|
||||
"texImage3D",
|
||||
"texSubImage3D",
|
||||
"compressedTexImage3D",
|
||||
"clearBufferfv",
|
||||
"clearBufferiv",
|
||||
"clearBufferuiv",
|
||||
// WebSocket
|
||||
"send",
|
||||
// TODO: Add another type's functions here. Leave a comment header with the type name
|
||||
]);
|
||||
}
|
@ -10,7 +10,6 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use proc_macro2::Ident;
|
||||
use weedle;
|
||||
use weedle::argument::Argument;
|
||||
use weedle::attribute::*;
|
||||
@ -28,7 +27,6 @@ use crate::{
|
||||
/// Collection of constructs that may use partial.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct FirstPassRecord<'src> {
|
||||
pub(crate) builtin_idents: BTreeSet<Ident>,
|
||||
pub(crate) interfaces: BTreeMap<&'src str, InterfaceData<'src>>,
|
||||
pub(crate) enums: BTreeMap<&'src str, EnumData<'src>>,
|
||||
/// The mixins, mapping their name to the webidl ast node for the mixin.
|
||||
@ -39,7 +37,6 @@ pub(crate) struct FirstPassRecord<'src> {
|
||||
pub(crate) dictionaries: BTreeMap<&'src str, DictionaryData<'src>>,
|
||||
pub(crate) callbacks: BTreeSet<&'src str>,
|
||||
pub(crate) callback_interfaces: BTreeMap<&'src str, CallbackInterfaceData<'src>>,
|
||||
pub(crate) immutable_slice_whitelist: BTreeSet<&'static str>,
|
||||
}
|
||||
|
||||
pub(crate) struct AttributeInterfaceData<'src> {
|
||||
@ -104,7 +101,8 @@ pub(crate) struct CallbackInterfaceData<'src> {
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
||||
pub(crate) enum OperationId<'src> {
|
||||
Constructor(IgnoreTraits<&'src str>),
|
||||
Constructor,
|
||||
NamedConstructor(IgnoreTraits<&'src str>),
|
||||
/// The name of a function in crates/web-sys/webidls/enabled/*.webidl
|
||||
///
|
||||
/// ex: Operation(Some("vertexAttrib1fv"))
|
||||
@ -392,7 +390,7 @@ fn process_interface_attribute<'src>(
|
||||
record,
|
||||
FirstPassOperationType::Interface,
|
||||
self_name,
|
||||
&[OperationId::Constructor(IgnoreTraits(self_name))],
|
||||
&[OperationId::Constructor],
|
||||
&list.args.body.list,
|
||||
&return_ty,
|
||||
&None,
|
||||
@ -404,7 +402,7 @@ fn process_interface_attribute<'src>(
|
||||
record,
|
||||
FirstPassOperationType::Interface,
|
||||
self_name,
|
||||
&[OperationId::Constructor(IgnoreTraits(self_name))],
|
||||
&[OperationId::Constructor],
|
||||
&[],
|
||||
&return_ty,
|
||||
&None,
|
||||
@ -416,7 +414,7 @@ fn process_interface_attribute<'src>(
|
||||
record,
|
||||
FirstPassOperationType::Interface,
|
||||
self_name,
|
||||
&[OperationId::Constructor(IgnoreTraits(
|
||||
&[OperationId::NamedConstructor(IgnoreTraits(
|
||||
list.rhs_identifier.0,
|
||||
))],
|
||||
&list.args.body.list,
|
||||
|
887
crates/webidl/src/generator.rs
Normal file
887
crates/webidl/src/generator.rs
Normal file
@ -0,0 +1,887 @@
|
||||
use proc_macro2::Literal;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use std::collections::BTreeSet;
|
||||
use syn::{Ident, Type};
|
||||
use wasm_bindgen_backend::util::{raw_ident, rust_ident};
|
||||
|
||||
use crate::constants::{BUILTIN_IDENTS, POLYFILL_INTERFACES};
|
||||
use crate::traverse::TraverseType;
|
||||
use crate::util::{get_cfg_features, mdn_doc, required_doc_string, snake_case_ident};
|
||||
use crate::Options;
|
||||
|
||||
fn add_features(features: &mut BTreeSet<String>, ty: &impl TraverseType) {
|
||||
ty.traverse_type(&mut |ident| {
|
||||
let ident = ident.to_string();
|
||||
|
||||
if !BUILTIN_IDENTS.contains(ident.as_str()) {
|
||||
features.insert(ident);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn get_features_doc(options: &Options, name: String) -> Option<String> {
|
||||
let mut features = BTreeSet::new();
|
||||
features.insert(name);
|
||||
required_doc_string(options, &features)
|
||||
}
|
||||
|
||||
fn comment(mut comment: String, features: &Option<String>) -> TokenStream {
|
||||
if let Some(s) = features {
|
||||
comment.push_str(s);
|
||||
}
|
||||
|
||||
let lines = comment.lines().map(|doc| quote!( #[doc = #doc] ));
|
||||
|
||||
quote! {
|
||||
#(#lines)*
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_unstable_attr(unstable: bool) -> Option<proc_macro2::TokenStream> {
|
||||
if unstable {
|
||||
Some(quote! {
|
||||
#[cfg(web_sys_unstable_apis)]
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_unstable_docs(unstable: bool) -> Option<proc_macro2::TokenStream> {
|
||||
if unstable {
|
||||
Some(quote! {
|
||||
#[doc = ""]
|
||||
#[doc = "*This API is unstable and requires `--cfg=web_sys_unstable_apis` to be activated, as"]
|
||||
#[doc = "[described in the `wasm-bindgen` guide](https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html)*"]
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_arguments(arguments: &[(Ident, Type)], variadic: bool) -> Vec<TokenStream> {
|
||||
arguments
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, (name, ty))| {
|
||||
if variadic && i + 1 == arguments.len() {
|
||||
quote!( #name: &::js_sys::Array )
|
||||
} else {
|
||||
quote!( #name: #ty )
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn generate_variadic(variadic: bool) -> Option<TokenStream> {
|
||||
if variadic {
|
||||
Some(quote!(variadic,))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnumVariant {
|
||||
pub name: Ident,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
impl EnumVariant {
|
||||
fn generate(&self) -> TokenStream {
|
||||
let EnumVariant { name, value } = self;
|
||||
|
||||
quote!( #name = #value )
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Enum {
|
||||
pub name: Ident,
|
||||
pub variants: Vec<EnumVariant>,
|
||||
pub unstable: bool,
|
||||
}
|
||||
|
||||
impl Enum {
|
||||
pub fn generate(&self, options: &Options) -> TokenStream {
|
||||
let Enum {
|
||||
name,
|
||||
variants,
|
||||
unstable,
|
||||
} = self;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(*unstable);
|
||||
let unstable_docs = maybe_unstable_docs(*unstable);
|
||||
|
||||
let doc_comment = comment(
|
||||
format!("The `{}` enum.", name),
|
||||
&get_features_doc(options, name.to_string()),
|
||||
);
|
||||
|
||||
let variants = variants
|
||||
.into_iter()
|
||||
.map(|variant| variant.generate())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
#![allow(unused_imports)]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#unstable_attr
|
||||
#[wasm_bindgen]
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum #name {
|
||||
#(#variants),*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InterfaceAttributeKind {
|
||||
Getter,
|
||||
Setter,
|
||||
}
|
||||
|
||||
pub struct InterfaceAttribute {
|
||||
pub js_name: String,
|
||||
pub ty: Type,
|
||||
pub is_static: bool,
|
||||
pub structural: bool,
|
||||
pub catch: bool,
|
||||
pub kind: InterfaceAttributeKind,
|
||||
pub unstable: bool,
|
||||
}
|
||||
|
||||
impl InterfaceAttribute {
|
||||
fn generate(
|
||||
&self,
|
||||
options: &Options,
|
||||
parent_name: &Ident,
|
||||
parent_js_name: &str,
|
||||
parents: &[Ident],
|
||||
) -> TokenStream {
|
||||
let InterfaceAttribute {
|
||||
js_name,
|
||||
ty,
|
||||
is_static,
|
||||
structural,
|
||||
catch,
|
||||
kind,
|
||||
unstable,
|
||||
} = self;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(*unstable);
|
||||
let unstable_docs = maybe_unstable_docs(*unstable);
|
||||
|
||||
let mdn_docs = mdn_doc(parent_js_name, Some(js_name));
|
||||
|
||||
let mut features = BTreeSet::new();
|
||||
|
||||
add_features(&mut features, ty);
|
||||
|
||||
for parent in parents {
|
||||
features.remove(&parent.to_string());
|
||||
}
|
||||
|
||||
features.remove(&parent_name.to_string());
|
||||
|
||||
let cfg_features = get_cfg_features(options, &features);
|
||||
|
||||
features.insert(parent_name.to_string());
|
||||
|
||||
let doc_comment = required_doc_string(options, &features);
|
||||
|
||||
let structural = if *structural {
|
||||
quote!(structural,)
|
||||
} else {
|
||||
quote!(final,)
|
||||
};
|
||||
|
||||
let (method, this) = if *is_static {
|
||||
(quote!( static_method_of = #parent_name, ), None)
|
||||
} else {
|
||||
(quote!(method,), Some(quote!( this: &#parent_name, )))
|
||||
};
|
||||
|
||||
let (prefix, attr, def) = match kind {
|
||||
InterfaceAttributeKind::Getter => {
|
||||
let name = rust_ident(&snake_case_ident(js_name));
|
||||
|
||||
let ty = if *catch {
|
||||
quote!( Result<#ty, JsValue> )
|
||||
} else {
|
||||
quote!( #ty )
|
||||
};
|
||||
|
||||
(
|
||||
"Getter",
|
||||
quote!(getter,),
|
||||
quote!( pub fn #name(#this) -> #ty; ),
|
||||
)
|
||||
}
|
||||
|
||||
InterfaceAttributeKind::Setter => {
|
||||
let name = rust_ident(&format!("set_{}", snake_case_ident(js_name)));
|
||||
|
||||
let ret_ty = if *catch {
|
||||
Some(quote!( -> Result<(), JsValue> ))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(
|
||||
"Setter",
|
||||
quote!(setter,),
|
||||
quote!( pub fn #name(#this value: #ty) #ret_ty; ),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let catch = if *catch { Some(quote!(catch,)) } else { None };
|
||||
|
||||
let doc_comment = comment(
|
||||
format!(
|
||||
"{} for the `{}` field of this object.\n\n{}",
|
||||
prefix, js_name, mdn_docs
|
||||
),
|
||||
&doc_comment,
|
||||
);
|
||||
|
||||
let js_name = raw_ident(js_name);
|
||||
|
||||
quote! {
|
||||
#unstable_attr
|
||||
#cfg_features
|
||||
#[wasm_bindgen(
|
||||
#structural
|
||||
#catch
|
||||
#method
|
||||
#attr
|
||||
js_class = #parent_js_name,
|
||||
js_name = #js_name
|
||||
)]
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
#def
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InterfaceMethodKind {
|
||||
Constructor(Option<String>),
|
||||
Regular,
|
||||
IndexingGetter,
|
||||
IndexingSetter,
|
||||
IndexingDeleter,
|
||||
}
|
||||
|
||||
pub struct InterfaceMethod {
|
||||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub arguments: Vec<(Ident, Type)>,
|
||||
pub ret_ty: Option<Type>,
|
||||
pub kind: InterfaceMethodKind,
|
||||
pub is_static: bool,
|
||||
pub structural: bool,
|
||||
pub catch: bool,
|
||||
pub variadic: bool,
|
||||
pub unstable: bool,
|
||||
}
|
||||
|
||||
impl InterfaceMethod {
|
||||
fn generate(
|
||||
&self,
|
||||
options: &Options,
|
||||
parent_name: &Ident,
|
||||
parent_js_name: String,
|
||||
parents: &[Ident],
|
||||
) -> TokenStream {
|
||||
let InterfaceMethod {
|
||||
name,
|
||||
js_name,
|
||||
arguments,
|
||||
ret_ty,
|
||||
kind,
|
||||
is_static,
|
||||
structural,
|
||||
catch,
|
||||
variadic,
|
||||
unstable,
|
||||
} = self;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(*unstable);
|
||||
let unstable_docs = maybe_unstable_docs(*unstable);
|
||||
|
||||
let mut is_constructor = false;
|
||||
|
||||
let mut extra_args = vec![quote!( js_class = #parent_js_name )];
|
||||
|
||||
let doc_comment = match kind {
|
||||
InterfaceMethodKind::Constructor(name) => {
|
||||
is_constructor = true;
|
||||
if let Some(name) = name {
|
||||
extra_args[0] = quote!( js_class = #name );
|
||||
}
|
||||
format!(
|
||||
"The `new {}(..)` constructor, creating a new \
|
||||
instance of `{0}`.\n\n{}",
|
||||
parent_name,
|
||||
mdn_doc(&parent_js_name, Some(&parent_js_name))
|
||||
)
|
||||
}
|
||||
InterfaceMethodKind::Regular => {
|
||||
{
|
||||
let js_name = raw_ident(js_name);
|
||||
extra_args.push(quote!( js_name = #js_name ));
|
||||
}
|
||||
format!(
|
||||
"The `{}()` method.\n\n{}",
|
||||
js_name,
|
||||
mdn_doc(&parent_js_name, Some(js_name))
|
||||
)
|
||||
}
|
||||
InterfaceMethodKind::IndexingGetter => {
|
||||
extra_args.push(quote!(indexing_getter));
|
||||
format!("Indexing getter.\n\n")
|
||||
}
|
||||
InterfaceMethodKind::IndexingSetter => {
|
||||
extra_args.push(quote!(indexing_setter));
|
||||
format!("Indexing setter.\n\n")
|
||||
}
|
||||
InterfaceMethodKind::IndexingDeleter => {
|
||||
extra_args.push(quote!(indexing_deleter));
|
||||
format!("Indexing deleter.\n\n")
|
||||
}
|
||||
};
|
||||
|
||||
let mut features = BTreeSet::new();
|
||||
|
||||
for (_, ty) in arguments.iter() {
|
||||
add_features(&mut features, ty);
|
||||
}
|
||||
|
||||
if let Some(ty) = ret_ty {
|
||||
add_features(&mut features, ty);
|
||||
}
|
||||
|
||||
for parent in parents {
|
||||
features.remove(&parent.to_string());
|
||||
}
|
||||
|
||||
features.remove(&parent_name.to_string());
|
||||
|
||||
let cfg_features = get_cfg_features(options, &features);
|
||||
|
||||
features.insert(parent_name.to_string());
|
||||
|
||||
let doc_comment = comment(doc_comment, &required_doc_string(options, &features));
|
||||
|
||||
let ret = ret_ty.as_ref().map(|ret| quote!( #ret ));
|
||||
|
||||
let ret = if *catch {
|
||||
let ret = ret.unwrap_or_else(|| quote!(()));
|
||||
Some(quote!( Result<#ret, JsValue> ))
|
||||
} else {
|
||||
ret
|
||||
};
|
||||
|
||||
let ret = ret.as_ref().map(|ret| quote!( -> #ret ));
|
||||
|
||||
let catch = if *catch { Some(quote!(catch,)) } else { None };
|
||||
|
||||
let (method, this) = if is_constructor {
|
||||
assert!(!is_static);
|
||||
|
||||
(quote!(constructor,), None)
|
||||
} else if *is_static {
|
||||
(quote!( static_method_of = #parent_name, ), None)
|
||||
} else {
|
||||
let structural = if *structural {
|
||||
quote!(structural)
|
||||
} else {
|
||||
quote!(final)
|
||||
};
|
||||
|
||||
(
|
||||
quote!( method, #structural, ),
|
||||
Some(quote!( this: &#parent_name, )),
|
||||
)
|
||||
};
|
||||
|
||||
let arguments = generate_arguments(arguments, *variadic);
|
||||
let variadic = generate_variadic(*variadic);
|
||||
|
||||
quote! {
|
||||
#unstable_attr
|
||||
#cfg_features
|
||||
#[wasm_bindgen(
|
||||
#catch
|
||||
#method
|
||||
#variadic
|
||||
#(#extra_args),*
|
||||
)]
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
pub fn #name(#this #(#arguments),*) #ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InterfaceConstValue {
|
||||
BooleanLiteral(bool),
|
||||
FloatLiteral(f64),
|
||||
SignedIntegerLiteral(i64),
|
||||
UnsignedIntegerLiteral(u64),
|
||||
}
|
||||
|
||||
impl InterfaceConstValue {
|
||||
fn generate(&self) -> TokenStream {
|
||||
use InterfaceConstValue::*;
|
||||
|
||||
match self {
|
||||
BooleanLiteral(false) => quote!(false),
|
||||
BooleanLiteral(true) => quote!(true),
|
||||
// the actual type is unknown because of typedefs
|
||||
// so we cannot use std::fxx::INFINITY
|
||||
// but we can use type inference
|
||||
FloatLiteral(f) if f.is_infinite() && f.is_sign_positive() => quote!(1.0 / 0.0),
|
||||
FloatLiteral(f) if f.is_infinite() && f.is_sign_negative() => quote!(-1.0 / 0.0),
|
||||
FloatLiteral(f) if f.is_nan() => quote!(0.0 / 0.0),
|
||||
// again no suffix
|
||||
// panics on +-inf, nan
|
||||
FloatLiteral(f) => {
|
||||
let f = Literal::f64_suffixed(*f);
|
||||
quote!(#f)
|
||||
}
|
||||
SignedIntegerLiteral(i) => {
|
||||
let i = Literal::i64_suffixed(*i);
|
||||
quote!(#i)
|
||||
}
|
||||
UnsignedIntegerLiteral(i) => {
|
||||
let i = Literal::u64_suffixed(*i);
|
||||
quote!(#i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InterfaceConst {
|
||||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub ty: syn::Type,
|
||||
pub value: InterfaceConstValue,
|
||||
pub unstable: bool,
|
||||
}
|
||||
|
||||
impl InterfaceConst {
|
||||
fn generate(
|
||||
&self,
|
||||
options: &Options,
|
||||
parent_name: &Ident,
|
||||
parent_js_name: &str,
|
||||
) -> TokenStream {
|
||||
let name = &self.name;
|
||||
let ty = &self.ty;
|
||||
let js_name = &self.js_name;
|
||||
let value = self.value.generate();
|
||||
let unstable = self.unstable;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(unstable);
|
||||
let unstable_docs = maybe_unstable_docs(unstable);
|
||||
|
||||
let doc_comment = comment(
|
||||
format!("The `{}.{}` const.", parent_js_name, js_name),
|
||||
&get_features_doc(options, parent_name.to_string()),
|
||||
);
|
||||
|
||||
quote! {
|
||||
#unstable_attr
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
pub const #name: #ty = #value as #ty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Interface {
|
||||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub deprecated: Option<String>,
|
||||
pub has_interface: bool,
|
||||
pub parents: Vec<Ident>,
|
||||
pub consts: Vec<InterfaceConst>,
|
||||
pub attributes: Vec<InterfaceAttribute>,
|
||||
pub methods: Vec<InterfaceMethod>,
|
||||
pub unstable: bool,
|
||||
}
|
||||
|
||||
impl Interface {
|
||||
pub fn generate(&self, options: &Options) -> TokenStream {
|
||||
let Interface {
|
||||
name,
|
||||
js_name,
|
||||
deprecated,
|
||||
has_interface,
|
||||
parents,
|
||||
consts,
|
||||
attributes,
|
||||
methods,
|
||||
unstable,
|
||||
} = self;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(*unstable);
|
||||
let unstable_docs = maybe_unstable_docs(*unstable);
|
||||
|
||||
let doc_comment = comment(
|
||||
format!("The `{}` class.\n\n{}", name, mdn_doc(js_name, None)),
|
||||
&get_features_doc(options, name.to_string()),
|
||||
);
|
||||
|
||||
let deprecated = deprecated
|
||||
.as_ref()
|
||||
.map(|msg| quote!( #[deprecated(note = #msg)] ));
|
||||
|
||||
let is_type_of = if *has_interface {
|
||||
None
|
||||
} else {
|
||||
Some(quote!(is_type_of = |_| false,))
|
||||
};
|
||||
|
||||
let prefixes = if POLYFILL_INTERFACES.contains(js_name.as_str()) {
|
||||
Some(quote!(vendor_prefix = webkit,))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let extends = parents
|
||||
.into_iter()
|
||||
.map(|x| quote!( extends = #x, ))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let consts = consts
|
||||
.into_iter()
|
||||
.map(|x| x.generate(options, &name, js_name))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let consts = if consts.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(quote! {
|
||||
#unstable_attr
|
||||
impl #name {
|
||||
#(#deprecated #consts)*
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let attributes = attributes
|
||||
.into_iter()
|
||||
.map(|x| x.generate(options, &name, js_name, &parents))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let methods = methods
|
||||
.into_iter()
|
||||
.map(|x| x.generate(options, &name, js_name.to_string(), &parents))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let js_ident = raw_ident(js_name);
|
||||
|
||||
quote! {
|
||||
#![allow(unused_imports)]
|
||||
use super::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#unstable_attr
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(
|
||||
#is_type_of
|
||||
#prefixes
|
||||
#(#extends)*
|
||||
extends = ::js_sys::Object,
|
||||
js_name = #js_ident,
|
||||
typescript_type = #js_name
|
||||
)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
#deprecated
|
||||
pub type #name;
|
||||
|
||||
#(#deprecated #attributes)*
|
||||
#(#deprecated #methods)*
|
||||
}
|
||||
|
||||
#consts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DictionaryField {
|
||||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub ty: Type,
|
||||
pub required: bool,
|
||||
}
|
||||
|
||||
impl DictionaryField {
|
||||
fn generate_rust(&self, options: &Options, parent_name: String, unstable: bool) -> TokenStream {
|
||||
let DictionaryField {
|
||||
name,
|
||||
js_name,
|
||||
ty,
|
||||
required: _,
|
||||
} = self;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(unstable);
|
||||
let unstable_docs = maybe_unstable_docs(unstable);
|
||||
|
||||
let mut features = BTreeSet::new();
|
||||
|
||||
add_features(&mut features, ty);
|
||||
|
||||
features.remove(&parent_name);
|
||||
|
||||
let cfg_features = get_cfg_features(options, &features);
|
||||
|
||||
features.insert(parent_name);
|
||||
|
||||
let doc_comment = comment(
|
||||
format!("Change the `{}` field of this object.", js_name),
|
||||
&required_doc_string(options, &features),
|
||||
);
|
||||
|
||||
quote! {
|
||||
#unstable_attr
|
||||
#cfg_features
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
pub fn #name(&mut self, val: #ty) -> &mut Self {
|
||||
use wasm_bindgen::JsValue;
|
||||
let r = ::js_sys::Reflect::set(
|
||||
self.as_ref(),
|
||||
&JsValue::from(#js_name),
|
||||
&JsValue::from(val),
|
||||
);
|
||||
debug_assert!(r.is_ok(), "setting properties should never fail on our dictionary objects");
|
||||
let _ = r;
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dictionary {
|
||||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub fields: Vec<DictionaryField>,
|
||||
pub unstable: bool,
|
||||
}
|
||||
|
||||
impl Dictionary {
|
||||
pub fn generate(&self, options: &Options) -> TokenStream {
|
||||
let Dictionary {
|
||||
name,
|
||||
js_name,
|
||||
fields,
|
||||
unstable,
|
||||
} = self;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(*unstable);
|
||||
let unstable_docs = maybe_unstable_docs(*unstable);
|
||||
|
||||
let js_name = raw_ident(js_name);
|
||||
|
||||
let mut required_features = BTreeSet::new();
|
||||
let mut required_args = vec![];
|
||||
let mut required_calls = vec![];
|
||||
|
||||
for field in fields.iter() {
|
||||
if field.required {
|
||||
let name = &field.name;
|
||||
let ty = &field.ty;
|
||||
required_args.push(quote!( #name: #ty ));
|
||||
required_calls.push(quote!( ret.#name(#name); ));
|
||||
add_features(&mut required_features, &field.ty);
|
||||
}
|
||||
}
|
||||
|
||||
required_features.remove(&name.to_string());
|
||||
|
||||
let cfg_features = get_cfg_features(options, &required_features);
|
||||
|
||||
required_features.insert(name.to_string());
|
||||
|
||||
let doc_comment = comment(
|
||||
format!("The `{}` dictionary.", name),
|
||||
&get_features_doc(options, name.to_string()),
|
||||
);
|
||||
let ctor_doc_comment = comment(
|
||||
format!("Construct a new `{}`.", name),
|
||||
&required_doc_string(options, &required_features),
|
||||
);
|
||||
|
||||
let fields = fields
|
||||
.into_iter()
|
||||
.map(|field| field.generate_rust(options, name.to_string(), *unstable))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
#![allow(unused_imports)]
|
||||
use super::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#unstable_attr
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(extends = ::js_sys::Object, js_name = #js_name)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
pub type #name;
|
||||
}
|
||||
|
||||
#unstable_attr
|
||||
impl #name {
|
||||
#cfg_features
|
||||
#ctor_doc_comment
|
||||
#unstable_docs
|
||||
pub fn new(#(#required_args),*) -> Self {
|
||||
#[allow(unused_mut)]
|
||||
let mut ret: Self = ::wasm_bindgen::JsCast::unchecked_into(::js_sys::Object::new());
|
||||
#(#required_calls)*
|
||||
ret
|
||||
}
|
||||
|
||||
#(#fields)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Function {
|
||||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub arguments: Vec<(Ident, Type)>,
|
||||
pub ret_ty: Option<Type>,
|
||||
pub catch: bool,
|
||||
pub variadic: bool,
|
||||
pub unstable: bool,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
fn generate(
|
||||
&self,
|
||||
options: &Options,
|
||||
parent_name: &Ident,
|
||||
parent_js_name: String,
|
||||
) -> TokenStream {
|
||||
let Function {
|
||||
name,
|
||||
js_name,
|
||||
arguments,
|
||||
ret_ty,
|
||||
catch,
|
||||
variadic,
|
||||
unstable,
|
||||
} = self;
|
||||
|
||||
let unstable_attr = maybe_unstable_attr(*unstable);
|
||||
let unstable_docs = maybe_unstable_docs(*unstable);
|
||||
|
||||
let js_namespace = raw_ident(&parent_js_name);
|
||||
|
||||
let doc_comment = format!(
|
||||
"The `{}.{}()` function.\n\n{}",
|
||||
parent_js_name,
|
||||
js_name,
|
||||
mdn_doc(&parent_js_name, Some(&js_name))
|
||||
);
|
||||
|
||||
let mut features = BTreeSet::new();
|
||||
|
||||
for (_, ty) in arguments.iter() {
|
||||
add_features(&mut features, ty);
|
||||
}
|
||||
|
||||
if let Some(ty) = ret_ty {
|
||||
add_features(&mut features, ty);
|
||||
}
|
||||
|
||||
features.remove(&parent_name.to_string());
|
||||
|
||||
let cfg_features = get_cfg_features(options, &features);
|
||||
|
||||
features.insert(parent_name.to_string());
|
||||
|
||||
let doc_comment = comment(doc_comment, &required_doc_string(options, &features));
|
||||
|
||||
let ret = ret_ty.as_ref().map(|ret| quote!( #ret ));
|
||||
|
||||
let ret = if *catch {
|
||||
let ret = ret.unwrap_or_else(|| quote!(()));
|
||||
Some(quote!( Result<#ret, JsValue> ))
|
||||
} else {
|
||||
ret
|
||||
};
|
||||
|
||||
let ret = ret.as_ref().map(|ret| quote!( -> #ret ));
|
||||
|
||||
let catch = if *catch { Some(quote!(catch,)) } else { None };
|
||||
|
||||
let arguments = generate_arguments(arguments, *variadic);
|
||||
let variadic = generate_variadic(*variadic);
|
||||
|
||||
let js_name = raw_ident(js_name);
|
||||
|
||||
quote! {
|
||||
#unstable_attr
|
||||
#cfg_features
|
||||
#[wasm_bindgen(
|
||||
#catch
|
||||
#variadic
|
||||
js_namespace = #js_namespace,
|
||||
js_name = #js_name
|
||||
)]
|
||||
#doc_comment
|
||||
#unstable_docs
|
||||
pub fn #name(#(#arguments),*) #ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Namespace {
|
||||
pub name: Ident,
|
||||
pub js_name: String,
|
||||
pub functions: Vec<Function>,
|
||||
}
|
||||
|
||||
impl Namespace {
|
||||
pub fn generate(&self, options: &Options) -> TokenStream {
|
||||
let Namespace {
|
||||
name,
|
||||
js_name,
|
||||
functions,
|
||||
} = self;
|
||||
|
||||
let functions = functions
|
||||
.into_iter()
|
||||
.map(|x| x.generate(options, &name, js_name.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
quote! {
|
||||
pub mod #name {
|
||||
#![allow(unused_imports)]
|
||||
use super::super::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#(#functions)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -408,6 +408,11 @@ terms_to_idl_type_maybe_immutable! {
|
||||
Uint8ClampedArray => Uint8ClampedArray
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TypeError {
|
||||
CannotConvert,
|
||||
}
|
||||
|
||||
impl<'a> IdlType<'a> {
|
||||
/// Generates a snake case type name.
|
||||
pub(crate) fn push_snake_case_name(&self, dst: &mut String) {
|
||||
@ -490,7 +495,7 @@ impl<'a> IdlType<'a> {
|
||||
}
|
||||
|
||||
/// Converts to syn type if possible.
|
||||
pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Option<syn::Type> {
|
||||
pub(crate) fn to_syn_type(&self, pos: TypePosition) -> Result<Option<syn::Type>, TypeError> {
|
||||
let anyref = |ty| {
|
||||
Some(match pos {
|
||||
TypePosition::Argument => shared_ref(ty, false),
|
||||
@ -507,13 +512,13 @@ impl<'a> IdlType<'a> {
|
||||
anyref(leading_colon_path_ty(path))
|
||||
};
|
||||
match self {
|
||||
IdlType::Boolean => Some(ident_ty(raw_ident("bool"))),
|
||||
IdlType::Byte => Some(ident_ty(raw_ident("i8"))),
|
||||
IdlType::Octet => Some(ident_ty(raw_ident("u8"))),
|
||||
IdlType::Short => Some(ident_ty(raw_ident("i16"))),
|
||||
IdlType::UnsignedShort => Some(ident_ty(raw_ident("u16"))),
|
||||
IdlType::Long => Some(ident_ty(raw_ident("i32"))),
|
||||
IdlType::UnsignedLong => Some(ident_ty(raw_ident("u32"))),
|
||||
IdlType::Boolean => Ok(Some(ident_ty(raw_ident("bool")))),
|
||||
IdlType::Byte => Ok(Some(ident_ty(raw_ident("i8")))),
|
||||
IdlType::Octet => Ok(Some(ident_ty(raw_ident("u8")))),
|
||||
IdlType::Short => Ok(Some(ident_ty(raw_ident("i16")))),
|
||||
IdlType::UnsignedShort => Ok(Some(ident_ty(raw_ident("u16")))),
|
||||
IdlType::Long => Ok(Some(ident_ty(raw_ident("i32")))),
|
||||
IdlType::UnsignedLong => Ok(Some(ident_ty(raw_ident("u32")))),
|
||||
|
||||
// Technically these are 64-bit numbers, but we're binding web
|
||||
// APIs that don't actually have return the corresponding 64-bit
|
||||
@ -527,74 +532,81 @@ impl<'a> IdlType<'a> {
|
||||
//
|
||||
// Perhaps one day we'll bind to u64/i64 here, but we need `BigInt`
|
||||
// to see more usage!
|
||||
IdlType::LongLong | IdlType::UnsignedLongLong => Some(ident_ty(raw_ident("f64"))),
|
||||
IdlType::LongLong | IdlType::UnsignedLongLong => Ok(Some(ident_ty(raw_ident("f64")))),
|
||||
|
||||
IdlType::Float => Some(ident_ty(raw_ident("f32"))),
|
||||
IdlType::UnrestrictedFloat => Some(ident_ty(raw_ident("f32"))),
|
||||
IdlType::Double => Some(ident_ty(raw_ident("f64"))),
|
||||
IdlType::UnrestrictedDouble => Some(ident_ty(raw_ident("f64"))),
|
||||
IdlType::Float => Ok(Some(ident_ty(raw_ident("f32")))),
|
||||
IdlType::UnrestrictedFloat => Ok(Some(ident_ty(raw_ident("f32")))),
|
||||
IdlType::Double => Ok(Some(ident_ty(raw_ident("f64")))),
|
||||
IdlType::UnrestrictedDouble => Ok(Some(ident_ty(raw_ident("f64")))),
|
||||
IdlType::DomString | IdlType::ByteString | IdlType::UsvString => match pos {
|
||||
TypePosition::Argument => Some(shared_ref(ident_ty(raw_ident("str")), false)),
|
||||
TypePosition::Return => Some(ident_ty(raw_ident("String"))),
|
||||
TypePosition::Argument => Ok(Some(shared_ref(ident_ty(raw_ident("str")), false))),
|
||||
TypePosition::Return => Ok(Some(ident_ty(raw_ident("String")))),
|
||||
},
|
||||
IdlType::Object => js_sys("Object"),
|
||||
IdlType::Symbol => None,
|
||||
IdlType::Error => None,
|
||||
IdlType::Object => Ok(js_sys("Object")),
|
||||
IdlType::Symbol => Err(TypeError::CannotConvert),
|
||||
IdlType::Error => Err(TypeError::CannotConvert),
|
||||
|
||||
IdlType::ArrayBuffer => js_sys("ArrayBuffer"),
|
||||
IdlType::DataView => None,
|
||||
IdlType::Int8Array { immutable } => Some(array("i8", pos, *immutable)),
|
||||
IdlType::Uint8Array { immutable } => Some(array("u8", pos, *immutable)),
|
||||
IdlType::Uint8ClampedArray { immutable } => Some(clamped(array("u8", pos, *immutable))),
|
||||
IdlType::Int16Array { immutable } => Some(array("i16", pos, *immutable)),
|
||||
IdlType::Uint16Array { immutable } => Some(array("u16", pos, *immutable)),
|
||||
IdlType::Int32Array { immutable } => Some(array("i32", pos, *immutable)),
|
||||
IdlType::Uint32Array { immutable } => Some(array("u32", pos, *immutable)),
|
||||
IdlType::Float32Array { immutable } => Some(array("f32", pos, *immutable)),
|
||||
IdlType::Float64Array { immutable } => Some(array("f64", pos, *immutable)),
|
||||
IdlType::ArrayBuffer => Ok(js_sys("ArrayBuffer")),
|
||||
IdlType::DataView => Err(TypeError::CannotConvert),
|
||||
IdlType::Int8Array { immutable } => Ok(Some(array("i8", pos, *immutable))),
|
||||
IdlType::Uint8Array { immutable } => Ok(Some(array("u8", pos, *immutable))),
|
||||
IdlType::Uint8ClampedArray { immutable } => {
|
||||
Ok(Some(clamped(array("u8", pos, *immutable))))
|
||||
}
|
||||
IdlType::Int16Array { immutable } => Ok(Some(array("i16", pos, *immutable))),
|
||||
IdlType::Uint16Array { immutable } => Ok(Some(array("u16", pos, *immutable))),
|
||||
IdlType::Int32Array { immutable } => Ok(Some(array("i32", pos, *immutable))),
|
||||
IdlType::Uint32Array { immutable } => Ok(Some(array("u32", pos, *immutable))),
|
||||
IdlType::Float32Array { immutable } => Ok(Some(array("f32", pos, *immutable))),
|
||||
IdlType::Float64Array { immutable } => Ok(Some(array("f64", pos, *immutable))),
|
||||
|
||||
IdlType::ArrayBufferView { .. } | IdlType::BufferSource { .. } => js_sys("Object"),
|
||||
IdlType::ArrayBufferView { .. } | IdlType::BufferSource { .. } => Ok(js_sys("Object")),
|
||||
IdlType::Interface(name)
|
||||
| IdlType::Dictionary(name)
|
||||
| IdlType::CallbackInterface { name, .. } => {
|
||||
let ty = ident_ty(rust_ident(camel_case_ident(name).as_str()));
|
||||
anyref(ty)
|
||||
Ok(anyref(ty))
|
||||
}
|
||||
IdlType::Enum(name) => Some(ident_ty(rust_ident(camel_case_ident(name).as_str()))),
|
||||
IdlType::Enum(name) => Ok(Some(ident_ty(rust_ident(camel_case_ident(name).as_str())))),
|
||||
|
||||
IdlType::Nullable(idl_type) => {
|
||||
let inner = idl_type.to_syn_type(pos)?;
|
||||
|
||||
// TODO: this is a bit of a hack, but `Option<JsValue>` isn't
|
||||
// supported right now. As a result if we see `JsValue` for our
|
||||
// inner type, leave that as the same when we create a nullable
|
||||
// version of that. That way `any?` just becomes `JsValue` and
|
||||
// it's up to users to dispatch and/or create instances
|
||||
// appropriately.
|
||||
if let syn::Type::Path(path) = &inner {
|
||||
if path.qself.is_none()
|
||||
&& path
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map(|p| p.ident == "JsValue")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Some(inner.clone());
|
||||
}
|
||||
}
|
||||
match inner {
|
||||
Some(inner) => {
|
||||
// TODO: this is a bit of a hack, but `Option<JsValue>` isn't
|
||||
// supported right now. As a result if we see `JsValue` for our
|
||||
// inner type, leave that as the same when we create a nullable
|
||||
// version of that. That way `any?` just becomes `JsValue` and
|
||||
// it's up to users to dispatch and/or create instances
|
||||
// appropriately.
|
||||
if let syn::Type::Path(path) = &inner {
|
||||
if path.qself.is_none()
|
||||
&& path
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map(|p| p.ident == "JsValue")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Ok(Some(inner.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Some(option_ty(inner))
|
||||
Ok(Some(option_ty(inner)))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
IdlType::FrozenArray(_idl_type) => None,
|
||||
IdlType::FrozenArray(_idl_type) => Err(TypeError::CannotConvert),
|
||||
// webidl sequences must always be returned as javascript `Array`s. They may accept
|
||||
// anything implementing the @@iterable interface.
|
||||
IdlType::Sequence(_idl_type) => match pos {
|
||||
TypePosition::Argument => js_value,
|
||||
TypePosition::Return => js_sys("Array"),
|
||||
TypePosition::Argument => Ok(js_value),
|
||||
TypePosition::Return => Ok(js_sys("Array")),
|
||||
},
|
||||
IdlType::Promise(_idl_type) => js_sys("Promise"),
|
||||
IdlType::Record(_idl_type_from, _idl_type_to) => None,
|
||||
IdlType::Promise(_idl_type) => Ok(js_sys("Promise")),
|
||||
IdlType::Record(_idl_type_from, _idl_type_to) => Err(TypeError::CannotConvert),
|
||||
IdlType::Union(idl_types) => {
|
||||
// Note that most union types have already been expanded to
|
||||
// their components via `flatten`. Unions in a return position
|
||||
@ -629,10 +641,10 @@ impl<'a> IdlType<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
IdlType::Any => js_value,
|
||||
IdlType::Void => None,
|
||||
IdlType::Callback => js_sys("Function"),
|
||||
IdlType::UnknownInterface(_) => None,
|
||||
IdlType::Any => Ok(js_value),
|
||||
IdlType::Void => Ok(None),
|
||||
IdlType::Callback => Ok(js_sys("Function")),
|
||||
IdlType::UnknownInterface(_) => Err(TypeError::CannotConvert),
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
41
crates/webidl/src/main.rs
Normal file
41
crates/webidl/src/main.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use anyhow::{Context, Result};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
#[structopt(
|
||||
name = "wasm-bindgen-webidl",
|
||||
about = "Converts WebIDL into wasm-bindgen compatible code."
|
||||
)]
|
||||
struct Opt {
|
||||
#[structopt(parse(from_os_str))]
|
||||
input_dir: PathBuf,
|
||||
|
||||
#[structopt(parse(from_os_str))]
|
||||
output_dir: PathBuf,
|
||||
|
||||
#[structopt(long)]
|
||||
no_features: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let opt = Opt::from_args();
|
||||
|
||||
let features = !opt.no_features;
|
||||
|
||||
let generated_features = wasm_bindgen_webidl::generate(
|
||||
&opt.input_dir,
|
||||
&opt.output_dir,
|
||||
wasm_bindgen_webidl::Options { features },
|
||||
)?;
|
||||
|
||||
if features {
|
||||
fs::write(&"features", generated_features)
|
||||
.context("writing features to current directory")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
203
crates/webidl/src/traverse.rs
Normal file
203
crates/webidl/src/traverse.rs
Normal file
@ -0,0 +1,203 @@
|
||||
use syn::{Ident, Type};
|
||||
|
||||
pub trait TraverseType {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident);
|
||||
}
|
||||
|
||||
impl TraverseType for Type {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
match self {
|
||||
Type::Array(x) => x.traverse_type(f),
|
||||
Type::BareFn(x) => x.traverse_type(f),
|
||||
Type::Group(x) => x.traverse_type(f),
|
||||
Type::Paren(x) => x.traverse_type(f),
|
||||
Type::Path(x) => x.traverse_type(f),
|
||||
Type::Ptr(x) => x.traverse_type(f),
|
||||
Type::Reference(x) => x.traverse_type(f),
|
||||
Type::Slice(x) => x.traverse_type(f),
|
||||
Type::Tuple(x) => x.traverse_type(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypeArray {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.elem.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypeBareFn {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
for input in self.inputs.iter() {
|
||||
input.ty.traverse_type(f);
|
||||
}
|
||||
|
||||
self.output.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::ReturnType {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
match self {
|
||||
Self::Default => {}
|
||||
Self::Type(_, ty) => {
|
||||
ty.traverse_type(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypeGroup {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.elem.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypeParen {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.elem.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypePath {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
if let Some(qself) = self.qself.as_ref() {
|
||||
qself.traverse_type(f);
|
||||
}
|
||||
|
||||
if let Some(last) = self.path.segments.last() {
|
||||
f(&last.ident);
|
||||
last.arguments.traverse_type(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::QSelf {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.ty.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::PathArguments {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
match self {
|
||||
Self::None => {}
|
||||
Self::AngleBracketed(x) => x.traverse_type(f),
|
||||
Self::Parenthesized(x) => x.traverse_type(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::AngleBracketedGenericArguments {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
for ty in self.args.iter() {
|
||||
ty.traverse_type(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::GenericArgument {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
match self {
|
||||
Self::Type(x) => x.traverse_type(f),
|
||||
Self::Binding(x) => x.traverse_type(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::Binding {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.ty.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::ParenthesizedGenericArguments {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
for ty in self.inputs.iter() {
|
||||
ty.traverse_type(f);
|
||||
}
|
||||
|
||||
self.output.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypePtr {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.elem.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypeReference {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.elem.traverse_type(f);
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypeTuple {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
for ty in self.elems.iter() {
|
||||
ty.traverse_type(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TraverseType for syn::TypeSlice {
|
||||
fn traverse_type<F>(&self, f: &mut F)
|
||||
where
|
||||
F: FnMut(&Ident),
|
||||
{
|
||||
self.elem.traverse_type(f);
|
||||
}
|
||||
}
|
@ -1,17 +1,21 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::iter::FromIterator;
|
||||
use std::ptr;
|
||||
|
||||
use heck::{CamelCase, ShoutySnakeCase, SnakeCase};
|
||||
use proc_macro2::{Ident, Span};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
use syn;
|
||||
use wasm_bindgen_backend::ast;
|
||||
use wasm_bindgen_backend::util::{ident_ty, leading_colon_path_ty, raw_ident, rust_ident};
|
||||
use wasm_bindgen_backend::util::{ident_ty, raw_ident, rust_ident};
|
||||
use weedle;
|
||||
use weedle::attribute::{ExtendedAttribute, ExtendedAttributeList, IdentifierOrString};
|
||||
use weedle::literal::{ConstValue, FloatLit, IntegerLit};
|
||||
|
||||
use crate::constants::IMMUTABLE_SLICE_WHITELIST;
|
||||
use crate::first_pass::{FirstPassRecord, OperationData, OperationId, Signature};
|
||||
use crate::generator::{InterfaceConstValue, InterfaceMethod, InterfaceMethodKind};
|
||||
use crate::idl_type::{IdlType, ToIdlType};
|
||||
use crate::Options;
|
||||
|
||||
/// For variadic operations an overload with a `js_sys::Array` argument is generated alongside with
|
||||
/// `operation_name_0`, `operation_name_1`, `operation_name_2`, ..., `operation_name_n` overloads
|
||||
@ -81,16 +85,18 @@ pub(crate) fn array(base_ty: &str, pos: TypePosition, immutable: bool) -> syn::T
|
||||
}
|
||||
|
||||
/// Map a webidl const value to the correct wasm-bindgen const value
|
||||
pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> ast::ConstValue {
|
||||
pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> InterfaceConstValue {
|
||||
use std::f64::{INFINITY, NAN, NEG_INFINITY};
|
||||
|
||||
match *v {
|
||||
ConstValue::Boolean(b) => ast::ConstValue::BooleanLiteral(b.0),
|
||||
ConstValue::Float(FloatLit::NegInfinity(_)) => ast::ConstValue::FloatLiteral(NEG_INFINITY),
|
||||
ConstValue::Float(FloatLit::Infinity(_)) => ast::ConstValue::FloatLiteral(INFINITY),
|
||||
ConstValue::Float(FloatLit::NaN(_)) => ast::ConstValue::FloatLiteral(NAN),
|
||||
ConstValue::Boolean(b) => InterfaceConstValue::BooleanLiteral(b.0),
|
||||
ConstValue::Float(FloatLit::NegInfinity(_)) => {
|
||||
InterfaceConstValue::FloatLiteral(NEG_INFINITY)
|
||||
}
|
||||
ConstValue::Float(FloatLit::Infinity(_)) => InterfaceConstValue::FloatLiteral(INFINITY),
|
||||
ConstValue::Float(FloatLit::NaN(_)) => InterfaceConstValue::FloatLiteral(NAN),
|
||||
ConstValue::Float(FloatLit::Value(s)) => {
|
||||
ast::ConstValue::FloatLiteral(s.0.parse().unwrap())
|
||||
InterfaceConstValue::FloatLiteral(s.0.parse().unwrap())
|
||||
}
|
||||
ConstValue::Integer(lit) => {
|
||||
let mklit = |orig_text: &str, base: u32, offset: usize| {
|
||||
@ -100,7 +106,7 @@ pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> ast::ConstValue {
|
||||
(false, orig_text)
|
||||
};
|
||||
if text == "0" {
|
||||
return ast::ConstValue::SignedIntegerLiteral(0);
|
||||
return InterfaceConstValue::SignedIntegerLiteral(0);
|
||||
}
|
||||
let text = &text[offset..];
|
||||
let n = u64::from_str_radix(text, base)
|
||||
@ -111,9 +117,9 @@ pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> ast::ConstValue {
|
||||
} else {
|
||||
n.wrapping_neg() as i64
|
||||
};
|
||||
ast::ConstValue::SignedIntegerLiteral(n)
|
||||
InterfaceConstValue::SignedIntegerLiteral(n)
|
||||
} else {
|
||||
ast::ConstValue::UnsignedIntegerLiteral(n)
|
||||
InterfaceConstValue::UnsignedIntegerLiteral(n)
|
||||
}
|
||||
};
|
||||
match lit {
|
||||
@ -122,55 +128,10 @@ pub fn webidl_const_v_to_backend_const_v(v: &ConstValue) -> ast::ConstValue {
|
||||
IntegerLit::Dec(h) => mklit(h.0, 10, 0),
|
||||
}
|
||||
}
|
||||
ConstValue::Null(_) => ast::ConstValue::Null,
|
||||
ConstValue::Null(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// From `ident` and `Ty`, create `ident: Ty` for use in e.g. `fn(ident: Ty)`.
|
||||
fn simple_fn_arg(ident: Ident, ty: syn::Type) -> syn::PatType {
|
||||
syn::PatType {
|
||||
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
|
||||
attrs: Vec::new(),
|
||||
by_ref: None,
|
||||
ident,
|
||||
mutability: None,
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: Default::default(),
|
||||
ty: Box::new(ty),
|
||||
attrs: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create `()`.
|
||||
fn unit_ty() -> syn::Type {
|
||||
syn::Type::Tuple(syn::TypeTuple {
|
||||
paren_token: Default::default(),
|
||||
elems: syn::punctuated::Punctuated::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// From `T` create `Result<T, wasm_bindgen::JsValue>`.
|
||||
fn result_ty(t: syn::Type) -> syn::Type {
|
||||
let js_value = leading_colon_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]);
|
||||
|
||||
let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
|
||||
colon2_token: None,
|
||||
lt_token: Default::default(),
|
||||
args: FromIterator::from_iter(vec![
|
||||
syn::GenericArgument::Type(t),
|
||||
syn::GenericArgument::Type(js_value),
|
||||
]),
|
||||
gt_token: Default::default(),
|
||||
});
|
||||
|
||||
let ident = raw_ident("Result");
|
||||
let seg = syn::PathSegment { ident, arguments };
|
||||
let path: syn::Path = seg.into();
|
||||
let ty = syn::TypePath { qself: None, path };
|
||||
ty.into()
|
||||
}
|
||||
|
||||
/// From `T` create `[T]`.
|
||||
pub(crate) fn slice_ty(t: syn::Type) -> syn::Type {
|
||||
syn::TypeSlice {
|
||||
@ -220,208 +181,15 @@ pub enum TypePosition {
|
||||
}
|
||||
|
||||
impl<'src> FirstPassRecord<'src> {
|
||||
pub fn create_one_function<'a>(
|
||||
&self,
|
||||
js_name: &str,
|
||||
rust_name: &str,
|
||||
idl_arguments: impl Iterator<Item = (&'a str, &'a IdlType<'src>)>,
|
||||
ret: &IdlType<'src>,
|
||||
kind: ast::ImportFunctionKind,
|
||||
structural: bool,
|
||||
catch: bool,
|
||||
variadic: bool,
|
||||
doc_comment: Option<String>,
|
||||
unstable_api: bool,
|
||||
) -> Option<ast::ImportFunction>
|
||||
where
|
||||
'src: 'a,
|
||||
{
|
||||
// Convert all of the arguments from their IDL type to a `syn` type,
|
||||
// ready to pass to the backend.
|
||||
//
|
||||
// Note that for non-static methods we add a `&self` type placeholder,
|
||||
// but this type isn't actually used so it's just here for show mostly.
|
||||
let mut arguments = if let &ast::ImportFunctionKind::Method {
|
||||
ref ty,
|
||||
kind:
|
||||
ast::MethodKind::Operation(ast::Operation {
|
||||
is_static: false, ..
|
||||
}),
|
||||
..
|
||||
} = &kind
|
||||
{
|
||||
let mut res = Vec::with_capacity(idl_arguments.size_hint().0 + 1);
|
||||
res.push(simple_fn_arg(
|
||||
raw_ident("self_"),
|
||||
shared_ref(ty.clone(), false),
|
||||
));
|
||||
res
|
||||
} else {
|
||||
Vec::with_capacity(idl_arguments.size_hint().0)
|
||||
};
|
||||
let idl_arguments: Vec<_> = idl_arguments.collect();
|
||||
let arguments_count = idl_arguments.len();
|
||||
for (i, (argument_name, idl_type)) in idl_arguments.into_iter().enumerate() {
|
||||
let syn_type = match idl_type.to_syn_type(TypePosition::Argument) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
log::warn!(
|
||||
"Unsupported argument type: {:?} on {:?}",
|
||||
idl_type,
|
||||
rust_name
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let syn_type = if variadic && i == arguments_count - 1 {
|
||||
let path = vec![rust_ident("js_sys"), rust_ident("Array")];
|
||||
shared_ref(leading_colon_path_ty(path), false)
|
||||
} else {
|
||||
syn_type
|
||||
};
|
||||
let argument_name = rust_ident(&argument_name.to_snake_case());
|
||||
arguments.push(simple_fn_arg(argument_name, syn_type));
|
||||
}
|
||||
|
||||
// Convert the return type to a `syn` type, handling the `catch`
|
||||
// attribute here to use a `Result` in Rust.
|
||||
let ret = match ret {
|
||||
IdlType::Void => None,
|
||||
ret @ _ => match ret.to_syn_type(TypePosition::Return) {
|
||||
Some(ret) => Some(ret),
|
||||
None => {
|
||||
log::warn!("Unsupported return type: {:?} on {:?}", ret, rust_name);
|
||||
return None;
|
||||
}
|
||||
},
|
||||
};
|
||||
let js_ret = ret.clone();
|
||||
let ret = if catch {
|
||||
Some(ret.map_or_else(|| result_ty(unit_ty()), result_ty))
|
||||
} else {
|
||||
ret
|
||||
};
|
||||
|
||||
Some(ast::ImportFunction {
|
||||
function: ast::Function {
|
||||
name: js_name.to_string(),
|
||||
name_span: Span::call_site(),
|
||||
renamed_via_js_name: false,
|
||||
arguments,
|
||||
ret: ret.clone(),
|
||||
rust_attrs: vec![],
|
||||
rust_vis: public(),
|
||||
r#async: false,
|
||||
},
|
||||
rust_name: rust_ident(rust_name),
|
||||
js_ret: js_ret.clone(),
|
||||
variadic,
|
||||
catch,
|
||||
structural,
|
||||
assert_no_shim: false,
|
||||
shim: {
|
||||
let ns = match kind {
|
||||
ast::ImportFunctionKind::Normal => "",
|
||||
ast::ImportFunctionKind::Method { ref class, .. } => class,
|
||||
};
|
||||
raw_ident(&format!("__widl_f_{}_{}", rust_name, ns))
|
||||
},
|
||||
kind,
|
||||
doc_comment,
|
||||
unstable_api,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a wasm-bindgen getter method, if possible.
|
||||
pub fn create_getter(
|
||||
&self,
|
||||
name: &str,
|
||||
ty: &weedle::types::Type<'src>,
|
||||
self_name: &str,
|
||||
is_static: bool,
|
||||
attrs: &Option<ExtendedAttributeList>,
|
||||
container_attrs: Option<&ExtendedAttributeList>,
|
||||
unstable_api: bool,
|
||||
) -> Option<ast::ImportFunction> {
|
||||
let kind = ast::OperationKind::Getter(Some(raw_ident(name)));
|
||||
let kind = self.import_function_kind(self_name, is_static, kind);
|
||||
let ret = ty.to_idl_type(self);
|
||||
self.create_one_function(
|
||||
&name,
|
||||
&snake_case_ident(name),
|
||||
None.into_iter(),
|
||||
&ret,
|
||||
kind,
|
||||
is_structural(attrs.as_ref(), container_attrs),
|
||||
throws(attrs),
|
||||
false,
|
||||
Some(format!(
|
||||
"The `{}` getter\n\n{}",
|
||||
name,
|
||||
mdn_doc(self_name, Some(name))
|
||||
)),
|
||||
unstable_api,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a wasm-bindgen setter method, if possible.
|
||||
pub fn create_setter(
|
||||
&self,
|
||||
name: &str,
|
||||
field_ty: &weedle::types::Type<'src>,
|
||||
self_name: &str,
|
||||
is_static: bool,
|
||||
attrs: &Option<ExtendedAttributeList>,
|
||||
container_attrs: Option<&ExtendedAttributeList>,
|
||||
unstable_api: bool,
|
||||
) -> Option<ast::ImportFunction> {
|
||||
let kind = ast::OperationKind::Setter(Some(raw_ident(name)));
|
||||
let kind = self.import_function_kind(self_name, is_static, kind);
|
||||
let field_ty = field_ty.to_idl_type(self);
|
||||
self.create_one_function(
|
||||
&name,
|
||||
&format!("set_{}", name).to_snake_case(),
|
||||
Some((name, &field_ty)).into_iter(),
|
||||
&IdlType::Void,
|
||||
kind,
|
||||
is_structural(attrs.as_ref(), container_attrs),
|
||||
throws(attrs),
|
||||
false,
|
||||
Some(format!(
|
||||
"The `{}` setter\n\n{}",
|
||||
name,
|
||||
mdn_doc(self_name, Some(name))
|
||||
)),
|
||||
unstable_api,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn import_function_kind(
|
||||
&self,
|
||||
self_name: &str,
|
||||
is_static: bool,
|
||||
operation_kind: ast::OperationKind,
|
||||
) -> ast::ImportFunctionKind {
|
||||
let operation = ast::Operation {
|
||||
is_static,
|
||||
kind: operation_kind,
|
||||
};
|
||||
let ty = ident_ty(rust_ident(camel_case_ident(&self_name).as_str()));
|
||||
ast::ImportFunctionKind::Method {
|
||||
class: self_name.to_string(),
|
||||
ty,
|
||||
kind: ast::MethodKind::Operation(operation),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_imports(
|
||||
&self,
|
||||
container_attrs: Option<&ExtendedAttributeList<'src>>,
|
||||
kind: ast::ImportFunctionKind,
|
||||
id: &OperationId<'src>,
|
||||
data: &OperationData<'src>,
|
||||
unstable_api: bool,
|
||||
) -> Vec<ast::ImportFunction> {
|
||||
unstable: bool,
|
||||
) -> Vec<InterfaceMethod> {
|
||||
let is_static = data.is_static;
|
||||
|
||||
// First up, prune all signatures that reference unsupported arguments.
|
||||
// We won't consider these until said arguments are implemented.
|
||||
//
|
||||
@ -499,7 +267,7 @@ impl<'src> FirstPassRecord<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
let (name, force_structural, force_throws) = match id {
|
||||
let (name, kind, force_structural, force_throws) = match id {
|
||||
// Constructors aren't annotated with `[Throws]` extended attributes
|
||||
// (how could they be, since they themselves are extended
|
||||
// attributes?) so we must conservatively assume that they can
|
||||
@ -512,15 +280,29 @@ impl<'src> FirstPassRecord<'src> {
|
||||
// > value of a type corresponding to the interface the
|
||||
// > `[Constructor]` extended attribute appears on, **or throw an
|
||||
// > exception**.
|
||||
OperationId::Constructor(_) => ("new", false, true),
|
||||
OperationId::Operation(Some(s)) => (*s, false, false),
|
||||
OperationId::Constructor => {
|
||||
("new", InterfaceMethodKind::Constructor(None), false, true)
|
||||
}
|
||||
OperationId::NamedConstructor(n) => (
|
||||
"new",
|
||||
InterfaceMethodKind::Constructor(Some(n.0.to_string())),
|
||||
false,
|
||||
true,
|
||||
),
|
||||
OperationId::Operation(Some(s)) => (*s, InterfaceMethodKind::Regular, false, false),
|
||||
OperationId::Operation(None) => {
|
||||
log::warn!("unsupported unnamed operation");
|
||||
return Vec::new();
|
||||
}
|
||||
OperationId::IndexingGetter => ("get", true, false),
|
||||
OperationId::IndexingSetter => ("set", true, false),
|
||||
OperationId::IndexingDeleter => ("delete", true, false),
|
||||
OperationId::IndexingGetter => {
|
||||
("get", InterfaceMethodKind::IndexingGetter, true, false)
|
||||
}
|
||||
OperationId::IndexingSetter => {
|
||||
("set", InterfaceMethodKind::IndexingSetter, true, false)
|
||||
}
|
||||
OperationId::IndexingDeleter => {
|
||||
("delete", InterfaceMethodKind::IndexingDeleter, true, false)
|
||||
}
|
||||
};
|
||||
|
||||
let mut ret = Vec::new();
|
||||
@ -610,51 +392,81 @@ impl<'src> FirstPassRecord<'src> {
|
||||
.last()
|
||||
.map(|arg| arg.variadic)
|
||||
.unwrap_or(false);
|
||||
ret.extend(
|
||||
self.create_one_function(
|
||||
name,
|
||||
&rust_name,
|
||||
signature
|
||||
.args
|
||||
.iter()
|
||||
.zip(&signature.orig.args)
|
||||
.map(|(idl_type, orig_arg)| (orig_arg.name, idl_type)),
|
||||
&ret_ty,
|
||||
kind.clone(),
|
||||
structural,
|
||||
catch,
|
||||
variadic,
|
||||
None,
|
||||
unstable_api,
|
||||
),
|
||||
|
||||
fn idl_arguments<'a>(
|
||||
args: impl Iterator<Item = (String, &'a IdlType<'a>)>,
|
||||
) -> Option<Vec<(Ident, syn::Type)>> {
|
||||
let mut output = vec![];
|
||||
|
||||
for (name, idl_type) in args {
|
||||
let ty = match idl_type.to_syn_type(TypePosition::Argument) {
|
||||
Ok(ty) => ty.unwrap(),
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
output.push((rust_ident(&snake_case_ident(&name[..])), ty));
|
||||
}
|
||||
|
||||
Some(output)
|
||||
}
|
||||
|
||||
let arguments = idl_arguments(
|
||||
signature
|
||||
.args
|
||||
.iter()
|
||||
.zip(&signature.orig.args)
|
||||
.map(|(idl_type, orig_arg)| (orig_arg.name.to_string(), idl_type)),
|
||||
);
|
||||
|
||||
if let Some(arguments) = arguments {
|
||||
if let Ok(ret_ty) = ret_ty.to_syn_type(TypePosition::Return) {
|
||||
ret.push(InterfaceMethod {
|
||||
name: rust_ident(&rust_name),
|
||||
js_name: name.to_string(),
|
||||
arguments,
|
||||
ret_ty,
|
||||
kind: kind.clone(),
|
||||
is_static,
|
||||
structural,
|
||||
catch,
|
||||
variadic,
|
||||
unstable,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if !variadic {
|
||||
continue;
|
||||
}
|
||||
let last_idl_type = &signature.args[signature.args.len() - 1];
|
||||
let last_name = signature.orig.args[signature.args.len() - 1].name;
|
||||
for i in 0..=MAX_VARIADIC_ARGUMENTS_COUNT {
|
||||
ret.extend(
|
||||
self.create_one_function(
|
||||
name,
|
||||
&format!("{}_{}", rust_name, i),
|
||||
signature.args[..signature.args.len() - 1]
|
||||
.iter()
|
||||
.zip(&signature.orig.args)
|
||||
.map(|(idl_type, orig_arg)| (orig_arg.name.to_string(), idl_type))
|
||||
.chain((1..=i).map(|j| (format!("{}_{}", last_name, j), last_idl_type)))
|
||||
.collect::<Vec<_>>()
|
||||
.iter()
|
||||
.map(|(name, idl_type)| (&name[..], idl_type.clone())),
|
||||
&ret_ty,
|
||||
kind.clone(),
|
||||
structural,
|
||||
catch,
|
||||
false,
|
||||
None,
|
||||
unstable_api,
|
||||
),
|
||||
let arguments = idl_arguments(
|
||||
signature.args[..signature.args.len() - 1]
|
||||
.iter()
|
||||
.zip(&signature.orig.args)
|
||||
.map(|(idl_type, orig_arg)| (orig_arg.name.to_string(), idl_type))
|
||||
.chain((1..=i).map(|j| (format!("{}_{}", last_name, j), last_idl_type))),
|
||||
);
|
||||
|
||||
if let Some(arguments) = arguments {
|
||||
if let Ok(ret_ty) = ret_ty.to_syn_type(TypePosition::Return) {
|
||||
ret.push(InterfaceMethod {
|
||||
name: rust_ident(&format!("{}_{}", rust_name, i)),
|
||||
js_name: name.to_string(),
|
||||
arguments,
|
||||
kind: kind.clone(),
|
||||
ret_ty,
|
||||
is_static,
|
||||
structural,
|
||||
catch,
|
||||
variadic: false,
|
||||
unstable,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -676,7 +488,7 @@ impl<'src> FirstPassRecord<'src> {
|
||||
_ => return idl_type,
|
||||
};
|
||||
|
||||
if self.immutable_slice_whitelist.contains(op) {
|
||||
if IMMUTABLE_SLICE_WHITELIST.contains(op) {
|
||||
flag_slices_immutable(&mut idl_type)
|
||||
}
|
||||
|
||||
@ -755,13 +567,6 @@ pub fn throws(attrs: &Option<ExtendedAttributeList>) -> bool {
|
||||
has_named_attribute(attrs.as_ref(), "Throws")
|
||||
}
|
||||
|
||||
/// Create a syn `pub` token
|
||||
pub fn public() -> syn::Visibility {
|
||||
syn::Visibility::Public(syn::VisPublic {
|
||||
pub_token: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
fn flag_slices_immutable(ty: &mut IdlType) {
|
||||
match ty {
|
||||
IdlType::Int8Array { immutable }
|
||||
@ -792,3 +597,39 @@ fn flag_slices_immutable(ty: &mut IdlType) {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn required_doc_string(options: &Options, features: &BTreeSet<String>) -> Option<String> {
|
||||
if !options.features || features.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
let list = features
|
||||
.iter()
|
||||
.map(|ident| format!("`{}`", ident))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
Some(format!(
|
||||
"\n\n*This API requires the following crate features \
|
||||
to be activated: {}*",
|
||||
list,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_cfg_features(options: &Options, features: &BTreeSet<String>) -> Option<syn::Attribute> {
|
||||
let len = features.len();
|
||||
|
||||
if !options.features || len == 0 {
|
||||
None
|
||||
} else {
|
||||
let features = features
|
||||
.into_iter()
|
||||
.map(|feature| quote!( feature = #feature, ))
|
||||
.collect::<TokenStream>();
|
||||
|
||||
// This is technically unneeded but it generates more idiomatic code
|
||||
if len == 1 {
|
||||
Some(syn::parse_quote!( #[cfg(#features)] ))
|
||||
} else {
|
||||
Some(syn::parse_quote!( #[cfg(all(#features))] ))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user