webidl: add support for static methods

This commit is contained in:
R. Andrew Ohana 2018-06-14 19:21:33 -07:00
parent 639ccd53ce
commit fe5cde8636
5 changed files with 186 additions and 88 deletions

View File

@ -49,11 +49,21 @@ pub struct ImportFunction {
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub enum ImportFunctionKind {
Method { class: String, ty: syn::Type },
JsConstructor { class: String, ty: syn::Type },
Method {
class: String,
ty: syn::Type,
kind: MethodKind,
},
Normal,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub enum MethodKind {
Normal,
Constructor,
Static,
}
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
pub struct ImportStatic {
pub vis: syn::Visibility,
@ -398,6 +408,7 @@ impl Program {
ImportFunctionKind::Method {
class: class_name.to_string(),
ty: class.clone(),
kind: MethodKind::Normal,
}
} else if wasm.opts.constructor() {
let class = match wasm.ret {
@ -414,9 +425,10 @@ impl Program {
let class_name = extract_path_ident(class_name)
.expect("first argument of method must be a bare type");
ImportFunctionKind::JsConstructor {
ImportFunctionKind::Method {
class: class_name.to_string(),
ty: class.clone(),
kind: MethodKind::Constructor,
}
} else {
ImportFunctionKind::Normal
@ -426,7 +438,6 @@ impl Program {
let ns = match kind {
ImportFunctionKind::Normal => "n",
ImportFunctionKind::Method { ref class, .. } => class,
ImportFunctionKind::JsConstructor { ref class, .. } => class,
};
format!("__wbg_f_{}_{}_{}", js_name, f.ident, ns)
};
@ -694,12 +705,16 @@ impl ImportFunction {
let mut js_new = false;
let mut class_name = None;
match self.kind {
ImportFunctionKind::Method { ref class, .. } => {
method = true;
class_name = Some(class);
}
ImportFunctionKind::JsConstructor { ref class, .. } => {
js_new = true;
ImportFunctionKind::Method {
ref class,
ref kind,
..
} => {
match kind {
MethodKind::Normal => method = true,
MethodKind::Constructor => js_new = true,
MethodKind::Static => {}
}
class_name = Some(class);
}
ImportFunctionKind::Normal => {}

View File

@ -552,11 +552,12 @@ impl ToTokens for ast::ImportFunction {
let mut class_ty = None;
let mut is_method = false;
match self.kind {
ast::ImportFunctionKind::Method { ref ty, .. } => {
is_method = true;
class_ty = Some(ty);
}
ast::ImportFunctionKind::JsConstructor { ref ty, .. } => {
ast::ImportFunctionKind::Method {
ref ty, ref kind, ..
} => {
if let ast::MethodKind::Normal = kind {
is_method = true;
}
class_ty = Some(ty);
}
ast::ImportFunctionKind::Normal => {}

View File

@ -24,12 +24,14 @@ use std::iter;
use std::path::Path;
use failure::ResultExt;
use heck::CamelCase;
use quote::ToTokens;
mod util;
use util::{create_function, ident_ty, raw_ident, rust_ident, webidl_ty_to_syn_ty, TypePosition};
use util::{
create_basic_method, create_function, ident_ty, raw_ident, rust_ident, webidl_ty_to_syn_ty,
wrap_import_function, TypePosition,
};
/// Either `Ok(t)` or `Err(failure::Error)`.
pub type Result<T> = ::std::result::Result<T, failure::Error>;
@ -184,9 +186,10 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
) -> Result<()> {
let mut add_constructor = |arguments: &[webidl::ast::Argument], class: &str| {
let self_ty = ident_ty(rust_ident(&interface.name));
let kind = backend::ast::ImportFunctionKind::JsConstructor {
let kind = backend::ast::ImportFunctionKind::Method {
class: class.to_string(),
ty: self_ty.clone(),
kind: backend::ast::MethodKind::Constructor,
};
create_function(
"new",
@ -216,7 +219,7 @@ impl<'a> WebidlParse<&'a webidl::ast::NonPartialInterface> for webidl::ast::Exte
webidl::ast::ExtendedAttribute::NoArguments(webidl::ast::Other::Identifier(name))
if name == "Constructor" =>
{
add_constructor(&[] as &[_], &interface.name);
add_constructor(&[], &interface.name);
}
webidl::ast::ExtendedAttribute::NamedArgumentList(
webidl::ast::NamedArgumentListExtendedAttribute {
@ -275,12 +278,11 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute {
impl<'a> WebidlParse<&'a str> for webidl::ast::Operation {
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
match *self {
webidl::ast::Operation::Regular(ref op) => op.webidl_parse(program, self_name),
match self {
webidl::ast::Operation::Regular(op) => op.webidl_parse(program, self_name),
webidl::ast::Operation::Static(op) => op.webidl_parse(program, self_name),
// TODO
webidl::ast::Operation::Special(_)
| webidl::ast::Operation::Static(_)
| webidl::ast::Operation::Stringifier(_) => {
webidl::ast::Operation::Special(_) | webidl::ast::Operation::Stringifier(_) => {
warn!("Unsupported WebIDL operation: {:?}", self);
Ok(())
}
@ -306,6 +308,7 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute {
let kind = backend::ast::ImportFunctionKind::Method {
class: self_name.to_string(),
ty: ident_ty(rust_ident(self_name)),
kind: backend::ast::MethodKind::Normal,
};
create_function(
@ -316,12 +319,7 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute {
vec![backend::ast::BindgenAttr::Getter(Some(raw_ident(
&this.name,
)))],
).map(|function| backend::ast::Import {
module: None,
version: None,
js_namespace: None,
kind: backend::ast::ImportKind::Function(function),
})
).map(wrap_import_function)
}
fn create_setter(
@ -331,22 +329,18 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute {
let kind = backend::ast::ImportFunctionKind::Method {
class: self_name.to_string(),
ty: ident_ty(rust_ident(self_name)),
kind: backend::ast::MethodKind::Normal,
};
create_function(
&format!("set_{}", this.name.to_camel_case()),
&format!("set_{}", this.name),
iter::once((&*this.name, &*this.type_, false)),
kind,
None,
vec![backend::ast::BindgenAttr::Setter(Some(raw_ident(
&this.name,
)))],
).map(|function| backend::ast::Import {
module: None,
version: None,
js_namespace: None,
kind: backend::ast::ImportKind::Function(function),
})
).map(wrap_import_function)
}
create_getter(self, self_name).map(|import| program.imports.push(import));
@ -361,54 +355,29 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute {
impl<'a> WebidlParse<&'a str> for webidl::ast::RegularOperation {
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
let name = match self.name {
None => {
warn!(
"Operations without a name are unsupported. Skipping {:?}",
self
);
return Ok(());
}
Some(ref name) => name,
};
let kind = backend::ast::ImportFunctionKind::Method {
class: self_name.to_string(),
ty: ident_ty(rust_ident(self_name)),
};
let ret = match self.return_type {
webidl::ast::ReturnType::Void => None,
webidl::ast::ReturnType::NonVoid(ref ty) => {
match webidl_ty_to_syn_ty(ty, TypePosition::Return) {
None => {
warn!(
"Operation's return type is not yet supported: {:?}. Skipping bindings for {:?}",
ty, self
);
return Ok(());
}
Some(ty) => Some(ty),
}
}
};
create_function(
&name,
self.arguments
.iter()
.map(|arg| (&*arg.name, &*arg.type_, arg.variadic)),
kind,
ret,
Vec::new(),
).map(|function| {
program.imports.push(backend::ast::Import {
module: None,
version: None,
js_namespace: None,
kind: backend::ast::ImportKind::Function(function),
})
});
create_basic_method(
&self.arguments,
self.name.as_ref(),
&self.return_type,
self_name,
backend::ast::MethodKind::Normal,
).map(wrap_import_function)
.map(|import| program.imports.push(import));
Ok(())
}
}
impl<'a> WebidlParse<&'a str> for webidl::ast::StaticOperation {
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
create_basic_method(
&self.arguments,
self.name.as_ref(),
&self.return_type,
self_name,
backend::ast::MethodKind::Static,
).map(wrap_import_function)
.map(|import| program.imports.push(import));
Ok(())
}

View File

@ -163,7 +163,12 @@ where
{
let estimate = arguments.size_hint();
let len = estimate.1.unwrap_or(estimate.0);
let mut res = if let backend::ast::ImportFunctionKind::Method { ty, .. } = kind {
let mut res = if let backend::ast::ImportFunctionKind::Method {
ty,
kind: backend::ast::MethodKind::Normal,
..
} = kind
{
let mut res = Vec::with_capacity(len + 1);
res.push(simple_fn_arg(raw_ident("self_"), shared_ref(ty.clone())));
res
@ -189,7 +194,7 @@ where
Some(res)
}
pub fn create_function<'a, 'b, I>(
pub fn create_function<'a, I>(
name: &str,
arguments: I,
kind: backend::ast::ImportFunctionKind,
@ -216,7 +221,6 @@ where
let ns = match kind {
backend::ast::ImportFunctionKind::Normal => "",
backend::ast::ImportFunctionKind::Method { ref class, .. } => class,
backend::ast::ImportFunctionKind::JsConstructor { ref class, .. } => class,
};
raw_ident(&format!("__widl_f_{}_{}", rust_name, ns))
@ -239,3 +243,56 @@ where
shim,
})
}
pub fn create_basic_method(
arguments: &[webidl::ast::Argument],
name: Option<&String>,
return_type: &webidl::ast::ReturnType,
self_name: &str,
kind: backend::ast::MethodKind,
) -> Option<backend::ast::ImportFunction> {
let name = match name {
None => {
warn!("Operations without a name are unsupported");
return None;
}
Some(ref name) => name,
};
let kind = backend::ast::ImportFunctionKind::Method {
class: self_name.to_string(),
ty: ident_ty(rust_ident(self_name)),
kind,
};
let ret = match return_type {
webidl::ast::ReturnType::Void => None,
webidl::ast::ReturnType::NonVoid(ty) => match webidl_ty_to_syn_ty(ty, TypePosition::Return)
{
None => {
warn!("Operation's return type is not yet supported: {:?}", ty);
return None;
}
Some(ty) => Some(ty),
},
};
create_function(
&name,
arguments
.iter()
.map(|arg| (&*arg.name, &*arg.type_, arg.variadic)),
kind,
ret,
Vec::new(),
)
}
pub fn wrap_import_function(function: backend::ast::ImportFunction) -> backend::ast::Import {
backend::ast::Import {
module: None,
version: None,
js_namespace: None,
kind: backend::ast::ImportKind::Function(function),
}
}

View File

@ -176,3 +176,59 @@ fn named_constructor() {
)
.test();
}
#[test]
fn static_method() {
project()
.file(
"foo.webidl",
r#"
interface Foo {
[Pure]
static double swap(double value);
};
"#,
)
.file(
"foo.ts",
r#"
export class Foo {
private static value: number = 0;
static swap(value: number): number {
const res = Foo.value;
Foo.value = value;
return res;
}
}
"#,
)
.file(
"src/lib.rs",
r#"
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
pub mod foo;
use foo::Foo;
#[wasm_bindgen]
pub fn test() {
let tmp = Foo::swap(3.14159) == 0.;
assert!(tmp);
let tmp = Foo::swap(2.71828) == 3.14159;
assert!(tmp);
let tmp = Foo::swap(2.71828) != 3.14159;
assert!(tmp);
let tmp = Foo::swap(3.14159) == 2.71828;
assert!(tmp);
let tmp = Foo::swap(3.14159) != 2.71828;
assert!(tmp);
}
"#,
)
.test();
}