Merge pull request #547 from derekdreery/extra_docs

Extra docs
This commit is contained in:
Nick Fitzgerald 2018-07-25 12:04:29 -07:00 committed by GitHub
commit dbb498174e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 9 deletions

View File

@ -2,31 +2,54 @@ use proc_macro2::{Ident, Span};
use shared; use shared;
use syn; use syn;
/// An abstract syntax tree representing a rust program. Contains
/// extra information for joining up this rust code with javascript.
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq))]
#[derive(Default)] #[derive(Default)]
pub struct Program { pub struct Program {
/// rust -> js interfaces
pub exports: Vec<Export>, pub exports: Vec<Export>,
/// js -> rust interfaces
pub imports: Vec<Import>, pub imports: Vec<Import>,
/// rust enums
pub enums: Vec<Enum>, pub enums: Vec<Enum>,
/// rust structs
pub structs: Vec<Struct>, pub structs: Vec<Struct>,
/// rust type aliases
pub type_aliases: Vec<TypeAlias>, pub type_aliases: Vec<TypeAlias>,
/// rust consts
pub consts: Vec<Const>, pub consts: Vec<Const>,
} }
/// A rust to js interface. Allows interaction with rust objects/functions
/// from javascript.
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub struct Export { pub struct Export {
/// The javascript class name.
pub class: Option<Ident>, pub class: Option<Ident>,
/// The type of `self` (either `self`, `&self`, or `&mut self`)
pub method_self: Option<MethodSelf>, pub method_self: Option<MethodSelf>,
/// The name of the constructor function (e.g. new).
///
/// This allows javascript to expose an `Object` interface, where calling
/// the constructor maps correctly to rust.
pub constructor: Option<String>, pub constructor: Option<String>,
/// The rust function
pub function: Function, pub function: Function,
/// Comments extracted from the rust source.
pub comments: Vec<String>, pub comments: Vec<String>,
/// The name of the rust function/method on the rust side.
pub rust_name: Ident, pub rust_name: Ident,
} }
/// The 3 types variations of `self`.
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub enum MethodSelf { pub enum MethodSelf {
/// `self`
ByValue, ByValue,
/// `&mut self`
RefMutable, RefMutable,
/// `&self`
RefShared, RefShared,
} }
@ -219,6 +242,8 @@ impl Function {
} }
impl Export { impl Export {
/// Mangles a rust -> javascript export, so that the created Ident will be unique over function
/// name and class name, if the function belongs to a javascript class.
pub(crate) fn rust_symbol(&self) -> Ident { pub(crate) fn rust_symbol(&self) -> Ident {
let mut generated_name = String::from("__wasm_bindgen_generated"); let mut generated_name = String::from("__wasm_bindgen_generated");
if let Some(class) = &self.class { if let Some(class) = &self.class {
@ -230,6 +255,9 @@ impl Export {
Ident::new(&generated_name, Span::call_site()) Ident::new(&generated_name, Span::call_site())
} }
/// This is the name of the shim function that gets exported and takes the raw
/// ABI form of its arguments and converts them back into their normal,
/// "high level" form before calling the actual function.
pub(crate) fn export_name(&self) -> String { pub(crate) fn export_name(&self) -> String {
let fn_name = self.function.name.to_string(); let fn_name = self.function.name.to_string();
match &self.class { match &self.class {
@ -310,6 +338,7 @@ impl Import {
} }
impl ImportKind { impl ImportKind {
/// Whether this type can be inside an `impl` block.
pub fn fits_on_impl(&self) -> bool { pub fn fits_on_impl(&self) -> bool {
match *self { match *self {
ImportKind::Function(_) => true, ImportKind::Function(_) => true,
@ -330,10 +359,14 @@ impl ImportKind {
} }
impl ImportFunction { impl ImportFunction {
/// If the rust object has a `fn xxx(&self) -> MyType` method, get the name for a getter in
/// javascript (in this case `xxx`, so you can write `val = obj.xxx`)
fn infer_getter_property(&self) -> String { fn infer_getter_property(&self) -> String {
self.function.name.to_string() self.function.name.to_string()
} }
/// If the rust object has a `fn set_xxx(&mut self, MyType)` style method, get the name
/// for a setter in javascript (in this case `xxx`, so you can write `obj.xxx = val`)
fn infer_setter_property(&self) -> String { fn infer_setter_property(&self) -> String {
let name = self.function.name.to_string(); let name = self.function.name.to_string();
assert!(name.starts_with("set_"), "setters must start with `set_`"); assert!(name.starts_with("set_"), "setters must start with `set_`");

View File

@ -5,13 +5,16 @@ use quote::ToTokens;
use shared; use shared;
use syn; use syn;
/// Parsed attributes from a `#[wasm_bindgen(..)]`.
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
#[derive(Default)] #[derive(Default)]
pub struct BindgenAttrs { pub struct BindgenAttrs {
/// List of parsed attributes
pub attrs: Vec<BindgenAttr>, pub attrs: Vec<BindgenAttr>,
} }
impl BindgenAttrs { impl BindgenAttrs {
/// Find and parse the wasm_bindgen attributes.
fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs { fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs {
let pos = attrs let pos = attrs
.iter() .iter()
@ -34,6 +37,7 @@ impl BindgenAttrs {
syn::parse(tt.into()).expect("malformed #[wasm_bindgen] attribute") syn::parse(tt.into()).expect("malformed #[wasm_bindgen] attribute")
} }
/// Get the first module attribute
fn module(&self) -> Option<&str> { fn module(&self) -> Option<&str> {
self.attrs self.attrs
.iter() .iter()
@ -44,6 +48,7 @@ impl BindgenAttrs {
.next() .next()
} }
/// Get the first version attribute
fn version(&self) -> Option<&str> { fn version(&self) -> Option<&str> {
self.attrs self.attrs
.iter() .iter()
@ -54,6 +59,7 @@ impl BindgenAttrs {
.next() .next()
} }
/// Whether the catch attribute is present
fn catch(&self) -> bool { fn catch(&self) -> bool {
self.attrs.iter().any(|a| match a { self.attrs.iter().any(|a| match a {
BindgenAttr::Catch => true, BindgenAttr::Catch => true,
@ -61,6 +67,7 @@ impl BindgenAttrs {
}) })
} }
/// Whether the constructor attribute is present
fn constructor(&self) -> bool { fn constructor(&self) -> bool {
self.attrs.iter().any(|a| match a { self.attrs.iter().any(|a| match a {
BindgenAttr::Constructor => true, BindgenAttr::Constructor => true,
@ -68,6 +75,7 @@ impl BindgenAttrs {
}) })
} }
/// Get the first static_method_of attribute
fn static_method_of(&self) -> Option<&Ident> { fn static_method_of(&self) -> Option<&Ident> {
self.attrs self.attrs
.iter() .iter()
@ -78,6 +86,7 @@ impl BindgenAttrs {
.next() .next()
} }
/// Whether the method attributes is present
fn method(&self) -> bool { fn method(&self) -> bool {
self.attrs.iter().any(|a| match a { self.attrs.iter().any(|a| match a {
BindgenAttr::Method => true, BindgenAttr::Method => true,
@ -85,6 +94,7 @@ impl BindgenAttrs {
}) })
} }
/// Get the first js_namespace attribute
fn js_namespace(&self) -> Option<&Ident> { fn js_namespace(&self) -> Option<&Ident> {
self.attrs self.attrs
.iter() .iter()
@ -95,6 +105,7 @@ impl BindgenAttrs {
.next() .next()
} }
/// Get the first getter attribute
fn getter(&self) -> Option<Option<Ident>> { fn getter(&self) -> Option<Option<Ident>> {
self.attrs self.attrs
.iter() .iter()
@ -105,6 +116,7 @@ impl BindgenAttrs {
.next() .next()
} }
/// Get the first setter attribute
fn setter(&self) -> Option<Option<Ident>> { fn setter(&self) -> Option<Option<Ident>> {
self.attrs self.attrs
.iter() .iter()
@ -115,6 +127,7 @@ impl BindgenAttrs {
.next() .next()
} }
/// Whether the structural attributes is present
fn structural(&self) -> bool { fn structural(&self) -> bool {
self.attrs.iter().any(|a| match *a { self.attrs.iter().any(|a| match *a {
BindgenAttr::Structural => true, BindgenAttr::Structural => true,
@ -122,6 +135,7 @@ impl BindgenAttrs {
}) })
} }
/// Whether the readonly attributes is present
fn readonly(&self) -> bool { fn readonly(&self) -> bool {
self.attrs.iter().any(|a| match *a { self.attrs.iter().any(|a| match *a {
BindgenAttr::Readonly => true, BindgenAttr::Readonly => true,
@ -129,6 +143,7 @@ impl BindgenAttrs {
}) })
} }
/// Get the first js_name attribute
fn js_name(&self) -> Option<&Ident> { fn js_name(&self) -> Option<&Ident> {
self.attrs self.attrs
.iter() .iter()
@ -139,6 +154,7 @@ impl BindgenAttrs {
.next() .next()
} }
/// Get the first js_name attribute
fn js_class(&self) -> Option<&str> { fn js_class(&self) -> Option<&str> {
self.attrs self.attrs
.iter() .iter()
@ -165,6 +181,7 @@ impl syn::synom::Synom for BindgenAttrs {
)); ));
} }
/// The possible attributes in the `#[wasm_bindgen]`.
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))] #[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub enum BindgenAttr { pub enum BindgenAttr {
Catch, Catch,
@ -258,6 +275,7 @@ impl syn::synom::Synom for BindgenAttr {
)); ));
} }
/// Consumes a `Ident` with the given name
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> { fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> {
if let Some((ident, next)) = cursor.ident() { if let Some((ident, next)) = cursor.ident() {
if ident == name { if ident == name {
@ -267,6 +285,7 @@ fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<
syn::parse_error() syn::parse_error()
} }
/// Consumes a `Ident` and returns it.
fn term2ident<'a>(cursor: syn::buffer::Cursor<'a>) -> syn::synom::PResult<'a, Ident> { fn term2ident<'a>(cursor: syn::buffer::Cursor<'a>) -> syn::synom::PResult<'a, Ident> {
match cursor.ident() { match cursor.ident() {
Some(pair) => Ok(pair), Some(pair) => Ok(pair),
@ -274,8 +293,16 @@ fn term2ident<'a>(cursor: syn::buffer::Cursor<'a>) -> syn::synom::PResult<'a, Id
} }
} }
/// Conversion trait with context.
///
/// Used to convert syn tokens into an AST, that we can then use to generate glue code. The context
/// (`Ctx`) is used to pass in the attributes from the `#[wasm_bindgen]`, if needed.
trait ConvertToAst<Ctx> { trait ConvertToAst<Ctx> {
/// What we are converting to.
type Target; type Target;
/// Convert into our target.
///
/// Since this is used in a procedural macro, use panic to fail.
fn convert(self, context: Ctx) -> Self::Target; fn convert(self, context: Ctx) -> Self::Target;
} }
@ -497,6 +524,7 @@ impl ConvertToAst<BindgenAttrs> for syn::ItemFn {
} }
} }
/// Construct a function (and gets the self type if appropriate) for our AST from a syn function.
fn function_from_decl( fn function_from_decl(
name: &Ident, name: &Ident,
mut decl: Box<syn::FnDecl>, mut decl: Box<syn::FnDecl>,
@ -556,6 +584,10 @@ fn function_from_decl(
} }
pub(crate) trait MacroParse<Ctx> { pub(crate) trait MacroParse<Ctx> {
/// Parse the contents of an object into our AST, with a context if necessary.
///
/// The context is used to have access to the attributes on `#[wasm_bindgen]`, and to allow
/// writing to the output `TokenStream`.
fn macro_parse(self, program: &mut ast::Program, context: Ctx); fn macro_parse(self, program: &mut ast::Program, context: Ctx);
} }
@ -785,34 +817,36 @@ impl MacroParse<BindgenAttrs> for syn::ItemForeignMod {
} }
} }
fn extract_first_ty_param(ty: Option<&syn::Type>) -> Option<Option<syn::Type>> { /// Get the first type parameter of a generic type, errors on incorrect input.
fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result<Option<syn::Type>, ()> {
let t = match ty { let t = match ty {
Some(t) => t, Some(t) => t,
None => return Some(None), None => return Ok(None),
}; };
let path = match *t { let path = match *t {
syn::Type::Path(syn::TypePath { syn::Type::Path(syn::TypePath {
qself: None, qself: None,
ref path, ref path,
}) => path, }) => path,
_ => return None, _ => return Err(()),
}; };
let seg = path.segments.last()?.into_value(); let seg = path.segments.last().ok_or(())?.into_value();
let generics = match seg.arguments { let generics = match seg.arguments {
syn::PathArguments::AngleBracketed(ref t) => t, syn::PathArguments::AngleBracketed(ref t) => t,
_ => return None, _ => return Err(()),
}; };
let ty = match *generics.args.first()?.into_value() { let ty = match *generics.args.first().ok_or(())?.into_value() {
syn::GenericArgument::Type(ref t) => t, syn::GenericArgument::Type(ref t) => t,
_ => return None, _ => return Err(()),
}; };
match *ty { match *ty {
syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Some(None), syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Ok(None),
_ => {} _ => {}
} }
Some(Some(ty.clone())) Ok(Some(ty.clone()))
} }
/// Replace `Self` with the given name in `item`.
fn replace_self(name: &Ident, item: &mut syn::ImplItem) { fn replace_self(name: &Ident, item: &mut syn::ImplItem) {
struct Walk<'a>(&'a Ident); struct Walk<'a>(&'a Ident);
@ -854,6 +888,7 @@ fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
.fold(vec![], |mut acc, a| {acc.extend(a); acc}) .fold(vec![], |mut acc, a| {acc.extend(a); acc})
} }
/// Check there are no lifetimes on the function.
fn assert_no_lifetimes(decl: &mut syn::FnDecl) { fn assert_no_lifetimes(decl: &mut syn::FnDecl) {
struct Walk; struct Walk;
@ -869,6 +904,7 @@ fn assert_no_lifetimes(decl: &mut syn::FnDecl) {
syn::visit_mut::VisitMut::visit_fn_decl_mut(&mut Walk, decl); syn::visit_mut::VisitMut::visit_fn_decl_mut(&mut Walk, decl);
} }
/// If the path is a single ident, return it.
fn extract_path_ident(path: &syn::Path) -> Option<Ident> { fn extract_path_ident(path: &syn::Path) -> Option<Ident> {
if path.leading_colon.is_some() { if path.leading_colon.is_some() {
return None; return None;

View File

@ -1,3 +1,12 @@
//! Because some WebIDL constructs are defined in multiple places
//! (keyword `partial` is used to add to an existing construct),
//! We need to first walk the webidl to collect all non-partial
//! constructs so that we have containers in which to put the
//! partial ones.
//!
//! Only `interface`s, `dictionary`s, `enum`s and `mixin`s can
//! be partial.
use std::{ use std::{
collections::{BTreeMap, BTreeSet}, mem, collections::{BTreeMap, BTreeSet}, mem,
}; };
@ -6,21 +15,30 @@ use webidl;
use super::Result; use super::Result;
/// Collection of constructs that may use partial.
#[derive(Default)] #[derive(Default)]
pub(crate) struct FirstPassRecord<'a> { pub(crate) struct FirstPassRecord<'a> {
pub(crate) interfaces: BTreeSet<String>, pub(crate) interfaces: BTreeSet<String>,
pub(crate) dictionaries: BTreeSet<String>, pub(crate) dictionaries: BTreeSet<String>,
pub(crate) enums: BTreeSet<String>, pub(crate) enums: BTreeSet<String>,
/// The mixins, mapping their name to the webidl ast node for the mixin.
pub(crate) mixins: BTreeMap<String, MixinData<'a>>, pub(crate) mixins: BTreeMap<String, MixinData<'a>>,
} }
/// We need to collect mixin data during the first pass, to be used later.
#[derive(Default)] #[derive(Default)]
pub(crate) struct MixinData<'a> { pub(crate) struct MixinData<'a> {
/// The non partial mixin, if present. If there is more than one, we are
/// parsing is a malformed WebIDL file, but the parser will recover by
/// using the last parsed mixin.
pub(crate) non_partial: Option<&'a webidl::ast::NonPartialMixin>, pub(crate) non_partial: Option<&'a webidl::ast::NonPartialMixin>,
/// 0 or more partial mixins.
pub(crate) partials: Vec<&'a webidl::ast::PartialMixin>, pub(crate) partials: Vec<&'a webidl::ast::PartialMixin>,
} }
/// Implemented on an AST node to populate the `FirstPassRecord` struct.
pub(crate) trait FirstPass { pub(crate) trait FirstPass {
/// Populate `record` with any constructs in `self`.
fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()>; fn first_pass<'a>(&'a self, record: &mut FirstPassRecord<'a>) -> Result<()>;
} }

View File

@ -78,6 +78,7 @@ pub fn compile(webidl_source: &str) -> Result<String> {
Ok(compile_ast(ast)) Ok(compile_ast(ast))
} }
/// Run codegen on the AST to generate rust code.
fn compile_ast(mut ast: backend::ast::Program) -> String { fn compile_ast(mut ast: backend::ast::Program) -> String {
let mut defined = BTreeSet::from_iter( let mut defined = BTreeSet::from_iter(
vec![ vec![
@ -96,7 +97,9 @@ fn compile_ast(mut ast: backend::ast::Program) -> String {
tokens.to_string() tokens.to_string()
} }
/// The main trait for parsing WebIDL AST into wasm-bindgen AST.
trait WebidlParse<Ctx> { trait WebidlParse<Ctx> {
/// Parse `self` into wasm-bindgen AST, and insert it into `program`.
fn webidl_parse( fn webidl_parse(
&self, &self,
program: &mut backend::ast::Program, program: &mut backend::ast::Program,

View File

@ -10,6 +10,7 @@ use webidl::ast::ExtendedAttribute;
use first_pass::FirstPassRecord; use first_pass::FirstPassRecord;
/// Take a type and create an immutable shared reference to that type.
fn shared_ref(ty: syn::Type) -> syn::Type { fn shared_ref(ty: syn::Type) -> syn::Type {
syn::TypeReference { syn::TypeReference {
and_token: Default::default(), and_token: Default::default(),
@ -19,6 +20,7 @@ fn shared_ref(ty: syn::Type) -> syn::Type {
}.into() }.into()
} }
/// For a webidl const type node, get the corresponding syn type node.
pub fn webidl_const_ty_to_syn_ty(ty: &webidl::ast::ConstType) -> syn::Type { pub fn webidl_const_ty_to_syn_ty(ty: &webidl::ast::ConstType) -> syn::Type {
use webidl::ast::ConstType::*; use webidl::ast::ConstType::*;
@ -39,6 +41,7 @@ pub fn webidl_const_ty_to_syn_ty(ty: &webidl::ast::ConstType) -> syn::Type {
} }
} }
/// Map a webidl const value to the correct wasm-bindgen const value
pub fn webidl_const_v_to_backend_const_v(v: &webidl::ast::ConstValue) -> backend::ast::ConstValue { pub fn webidl_const_v_to_backend_const_v(v: &webidl::ast::ConstValue) -> backend::ast::ConstValue {
match *v { match *v {
webidl::ast::ConstValue::BooleanLiteral(b) => backend::ast::ConstValue::BooleanLiteral(b), webidl::ast::ConstValue::BooleanLiteral(b) => backend::ast::ConstValue::BooleanLiteral(b),
@ -49,6 +52,7 @@ pub fn webidl_const_v_to_backend_const_v(v: &webidl::ast::ConstValue) -> backend
} }
} }
/// 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::ArgCaptured { fn simple_fn_arg(ident: Ident, ty: syn::Type) -> syn::ArgCaptured {
syn::ArgCaptured { syn::ArgCaptured {
pat: syn::Pat::Ident(syn::PatIdent { pat: syn::Pat::Ident(syn::PatIdent {
@ -62,6 +66,7 @@ fn simple_fn_arg(ident: Ident, ty: syn::Type) -> syn::ArgCaptured {
} }
} }
/// Create `()`.
fn unit_ty() -> syn::Type { fn unit_ty() -> syn::Type {
syn::Type::Tuple(syn::TypeTuple { syn::Type::Tuple(syn::TypeTuple {
paren_token: Default::default(), paren_token: Default::default(),
@ -69,6 +74,7 @@ fn unit_ty() -> syn::Type {
}) })
} }
/// From `T` create `Result<T, ::wasm_bindgen::JsValue>`.
fn result_ty(t: syn::Type) -> syn::Type { fn result_ty(t: syn::Type) -> syn::Type {
let js_value = leading_colon_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]); let js_value = leading_colon_path_ty(vec![rust_ident("wasm_bindgen"), rust_ident("JsValue")]);
@ -89,6 +95,7 @@ fn result_ty(t: syn::Type) -> syn::Type {
ty.into() ty.into()
} }
/// From `T` create `[T]`.
fn slice_ty(t: syn::Type) -> syn::Type { fn slice_ty(t: syn::Type) -> syn::Type {
syn::TypeSlice { syn::TypeSlice {
bracket_token: Default::default(), bracket_token: Default::default(),
@ -96,6 +103,7 @@ fn slice_ty(t: syn::Type) -> syn::Type {
}.into() }.into()
} }
/// From `T` create `Vec<T>`.
fn vec_ty(t: syn::Type) -> syn::Type { fn vec_ty(t: syn::Type) -> syn::Type {
let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
colon2_token: None, colon2_token: None,
@ -113,6 +121,7 @@ fn vec_ty(t: syn::Type) -> syn::Type {
ty.into() ty.into()
} }
/// Possible positions for a type in a function signature.
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TypePosition { pub enum TypePosition {
Argument, Argument,
@ -120,11 +129,14 @@ pub enum TypePosition {
} }
impl<'a> FirstPassRecord<'a> { impl<'a> FirstPassRecord<'a> {
/// Use information from the first pass to work out the correct Rust type to use for
/// a given WebIDL type.
pub fn webidl_ty_to_syn_ty( pub fn webidl_ty_to_syn_ty(
&self, &self,
ty: &webidl::ast::Type, ty: &webidl::ast::Type,
pos: TypePosition, pos: TypePosition,
) -> Option<syn::Type> { ) -> Option<syn::Type> {
// Array type is borrowed for arguments (`&[T]`) and owned for return value (`Vec<T>`).
let array = |base_ty: &str| { let array = |base_ty: &str| {
match pos { match pos {
TypePosition::Argument => { TypePosition::Argument => {
@ -220,6 +232,8 @@ impl<'a> FirstPassRecord<'a> {
return None; return None;
} }
}; };
// Map nullable to an option.
if ty.nullable { if ty.nullable {
let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { let arguments = syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
colon2_token: None, colon2_token: None,
@ -240,6 +254,10 @@ impl<'a> FirstPassRecord<'a> {
} }
} }
/// Use the first pass to convert webidl function arguments to rust arguments.
///
/// `kind` is whether the function is a method, in which case we would need a `self`
/// parameter.
fn webidl_arguments_to_syn_arg_captured<'b, I>( fn webidl_arguments_to_syn_arg_captured<'b, I>(
&self, &self,
arguments: I, arguments: I,
@ -284,6 +302,7 @@ impl<'a> FirstPassRecord<'a> {
Some(res) Some(res)
} }
/// Create a wasm-bindgen function, if possible.
pub fn create_function<'b, I>( pub fn create_function<'b, I>(
&self, &self,
name: &str, name: &str,
@ -333,6 +352,7 @@ impl<'a> FirstPassRecord<'a> {
}) })
} }
/// Create a wasm-bindgen method, if possible.
pub fn create_basic_method( pub fn create_basic_method(
&self, &self,
arguments: &[webidl::ast::Argument], arguments: &[webidl::ast::Argument],
@ -384,6 +404,7 @@ impl<'a> FirstPassRecord<'a> {
) )
} }
/// Create a wasm-bindgen getter method, if possible.
pub fn create_getter( pub fn create_getter(
&self, &self,
name: &str, name: &str,
@ -413,6 +434,7 @@ impl<'a> FirstPassRecord<'a> {
self.create_function(name, iter::empty(), ret, kind, is_structural, catch) self.create_function(name, iter::empty(), ret, kind, is_structural, catch)
} }
/// Create a wasm-bindgen setter method, if possible.
pub fn create_setter( pub fn create_setter(
&self, &self,
name: &str, name: &str,
@ -442,6 +464,7 @@ impl<'a> FirstPassRecord<'a> {
} }
} }
/// Search for an attribute by name in some webidl object's attributes.
fn has_named_attribute(ext_attrs: &[Box<ExtendedAttribute>], attribute: &str) -> bool { fn has_named_attribute(ext_attrs: &[Box<ExtendedAttribute>], attribute: &str) -> bool {
ext_attrs.iter().any(|attr| match &**attr { ext_attrs.iter().any(|attr| match &**attr {
ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => {
@ -456,10 +479,12 @@ pub fn is_chrome_only(ext_attrs: &[Box<ExtendedAttribute>]) -> bool {
has_named_attribute(ext_attrs, "ChromeOnly") has_named_attribute(ext_attrs, "ChromeOnly")
} }
/// Whether a webidl object is marked as a no interface object.
pub fn is_no_interface_object(ext_attrs: &[Box<ExtendedAttribute>]) -> bool { pub fn is_no_interface_object(ext_attrs: &[Box<ExtendedAttribute>]) -> bool {
has_named_attribute(ext_attrs, "NoInterfaceObject") has_named_attribute(ext_attrs, "NoInterfaceObject")
} }
/// Whether a webidl object is marked as structural.
pub fn is_structural(attrs: &[Box<ExtendedAttribute>]) -> bool { pub fn is_structural(attrs: &[Box<ExtendedAttribute>]) -> bool {
attrs.iter().any(|attr| match &**attr { attrs.iter().any(|attr| match &**attr {
ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => { ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => {
@ -469,6 +494,7 @@ pub fn is_structural(attrs: &[Box<ExtendedAttribute>]) -> bool {
}) })
} }
/// Whether a webidl object is marked as throwing.
pub fn throws(attrs: &[Box<ExtendedAttribute>]) -> bool { pub fn throws(attrs: &[Box<ExtendedAttribute>]) -> bool {
attrs.iter().any(|attr| match &**attr { attrs.iter().any(|attr| match &**attr {
ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => name == "Throws", ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name)) => name == "Throws",
@ -476,6 +502,7 @@ pub fn throws(attrs: &[Box<ExtendedAttribute>]) -> bool {
}) })
} }
/// Create a syn `pub` token
pub fn public() -> syn::Visibility { pub fn public() -> syn::Visibility {
syn::Visibility::Public(syn::VisPublic { syn::Visibility::Public(syn::VisPublic {
pub_token: Default::default(), pub_token: Default::default(),