mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2025-01-01 08:00:09 +03:00
commit
dbb498174e
@ -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_`");
|
||||||
|
@ -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;
|
||||||
|
@ -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<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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(),
|
||||||
|
Loading…
Reference in New Issue
Block a user