From fe5cde86362018277122e05bda84a81bc4c685d9 Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Thu, 14 Jun 2018 19:21:33 -0700 Subject: [PATCH 1/3] webidl: add support for static methods --- crates/backend/src/ast.rs | 35 +++++++---- crates/backend/src/codegen.rs | 11 ++-- crates/webidl/src/lib.rs | 109 ++++++++++++---------------------- crates/webidl/src/util.rs | 63 +++++++++++++++++++- tests/all/webidl/simple.rs | 56 +++++++++++++++++ 5 files changed, 186 insertions(+), 88 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 8d1ac66c..77aebe74 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -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 => {} diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index 96275b82..a397aa4b 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -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 => {} diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 04f409cc..297ed813 100755 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -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 = ::std::result::Result; @@ -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(()) } diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 7d07569e..111e22c5 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -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 { + 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), + } +} diff --git a/tests/all/webidl/simple.rs b/tests/all/webidl/simple.rs index 767f25d6..a14b45e6 100644 --- a/tests/all/webidl/simple.rs +++ b/tests/all/webidl/simple.rs @@ -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(); +} From 0938858aa8b7f5e09384906256c4833077b33a3d Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Thu, 14 Jun 2018 22:48:32 -0700 Subject: [PATCH 2/3] webidl: add support for static attributes --- crates/backend/src/ast.rs | 45 +++++----- crates/cli-support/src/js/mod.rs | 136 ++++++++++++++++--------------- crates/shared/src/lib.rs | 52 ++++++------ crates/webidl/src/lib.rs | 102 ++++++++++------------- crates/webidl/src/util.rs | 54 +++++++++++- tests/all/webidl/simple.rs | 61 +++++++++++++- 6 files changed, 275 insertions(+), 175 deletions(-) diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 77aebe74..740ad183 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -701,24 +701,6 @@ impl ImportFunction { } fn shared(&self) -> shared::ImportFunction { - let mut method = false; - let mut js_new = false; - let mut class_name = None; - match self.kind { - 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 => {} - } let mut getter = None; let mut setter = None; @@ -730,15 +712,34 @@ impl ImportFunction { let s = s.map(|s| s.to_string()); setter = Some(s.unwrap_or_else(|| self.infer_setter_property())); } + + let mut method = None; + match self.kind { + ImportFunctionKind::Method { + ref class, + ref kind, + .. + } => { + let kind = match kind { + MethodKind::Normal => shared::MethodKind::Normal, + MethodKind::Constructor => shared::MethodKind::Constructor, + MethodKind::Static => shared::MethodKind::Static, + }; + method = Some(shared::MethodData { + class: class.clone(), + kind, + getter, + setter, + }); + } + ImportFunctionKind::Normal => {} + } + shared::ImportFunction { shim: self.shim.to_string(), catch: self.function.opts.catch(), method, - js_new, structural: self.function.opts.structural(), - getter, - setter, - class: class_name.cloned(), function: self.function.shared(), } } diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 9b8f1f60..300d2a15 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1605,76 +1605,78 @@ impl<'a, 'b> SubContext<'a, 'b> { { let descriptor = self.cx.describe(&import.shim); - let target = match import.class { - Some(ref class) if import.js_new => { - format!("new {}", self.import_name(info, class)?) - } - Some(ref class) if import.method => { + let target = match &import.method { + Some(shared::MethodData { class, kind, getter, setter }) => { let class = self.import_name(info, class)?; - let target = if let Some(ref g) = import.getter { - if import.structural { - format!("function() {{ - return this.{}; - }}", g) - } else { - self.cx.expose_get_inherited_descriptor(); - format!( - "GetOwnOrInheritedPropertyDescriptor\ - ({}.prototype, '{}').get", - class, - g, - ) - } - } else if let Some(ref s) = import.setter { - if import.structural { - format!("function(y) {{ - this.{} = y; - }}", s) - } else { - self.cx.expose_get_inherited_descriptor(); - format!( - "GetOwnOrInheritedPropertyDescriptor\ - ({}.prototype, '{}').set", - class, - s, - ) - } + if let shared::MethodKind::Constructor = kind { + format!("new {}", class) } else { - if import.structural { - let nargs = descriptor.unwrap_function().arguments.len(); - let mut s = format!("function("); - for i in 0..nargs - 1 { - if i > 0 { - drop(write!(s, ", ")); - } - drop(write!(s, "x{}", i)); - } - s.push_str(") { \nreturn this."); - s.push_str(&import.function.name); - s.push_str("("); - for i in 0..nargs - 1 { - if i > 0 { - drop(write!(s, ", ")); - } - drop(write!(s, "x{}", i)); - } - s.push_str(");\n}"); - s + let is_static = if let shared::MethodKind::Static = kind { + true } else { - format!("{}.prototype.{}", class, import.function.name) - } - }; - self.cx.global(&format!(" - const {}_target = {}; - ", import.shim, target)); - format!("{}_target.call", import.shim) - } - Some(ref class) => { - let class = self.import_name(info, class)?; - self.cx.global(&format!(" - const {}_target = {}.{}; - ", import.shim, class, import.function.name)); - format!("{}_target", import.shim) + false + }; + + let target = if let Some(g) = getter { + if import.structural { + format!("function(y) {{ + return this.{}; + }}", g) + } else { + self.cx.expose_get_inherited_descriptor(); + format!( + "GetOwnOrInheritedPropertyDescriptor\ + ({}{}, '{}').get", + class, + if is_static { "" } else { ".prototype "}, + g, + ) + } + } else if let Some(s) = setter { + if import.structural { + format!("function(y) {{ + this.{} = y; + }}", s) + } else { + self.cx.expose_get_inherited_descriptor(); + format!( + "GetOwnOrInheritedPropertyDescriptor\ + ({}{}, '{}').set", + class, + if is_static { "" } else { ".prototype "}, + s, + ) + } + } else { + if import.structural { + let nargs = descriptor.unwrap_function().arguments.len(); + let mut s = format!("function("); + for i in 0..nargs - 1 { + if i > 0 { + drop(write!(s, ", ")); + } + drop(write!(s, "x{}", i)); + } + s.push_str(") { \nreturn this."); + s.push_str(&import.function.name); + s.push_str("("); + for i in 0..nargs - 1 { + if i > 0 { + drop(write!(s, ", ")); + } + drop(write!(s, "x{}", i)); + } + s.push_str(");\n}"); + s + } else { + format!("{}{}.{}", class, if is_static { "" } else { ".prototype" }, import.function.name) + } + }; + self.cx.global(&format!(" + const {}_target = {}; + ", import.shim, target)); + format!("{}_target{}", import.shim, if is_static { "" } else { ".call" }) + } } None => { let name = self.import_name(info, &import.function.name)?; diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 55a6df15..6d1da854 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -39,13 +39,24 @@ pub enum ImportKind { pub struct ImportFunction { pub shim: String, pub catch: bool, - pub method: bool, - pub js_new: bool, + pub method: Option, pub structural: bool, + pub function: Function, +} + +#[derive(Deserialize, Serialize)] +pub struct MethodData { + pub class: String, + pub kind: MethodKind, pub getter: Option, pub setter: Option, - pub class: Option, - pub function: Function, +} + +#[derive(Deserialize, Serialize)] +pub enum MethodKind { + Normal, + Constructor, + Static, } #[derive(Deserialize, Serialize)] @@ -55,8 +66,7 @@ pub struct ImportStatic { } #[derive(Deserialize, Serialize)] -pub struct ImportType { -} +pub struct ImportType {} #[derive(Deserialize, Serialize)] pub struct Export { @@ -77,7 +87,7 @@ pub struct Enum { #[derive(Deserialize, Serialize)] pub struct EnumVariant { pub name: String, - pub value: u32 + pub value: u32, } #[derive(Deserialize, Serialize)] @@ -101,20 +111,16 @@ pub struct StructField { pub fn new_function(struct_name: &str) -> String { let mut name = format!("__wbg_"); - name.extend(struct_name - .chars() - .flat_map(|s| s.to_lowercase())); + name.extend(struct_name.chars().flat_map(|s| s.to_lowercase())); name.push_str("_new"); - return name + return name; } pub fn free_function(struct_name: &str) -> String { let mut name = format!("__wbg_"); - name.extend(struct_name - .chars() - .flat_map(|s| s.to_lowercase())); + name.extend(struct_name.chars().flat_map(|s| s.to_lowercase())); name.push_str("_free"); - return name + return name; } pub fn free_function_export_name(function_name: &str) -> String { @@ -128,27 +134,23 @@ pub fn struct_function_export_name(struct_: &str, f: &str) -> String { .collect::(); name.push_str("_"); name.push_str(f); - return name + return name; } pub fn struct_field_get(struct_: &str, f: &str) -> String { let mut name = String::from("__wbg_get_"); - name.extend(struct_ - .chars() - .flat_map(|s| s.to_lowercase())); + name.extend(struct_.chars().flat_map(|s| s.to_lowercase())); name.push_str("_"); name.push_str(f); - return name + return name; } pub fn struct_field_set(struct_: &str, f: &str) -> String { let mut name = String::from("__wbg_set_"); - name.extend(struct_ - .chars() - .flat_map(|s| s.to_lowercase())); + name.extend(struct_.chars().flat_map(|s| s.to_lowercase())); name.push_str("_"); name.push_str(f); - return name + return name; } pub fn version() -> String { @@ -158,5 +160,5 @@ pub fn version() -> String { v.push_str(s); v.push_str(")"); } - return v + return v; } diff --git a/crates/webidl/src/lib.rs b/crates/webidl/src/lib.rs index 297ed813..c024cd93 100755 --- a/crates/webidl/src/lib.rs +++ b/crates/webidl/src/lib.rs @@ -29,8 +29,8 @@ use quote::ToTokens; mod util; use util::{ - create_basic_method, create_function, ident_ty, raw_ident, rust_ident, webidl_ty_to_syn_ty, - wrap_import_function, TypePosition, + create_basic_method, create_function, create_getter, create_setter, ident_ty, rust_ident, + webidl_ty_to_syn_ty, wrap_import_function, TypePosition, }; /// Either `Ok(t)` or `Err(failure::Error)`. @@ -265,10 +265,11 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember { impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute { fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { - match *self { - webidl::ast::Attribute::Regular(ref attr) => attr.webidl_parse(program, self_name), + match self { + webidl::ast::Attribute::Regular(attr) => attr.webidl_parse(program, self_name), + webidl::ast::Attribute::Static(attr) => attr.webidl_parse(program, self_name), // TODO - webidl::ast::Attribute::Static(_) | webidl::ast::Attribute::Stringifier(_) => { + webidl::ast::Attribute::Stringifier(_) => { warn!("Unsupported WebIDL attribute: {:?}", self); Ok(()) } @@ -292,61 +293,46 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Operation { impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute { fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { - fn create_getter( - this: &webidl::ast::RegularAttribute, - self_name: &str, - ) -> Option { - let ret = match webidl_ty_to_syn_ty(&this.type_, TypePosition::Return) { - None => { - warn!("Attribute's type does not yet support reading: {:?}. Skipping getter binding for {:?}", - this.type_, this); - return None; - } - Some(ty) => Some(ty), - }; - - 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( - &this.name, - iter::empty(), - kind, - ret, - vec![backend::ast::BindgenAttr::Getter(Some(raw_ident( - &this.name, - )))], - ).map(wrap_import_function) - } - - fn create_setter( - this: &webidl::ast::RegularAttribute, - self_name: &str, - ) -> Option { - 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), - iter::once((&*this.name, &*this.type_, false)), - kind, - None, - vec![backend::ast::BindgenAttr::Setter(Some(raw_ident( - &this.name, - )))], - ).map(wrap_import_function) - } - - create_getter(self, self_name).map(|import| program.imports.push(import)); + create_getter( + &self.name, + &self.type_, + self_name, + backend::ast::MethodKind::Normal, + ).map(wrap_import_function) + .map(|import| program.imports.push(import)); if !self.read_only { - create_setter(self, self_name).map(|import| program.imports.push(import)); + create_setter( + &self.name, + &self.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::StaticAttribute { + fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> { + create_getter( + &self.name, + &self.type_, + self_name, + backend::ast::MethodKind::Static, + ).map(wrap_import_function) + .map(|import| program.imports.push(import)); + + if !self.read_only { + create_setter( + &self.name, + &self.type_, + self_name, + backend::ast::MethodKind::Static, + ).map(wrap_import_function) + .map(|import| program.imports.push(import)); } Ok(()) diff --git a/crates/webidl/src/util.rs b/crates/webidl/src/util.rs index 111e22c5..c8d2eb5a 100644 --- a/crates/webidl/src/util.rs +++ b/crates/webidl/src/util.rs @@ -1,4 +1,4 @@ -use std::iter::FromIterator; +use std::iter::{self, FromIterator}; use backend; use heck::SnakeCase; @@ -30,7 +30,7 @@ pub fn rust_ident(name: &str) -> Ident { // Create an `Ident` without checking to see if it conflicts with a Rust // keyword. -pub fn raw_ident(name: &str) -> Ident { +fn raw_ident(name: &str) -> Ident { Ident::new(name, proc_macro2::Span::call_site()) } @@ -288,6 +288,56 @@ pub fn create_basic_method( ) } +pub fn create_getter( + name: &str, + ty: &webidl::ast::Type, + self_name: &str, + kind: backend::ast::MethodKind, +) -> Option { + let ret = match webidl_ty_to_syn_ty(ty, TypePosition::Return) { + None => { + warn!("Attribute's type does not yet support reading: {:?}", ty); + return None; + } + Some(ty) => Some(ty), + }; + + let kind = backend::ast::ImportFunctionKind::Method { + class: self_name.to_string(), + ty: ident_ty(rust_ident(self_name)), + kind, + }; + + create_function( + name, + iter::empty(), + kind, + ret, + vec![backend::ast::BindgenAttr::Getter(Some(raw_ident(name)))], + ) +} + +pub fn create_setter( + name: &str, + ty: &webidl::ast::Type, + self_name: &str, + kind: backend::ast::MethodKind, +) -> Option { + let kind = backend::ast::ImportFunctionKind::Method { + class: self_name.to_string(), + ty: ident_ty(rust_ident(self_name)), + kind, + }; + + create_function( + &format!("set_{}", name), + iter::once((name, ty, false)), + kind, + None, + vec![backend::ast::BindgenAttr::Setter(Some(raw_ident(name)))], + ) +} + pub fn wrap_import_function(function: backend::ast::ImportFunction) -> backend::ast::Import { backend::ast::Import { module: None, diff --git a/tests/all/webidl/simple.rs b/tests/all/webidl/simple.rs index a14b45e6..48430426 100644 --- a/tests/all/webidl/simple.rs +++ b/tests/all/webidl/simple.rs @@ -184,7 +184,6 @@ fn static_method() { "foo.webidl", r#" interface Foo { - [Pure] static double swap(double value); }; "#, @@ -232,3 +231,63 @@ fn static_method() { ) .test(); } + +#[test] +fn static_property() { + project() + .file( + "foo.webidl", + r#" + interface Foo { + static attribute double value; + }; + "#, + ) + .file( + "foo.ts", + r#" + export class Foo { + private static _value: number = 0; + + static get value(): number { + return Foo._value; + } + + static set value(_value: number) { + Foo._value = _value; + } + } + "#, + ) + .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::value() == 0.; + assert!(tmp); + Foo::set_value(3.14159); + let tmp = Foo::value() == 3.14159; + assert!(tmp); + let tmp = Foo::value() != 2.71828; + assert!(tmp); + Foo::set_value(2.71828); + let tmp = Foo::value() == 2.71828; + assert!(tmp); + let tmp = Foo::value() != 3.14159; + assert!(tmp); + } + "#, + ) + .test(); +} \ No newline at end of file From d123bedc202fdb1f1867d83a17f128cc6f2e206a Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Mon, 18 Jun 2018 13:10:07 -0700 Subject: [PATCH 3/3] change some asserts to assert_(eq|ne)s --- tests/all/webidl/simple.rs | 52 ++++++++++++++------------------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/tests/all/webidl/simple.rs b/tests/all/webidl/simple.rs index 48430426..30aab25e 100644 --- a/tests/all/webidl/simple.rs +++ b/tests/all/webidl/simple.rs @@ -43,6 +43,8 @@ fn method() { pub fn test() { let pi = Foo::new(3.14159); let e = Foo::new(2.71828); + // TODO: figure out why the following doesn't fail + // assert!(!pi.my_cmp(Foo::new(3.14159))); let tmp = pi.my_cmp(Foo::new(3.14159)); assert!(tmp); let tmp =!pi.my_cmp(Foo::new(2.71828)); @@ -104,15 +106,11 @@ fn property() { #[wasm_bindgen] pub fn test() { let x = Foo::new(3.14159); - let tmp = x.value() == 3.14159; - assert!(tmp); - let tmp = x.value() != 2.71828; - assert!(tmp); + assert_eq!(x.value(), 3.14159); + assert_ne!(x.value(), 2.71828); x.set_value(2.71828); - let tmp = x.value() != 3.14159; - assert!(tmp); - let tmp = x.value() == 2.71828; - assert!(tmp); + assert_ne!(x.value(), 3.14159); + assert_eq!(x.value(), 2.71828); } "#, ) @@ -167,10 +165,8 @@ fn named_constructor() { #[wasm_bindgen] pub fn test() { let x = Foo::new(3.14159); - let tmp = x.value() == 3.14159; - assert!(tmp); - let tmp = x.value() != 0.; - assert!(tmp); + assert_eq!(x.value(), 3.14159); + assert_ne!(x.value(), 0.); } "#, ) @@ -216,16 +212,11 @@ fn static_method() { #[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); + assert_eq!(Foo::swap(3.14159), 0.); + assert_eq!(Foo::swap(2.71828), 3.14159); + assert_ne!(Foo::swap(2.71828), 3.14159); + assert_eq!(Foo::swap(3.14159), 2.71828); + assert_ne!(Foo::swap(3.14159), 2.71828); } "#, ) @@ -274,20 +265,15 @@ fn static_property() { #[wasm_bindgen] pub fn test() { - let tmp = Foo::value() == 0.; - assert!(tmp); + assert_eq!(Foo::value(), 0.); Foo::set_value(3.14159); - let tmp = Foo::value() == 3.14159; - assert!(tmp); - let tmp = Foo::value() != 2.71828; - assert!(tmp); + assert_eq!(Foo::value(), 3.14159); + assert_ne!(Foo::value(), 2.71828); Foo::set_value(2.71828); - let tmp = Foo::value() == 2.71828; - assert!(tmp); - let tmp = Foo::value() != 3.14159; - assert!(tmp); + assert_eq!(Foo::value(), 2.71828); + assert_ne!(Foo::value(), 3.14159); } "#, ) .test(); -} \ No newline at end of file +}