From 0938858aa8b7f5e09384906256c4833077b33a3d Mon Sep 17 00:00:00 2001 From: "R. Andrew Ohana" Date: Thu, 14 Jun 2018 22:48:32 -0700 Subject: [PATCH] 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