From 30936a6b225be6b9700e91f36fd9353e4c80168b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 22 Mar 2018 18:21:41 -0700 Subject: [PATCH] Allow specifying getter/setter properties Should help with style clashes! --- DESIGN.md | 16 +++++++ crates/wasm-bindgen-macro/src/ast.rs | 42 ++++++++++++----- crates/wasm-bindgen-macro/src/literal.rs | 8 ++-- tests/import-class.rs | 57 ++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 16 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 6ccc6510..23c695a6 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -975,6 +975,22 @@ possibilities! accessible as `foo.set_property(2)`. Note that both functions have a `this` argument as they're tagged with `method`. + Finally, you can also pass a string argument to the `getter` and `setter` + properties to configure what property is accessed. When the property is + explicitly specified then there is no restriction on the method name. For + example the below is equivalent to the above: + + ```rust + #[wasm_bindgen] + extern { + type Foo; + #[wasm_bindgen(method, getter = "property")] + fn assorted_method_name(this: &Foo) -> u32; + #[wasm_bindgen(method, setter = "property")] + fn some_other_method_name(this: &Foo, val: u32); + } + ``` + Properties in JS are accessed through `Object.getOwnPropertyDescriptor`. Note that this typically only works for class-like-defined properties which aren't just attached properties on any old object. For accessing any old property on diff --git a/crates/wasm-bindgen-macro/src/ast.rs b/crates/wasm-bindgen-macro/src/ast.rs index 30a19e31..5afdff0e 100644 --- a/crates/wasm-bindgen-macro/src/ast.rs +++ b/crates/wasm-bindgen-macro/src/ast.rs @@ -651,24 +651,26 @@ impl BindgenAttrs { .next() } - pub fn getter(&self) -> bool { + pub fn getter(&self) -> Option> { self.attrs.iter() - .any(|a| { + .filter_map(|a| { match *a { - BindgenAttr::Getter => true, - _ => false, + BindgenAttr::Getter(ref s) => Some(s.clone()), + _ => None, } }) + .next() } - pub fn setter(&self) -> bool { + pub fn setter(&self) -> Option> { self.attrs.iter() - .any(|a| { + .filter_map(|a| { match *a { - BindgenAttr::Setter => true, - _ => false, + BindgenAttr::Setter(ref s) => Some(s.clone()), + _ => None, } }) + .next() } pub fn structural(&self) -> bool { @@ -703,8 +705,8 @@ enum BindgenAttr { Method, JsNamespace(syn::Ident), Module(String), - Getter, - Setter, + Getter(Option), + Setter(Option), Structural, } @@ -716,9 +718,25 @@ impl syn::synom::Synom for BindgenAttr { | call!(term, "method") => { |_| BindgenAttr::Method } | - call!(term, "getter") => { |_| BindgenAttr::Getter } + do_parse!( + call!(term, "getter") >> + val: option!(do_parse!( + punct!(=) >> + s: syn!(syn::LitStr) >> + (s.value()) + )) >> + (val) + )=> { BindgenAttr::Getter } | - call!(term, "setter") => { |_| BindgenAttr::Setter } + do_parse!( + call!(term, "setter") >> + val: option!(do_parse!( + punct!(=) >> + s: syn!(syn::LitStr) >> + (s.value()) + )) >> + (val) + )=> { BindgenAttr::Setter } | call!(term, "structural") => { |_| BindgenAttr::Structural } | diff --git a/crates/wasm-bindgen-macro/src/literal.rs b/crates/wasm-bindgen-macro/src/literal.rs index 432bb062..60067b26 100644 --- a/crates/wasm-bindgen-macro/src/literal.rs +++ b/crates/wasm-bindgen-macro/src/literal.rs @@ -244,11 +244,11 @@ impl Literal for ast::ImportFunction { let mut setter = None; let structural = self.function.opts.structural(); - if self.function.opts.getter() { - getter = Some(self.infer_getter_property()); + if let Some(s) = self.function.opts.getter() { + getter = Some(s.unwrap_or_else(|| self.infer_getter_property())); } - if self.function.opts.setter() { - setter = Some(self.infer_setter_property()); + if let Some(s) = self.function.opts.setter() { + setter = Some(s.unwrap_or_else(|| self.infer_setter_property())); } a.fields(&[ ("kind", &|a| a.str("function")), diff --git a/tests/import-class.rs b/tests/import-class.rs index d76b172b..c9ae934b 100644 --- a/tests/import-class.rs +++ b/tests/import-class.rs @@ -335,3 +335,60 @@ fn properties() { "#) .test(); } + +#[test] +fn rename_setter_getter() { + test_support::project() + .file("src/lib.rs", r#" + #![feature(proc_macro)] + + extern crate wasm_bindgen; + + use wasm_bindgen::prelude::*; + + #[wasm_bindgen(module = "./another")] + extern { + type Foo; + + #[wasm_bindgen(constructor)] + fn new() -> Foo; + + #[wasm_bindgen(getter = "a", method)] + fn test(this: &Foo) -> i32; + + #[wasm_bindgen(setter = "a", method)] + fn another(this: &Foo, a: i32); + } + + #[wasm_bindgen] + pub fn run() { + let a = Foo::new(); + assert_eq!(a.test(), 1); + a.another(2); + assert_eq!(a.test(), 2); + } + "#) + .file("test.ts", r#" + import { run } from "./out"; + + export function test() { + run(); + } + "#) + .file("another.ts", r#" + export class Foo { + constructor(private num: number) { + this.num = 1; + } + + get a() { + return this.num; + } + + set a(val) { + this.num = val; + } + } + "#) + .test(); +}