Implement getter/setter bindings

This commit is contained in:
Alex Crichton 2018-02-14 13:16:02 -08:00
parent e72f9e176f
commit 9183236522
6 changed files with 183 additions and 10 deletions

View File

@ -669,12 +669,20 @@ extern {
#[wasm_bindgen(method)]
fn set(this: &Bar, val: i32);
#[wasm_bindgen(method, getter)]
fn property(this: &Bar) -> i32;
#[wasm_bindgen(method, setter)]
fn set_property(this: &Bar, val: i32);
}
fn run() {
let bar = Bar::new(Bar::another_function());
let x = bar.get();
bar.set(x + 3);
bar.set_property(bar.property() + 6);
}
```
@ -726,6 +734,16 @@ const set_shim = Bar.prototype.set;
export function __wbg_s_Bar_set(ptr, arg0) {
set_shim.call(getObject(ptr), arg0)
}
const property_shim = Object.getOwnPropertyDescriptor(Bar.prototype, 'property').get;
export function __wbg_s_Bar_property(ptr) {
return property_shim.call(getObject(ptr));
}
const set_property_shim = Object.getOwnPropertyDescriptor(Bar.prototype, 'property').set;
export function __wbg_s_Bar_set_property(ptr, arg0) {
set_property_shim.call(getObject(ptr), arg0)
}
```
Like when importing functions from JS we can see a bunch of shims are generated
@ -787,6 +805,27 @@ impl Bar {
__wbg_s_Bar_set(ptr, val);
}
}
fn property(&self) -> i32 {
extern {
fn __wbg_s_Bar_property(ptr: u32) -> i32;
}
unsafe {
let ptr = self.obj.__get_idx();
let ret = __wbg_s_Bar_property(ptr);
return ret
}
}
fn set_property(&self, val: i32) {
extern {
fn __wbg_s_Bar_set_property(ptr: u32, val: i32);
}
unsafe {
let ptr = self.obj.__get_idx();
__wbg_s_Bar_set_property(ptr, val);
}
}
}
impl WasmBoundary for Bar {

View File

@ -95,7 +95,8 @@ pub fn project() -> Project {
"strictFunctionTypes": true,
"strictNullChecks": true,
"alwaysStrict": true,
"strict": true
"strict": true,
"target": "es5"
}
}
"#.to_string()),

View File

@ -957,23 +957,39 @@ impl<'a, 'b> SubContext<'a, 'b> {
let invoc_args = invoc_args.join(", ");
let function_name = &import.function.name;
let invoc = match import.class {
Some(ref class) if import.method => {
self.cx.globals.push_str(&format!("
const {}_target = {}.prototype.{};
", name, class, function_name));
format!("{}_target.call({})", name, invoc_args)
}
Some(ref class) if import.js_new => {
format!("new {}({})", class, invoc_args)
format!("new {}", class)
}
Some(ref class) if import.method => {
let target = if let Some(ref g) = import.getter {
format!(
"Object.getOwnPropertyDescriptor({}.prototype, '{}').get;",
class,
g,
)
} else if let Some(ref s) = import.setter {
format!(
"Object.getOwnPropertyDescriptor({}.prototype, '{}').set;",
class,
s,
)
} else {
format!("{}.prototype.{}", class, function_name)
};
self.cx.globals.push_str(&format!("
const {}_target = {};
", name, target));
format!("{}_target.call", name)
}
Some(ref class) => {
self.cx.globals.push_str(&format!("
const {}_target = {}.{};
", name, class, function_name));
format!("{}_target({})", name, invoc_args)
format!("{}_target", name)
}
None => format!("{}({})", function_name, invoc_args),
None => function_name.to_string(),
};
let invoc = format!("{}({})", invoc, invoc_args);
let invoc = match import.function.ret {
Some(shared::TYPE_NUMBER) => format!("return {};", invoc),
Some(shared::TYPE_BOOLEAN) => format!("return {} ? 1 : 0;", invoc),

View File

@ -528,6 +528,16 @@ impl Import {
}
ImportKind::Normal => {}
}
let mut getter = None;
let mut setter = None;
if self.function.opts.getter() {
getter = Some(self.infer_getter_property());
}
if self.function.opts.setter() {
setter = Some(self.infer_setter_property());
}
a.fields(&[
("module", &|a| {
match self.module {
@ -539,6 +549,18 @@ impl Import {
("method", &|a| a.bool(method)),
("js_new", &|a| a.bool(js_new)),
("statik", &|a| a.bool(statik)),
("getter", &|a| {
match getter {
Some(ref s) => a.str(s),
None => a.append("null"),
}
}),
("setter", &|a| {
match setter {
Some(ref s) => a.str(s),
None => a.append("null"),
}
}),
("function", &|a| self.function.wbg_literal(a)),
("class", &|a| {
match class_name {
@ -548,6 +570,16 @@ impl Import {
}),
]);
}
fn infer_getter_property(&self) -> String {
self.function.name.as_ref().to_string()
}
fn infer_setter_property(&self) -> String {
let name = self.function.name.as_ref();
assert!(name.starts_with("set_"), "setters must start with `set_`");
name[4..].to_string()
}
}
impl Struct {
@ -702,6 +734,26 @@ impl BindgenAttrs {
})
.next()
}
fn getter(&self) -> bool {
self.attrs.iter()
.any(|a| {
match *a {
BindgenAttr::Getter => true,
_ => false,
}
})
}
fn setter(&self) -> bool {
self.attrs.iter()
.any(|a| {
match *a {
BindgenAttr::Setter => true,
_ => false,
}
})
}
}
impl syn::synom::Synom for BindgenAttrs {
@ -725,6 +777,8 @@ enum BindgenAttr {
Method,
Static(syn::Type),
Module(String),
Getter,
Setter,
}
impl syn::synom::Synom for BindgenAttr {
@ -735,6 +789,10 @@ impl syn::synom::Synom for BindgenAttr {
|
call!(term, "method") => { |_| BindgenAttr::Method }
|
call!(term, "getter") => { |_| BindgenAttr::Getter }
|
call!(term, "setter") => { |_| BindgenAttr::Setter }
|
do_parse!(
call!(term, "static") >>
punct!(=) >>

View File

@ -19,6 +19,8 @@ pub struct Import {
pub method: bool,
pub js_new: bool,
pub statik: bool,
pub getter: Option<String>,
pub setter: Option<String>,
pub class: Option<String>,
pub function: Function,
}

View File

@ -278,3 +278,60 @@ fn switch_methods() {
"#)
.test();
}
#[test]
fn properties() {
test_support::project()
.file("src/lib.rs", r#"
#![feature(proc_macro)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "./test")]
extern {
type Foo;
#[wasm_bindgen(constructor)]
fn new() -> Foo;
#[wasm_bindgen(getter, method)]
fn a(this: &Foo) -> i32;
#[wasm_bindgen(setter, method)]
fn set_a(this: &Foo, a: i32);
}
#[wasm_bindgen]
#[no_mangle]
pub extern fn run() {
let a = Foo::new();
assert_eq!(a.a(), 1);
a.set_a(2);
assert_eq!(a.a(), 2);
}
"#)
.file("test.ts", r#"
import { run } from "./out";
export class Foo {
constructor(private num: number) {
this.num = 1;
}
get a() {
return this.num;
}
set a(val) {
this.num = val;
}
}
export function test() {
run();
}
"#)
.test();
}