mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-11-28 14:27:36 +03:00
Implement a js_name
customization
This'll allow binding multiple signatures of a JS function as well as otherwise changing the name of the JS function you're calling from the Rust function that you're defining. Closes #72
This commit is contained in:
parent
a8045fbbe9
commit
7ebc428646
24
DESIGN.md
24
DESIGN.md
@ -975,7 +975,7 @@ 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`
|
||||
Finally, you can also pass an 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:
|
||||
@ -984,7 +984,7 @@ possibilities!
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
type Foo;
|
||||
#[wasm_bindgen(method, getter = "property")]
|
||||
#[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);
|
||||
@ -1017,6 +1017,26 @@ possibilities!
|
||||
Instead wasm-bindgen will generate shims that will access the passed in JS
|
||||
value's `bar` property to or the `baz` property (depending on the function).
|
||||
|
||||
* `js_name = foo` - this can be used to bind to a different function in JS than
|
||||
the identifier that's defined in Rust. For example you can also define
|
||||
multiple signatures for a polymorphic function in JS as well:
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
type Foo;
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_string(s: &str);
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_u32(n: u32);
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_many(a: u32, b: JsValue);
|
||||
}
|
||||
```
|
||||
|
||||
All of these functions will call `console.log` in Rust, but each identifier
|
||||
will have only one signature in Rust.
|
||||
|
||||
## Wrapping up
|
||||
|
||||
That's currently at least what `wasm-bindgen` has to offer! If you've got more
|
||||
|
@ -1336,27 +1336,24 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
info: &shared::Import,
|
||||
import: &shared::ImportStatic) {
|
||||
// TODO: should support more types to import here
|
||||
let name = shared::static_import_shim_name(&import.name);
|
||||
self.cx.imports_to_rewrite.insert(name.clone());
|
||||
self.cx.imports_to_rewrite.insert(import.shim.clone());
|
||||
let obj = self.import_name(info, &import.name);
|
||||
self.cx.expose_add_heap_object();
|
||||
self.cx.globals.push_str(&format!("
|
||||
export function {}() {{
|
||||
return addHeapObject({});
|
||||
}}
|
||||
", name, obj));
|
||||
", import.shim, obj));
|
||||
}
|
||||
|
||||
pub fn generate_import_function(&mut self,
|
||||
info: &shared::Import,
|
||||
import: &shared::ImportFunction) {
|
||||
let name = shared::mangled_import_name(import.class.as_ref().map(|s| &**s),
|
||||
&import.function.name);
|
||||
self.cx.imports_to_rewrite.insert(name.clone());
|
||||
self.cx.imports_to_rewrite.insert(import.shim.clone());
|
||||
|
||||
let mut dst = String::new();
|
||||
|
||||
dst.push_str(&format!("function {}(", name));
|
||||
dst.push_str(&format!("function {}(", import.shim));
|
||||
let mut invoc_args = Vec::new();
|
||||
let mut abi_args = Vec::new();
|
||||
|
||||
@ -1490,25 +1487,25 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
};
|
||||
self.cx.globals.push_str(&format!("
|
||||
const {}_target = {};
|
||||
", name, target));
|
||||
format!("{}_target.call", name)
|
||||
", import.shim, target));
|
||||
format!("{}_target.call", import.shim)
|
||||
}
|
||||
Some(ref class) => {
|
||||
let class = self.import_name(info, class);
|
||||
self.cx.globals.push_str(&format!("
|
||||
const {}_target = {}.{};
|
||||
", name, class, function_name));
|
||||
format!("{}_target", name)
|
||||
", import.shim, class, function_name));
|
||||
format!("{}_target", import.shim)
|
||||
}
|
||||
None => {
|
||||
let import = self.import_name(info, function_name);
|
||||
if import.contains(".") {
|
||||
let name = self.import_name(info, function_name);
|
||||
if name.contains(".") {
|
||||
self.cx.globals.push_str(&format!("
|
||||
const {}_target = {};
|
||||
", name, import));
|
||||
format!("{}_target", name)
|
||||
", import.shim, name));
|
||||
format!("{}_target", import.shim)
|
||||
} else {
|
||||
import
|
||||
name
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -33,7 +33,9 @@ pub enum ImportKind {
|
||||
|
||||
pub struct ImportFunction {
|
||||
pub function: Function,
|
||||
pub rust_name: syn::Ident,
|
||||
pub kind: ImportFunctionKind,
|
||||
pub shim: syn::Ident,
|
||||
}
|
||||
|
||||
pub enum ImportFunctionKind {
|
||||
@ -51,7 +53,9 @@ pub enum ImportFunctionKind {
|
||||
pub struct ImportStatic {
|
||||
pub vis: syn::Visibility,
|
||||
pub ty: syn::Type,
|
||||
pub name: syn::Ident,
|
||||
pub shim: syn::Ident,
|
||||
pub rust_name: syn::Ident,
|
||||
pub js_name: syn::Ident,
|
||||
}
|
||||
|
||||
pub struct ImportType {
|
||||
@ -273,7 +277,7 @@ impl Program {
|
||||
let mut kind = match item {
|
||||
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, item_opts),
|
||||
syn::ForeignItem::Type(t) => self.push_foreign_ty(t),
|
||||
syn::ForeignItem::Static(s) => self.push_foreign_static(s),
|
||||
syn::ForeignItem::Static(s) => self.push_foreign_static(s, item_opts),
|
||||
_ => panic!("only foreign functions/types allowed for now"),
|
||||
};
|
||||
|
||||
@ -284,7 +288,8 @@ impl Program {
|
||||
pub fn push_foreign_fn(&mut self, f: syn::ForeignItemFn, opts: BindgenAttrs)
|
||||
-> ImportKind
|
||||
{
|
||||
let mut wasm = Function::from_decl(f.ident,
|
||||
let js_name = opts.js_name().unwrap_or(f.ident);
|
||||
let mut wasm = Function::from_decl(js_name,
|
||||
f.decl,
|
||||
f.attrs,
|
||||
opts,
|
||||
@ -346,9 +351,19 @@ impl Program {
|
||||
ImportFunctionKind::Normal
|
||||
};
|
||||
|
||||
let shim = {
|
||||
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)
|
||||
};
|
||||
ImportKind::Function(ImportFunction {
|
||||
function: wasm,
|
||||
kind,
|
||||
rust_name: f.ident,
|
||||
shim: shim.into(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -361,16 +376,22 @@ impl Program {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn push_foreign_static(&mut self, f: syn::ForeignItemStatic)
|
||||
pub fn push_foreign_static(&mut self,
|
||||
f: syn::ForeignItemStatic,
|
||||
opts: BindgenAttrs)
|
||||
-> ImportKind
|
||||
{
|
||||
if f.mutability.is_some() {
|
||||
panic!("cannot import mutable globals yet")
|
||||
}
|
||||
let js_name = opts.js_name().unwrap_or(f.ident);
|
||||
let shim = format!("__wbg_static_accessor_{}_{}", js_name, f.ident);
|
||||
ImportKind::Static(ImportStatic {
|
||||
ty: *f.ty,
|
||||
vis: f.vis,
|
||||
name: f.ident,
|
||||
rust_name: f.ident,
|
||||
js_name,
|
||||
shim: shim.into(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -651,22 +672,22 @@ impl BindgenAttrs {
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn getter(&self) -> Option<Option<String>> {
|
||||
pub fn getter(&self) -> Option<Option<syn::Ident>> {
|
||||
self.attrs.iter()
|
||||
.filter_map(|a| {
|
||||
match *a {
|
||||
BindgenAttr::Getter(ref s) => Some(s.clone()),
|
||||
BindgenAttr::Getter(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
pub fn setter(&self) -> Option<Option<String>> {
|
||||
pub fn setter(&self) -> Option<Option<syn::Ident>> {
|
||||
self.attrs.iter()
|
||||
.filter_map(|a| {
|
||||
match *a {
|
||||
BindgenAttr::Setter(ref s) => Some(s.clone()),
|
||||
BindgenAttr::Setter(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
@ -682,6 +703,17 @@ impl BindgenAttrs {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn js_name(&self) -> Option<syn::Ident> {
|
||||
self.attrs.iter()
|
||||
.filter_map(|a| {
|
||||
match *a {
|
||||
BindgenAttr::JsName(s) => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttrs {
|
||||
@ -705,9 +737,10 @@ enum BindgenAttr {
|
||||
Method,
|
||||
JsNamespace(syn::Ident),
|
||||
Module(String),
|
||||
Getter(Option<String>),
|
||||
Setter(Option<String>),
|
||||
Getter(Option<syn::Ident>),
|
||||
Setter(Option<syn::Ident>),
|
||||
Structural,
|
||||
JsName(syn::Ident),
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttr {
|
||||
@ -722,8 +755,8 @@ impl syn::synom::Synom for BindgenAttr {
|
||||
call!(term, "getter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
s: syn!(syn::Ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Getter }
|
||||
@ -732,8 +765,8 @@ impl syn::synom::Synom for BindgenAttr {
|
||||
call!(term, "setter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
s: syn!(syn::Ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Setter }
|
||||
@ -753,6 +786,13 @@ impl syn::synom::Synom for BindgenAttr {
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Module }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_name") >>
|
||||
punct!(=) >>
|
||||
ns: syn!(syn::Ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::JsName }
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -388,23 +388,16 @@ impl ToTokens for ast::ImportFunction {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
let mut class_ty = None;
|
||||
let mut is_method = false;
|
||||
let mut class_name = None;
|
||||
match self.kind {
|
||||
ast::ImportFunctionKind::Method { ref ty, ref class } => {
|
||||
ast::ImportFunctionKind::Method { ref ty, .. } => {
|
||||
is_method = true;
|
||||
class_ty = Some(ty);
|
||||
class_name = Some(class);
|
||||
}
|
||||
ast::ImportFunctionKind::JsConstructor { ref ty, ref class } => {
|
||||
ast::ImportFunctionKind::JsConstructor { ref ty, .. } => {
|
||||
class_ty = Some(ty);
|
||||
class_name = Some(class);
|
||||
}
|
||||
ast::ImportFunctionKind::Normal => {}
|
||||
}
|
||||
let import_name = shared::mangled_import_name(
|
||||
class_name.map(|s| &**s),
|
||||
self.function.name.as_ref(),
|
||||
);
|
||||
let vis = &self.function.rust_vis;
|
||||
let ret = &self.function.rust_decl.output;
|
||||
let fn_token = &self.function.rust_decl.fn_token;
|
||||
@ -552,8 +545,8 @@ impl ToTokens for ast::ImportFunction {
|
||||
};
|
||||
}
|
||||
|
||||
let name = self.function.name;
|
||||
let import_name = syn::Ident::from(import_name);
|
||||
let rust_name = self.rust_name;
|
||||
let import_name = self.shim;
|
||||
let attrs = &self.function.rust_attrs;
|
||||
|
||||
let arguments = self.function.rust_decl.inputs
|
||||
@ -570,7 +563,7 @@ impl ToTokens for ast::ImportFunction {
|
||||
let invocation = my_quote! {
|
||||
#(#attrs)*
|
||||
#[allow(bad_style)]
|
||||
#vis extern #fn_token #name(#me #(#arguments),*) #ret {
|
||||
#vis extern #fn_token #rust_name(#me #(#arguments),*) #ret {
|
||||
::wasm_bindgen::__rt::link_this_library();
|
||||
extern {
|
||||
fn #import_name(#(#abi_arguments),*) -> #abi_ret;
|
||||
@ -637,10 +630,9 @@ impl ToTokens for ast::Enum {
|
||||
|
||||
impl ToTokens for ast::ImportStatic {
|
||||
fn to_tokens(&self, into: &mut Tokens) {
|
||||
let name = self.name;
|
||||
let name = self.rust_name;
|
||||
let ty = &self.ty;
|
||||
let shim_name = shared::static_import_shim_name(name.as_ref());
|
||||
let shim_name = syn::Ident::from(shim_name);
|
||||
let shim_name = self.shim;
|
||||
let vis = &self.vis;
|
||||
(my_quote! {
|
||||
#[allow(bad_style)]
|
||||
|
@ -245,9 +245,11 @@ impl Literal for ast::ImportFunction {
|
||||
let structural = self.function.opts.structural();
|
||||
|
||||
if let Some(s) = self.function.opts.getter() {
|
||||
let s = s.map(|s| s.to_string());
|
||||
getter = Some(s.unwrap_or_else(|| self.infer_getter_property()));
|
||||
}
|
||||
if let Some(s) = self.function.opts.setter() {
|
||||
let s = s.map(|s| s.to_string());
|
||||
setter = Some(s.unwrap_or_else(|| self.infer_setter_property()));
|
||||
}
|
||||
a.fields(&[
|
||||
@ -256,6 +258,7 @@ impl Literal for ast::ImportFunction {
|
||||
("method", &|a| a.bool(method)),
|
||||
("js_new", &|a| a.bool(js_new)),
|
||||
("structural", &|a| a.bool(structural)),
|
||||
("shim", &|a| a.str(self.shim.as_ref())),
|
||||
("getter", &|a| match getter {
|
||||
Some(ref s) => a.str(s),
|
||||
None => a.append("null"),
|
||||
@ -295,7 +298,8 @@ impl Literal for ast::ImportStatic {
|
||||
fn literal(&self, a: &mut LiteralBuilder) {
|
||||
a.fields(&[
|
||||
("kind", &|a| a.str("static")),
|
||||
("name", &|a| a.str(self.name.as_ref())),
|
||||
("name", &|a| a.str(self.js_name.as_ref())),
|
||||
("shim", &|a| a.str(self.shim.as_ref())),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ pub enum ImportKind {
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ImportFunction {
|
||||
pub shim: String,
|
||||
pub module: Option<String>,
|
||||
pub catch: bool,
|
||||
pub method: bool,
|
||||
@ -49,6 +50,7 @@ pub struct ImportFunction {
|
||||
pub struct ImportStatic {
|
||||
pub module: Option<String>,
|
||||
pub name: String,
|
||||
pub shim: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -110,17 +112,6 @@ pub fn struct_function_export_name(struct_: &str, f: &str) -> String {
|
||||
return name
|
||||
}
|
||||
|
||||
pub fn mangled_import_name(struct_: Option<&str>, f: &str) -> String {
|
||||
match struct_ {
|
||||
Some(s) => format!("__wbg_s_{}_{}", s, f),
|
||||
None => format!("__wbg_f_{}", f),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn static_import_shim_name(statik: &str) -> String {
|
||||
format!("__wbg_field_import_shim_{}", statik)
|
||||
}
|
||||
|
||||
pub type Type = char;
|
||||
|
||||
pub const TYPE_VECTOR_JSVALUE: char = '\u{5b}';
|
||||
|
@ -8,9 +8,15 @@ use wasm_bindgen::prelude::*;
|
||||
extern {
|
||||
#[wasm_bindgen(js_namespace = console)]
|
||||
fn log(s: &str);
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_u32(a: u32);
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
fn log_many(a: &str, b: &str);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run() {
|
||||
log("Hello from Rust!");
|
||||
log_u32(42);
|
||||
log_many("Logging", "many values!");
|
||||
}
|
||||
|
@ -24,10 +24,10 @@ extern {
|
||||
fn body(this: &HTMLDocument) -> Element;
|
||||
|
||||
type Element;
|
||||
#[wasm_bindgen(method, setter = "innerHTML")]
|
||||
#[wasm_bindgen(method, setter = innerHTML)]
|
||||
fn set_inner_html(this: &Element, html: &str);
|
||||
#[wasm_bindgen(method)]
|
||||
fn appendChild(this: &Element, other: Element);
|
||||
#[wasm_bindgen(method, js_name = appendChild)]
|
||||
fn append_child(this: &Element, other: Element);
|
||||
}
|
||||
|
||||
// Called by our JS entry point to run the example
|
||||
@ -35,5 +35,5 @@ extern {
|
||||
pub fn run() {
|
||||
let val = document.createElement("p");
|
||||
val.set_inner_html("Hello from Rust!");
|
||||
document.body().appendChild(val);
|
||||
document.body().append_child(val);
|
||||
}
|
||||
|
@ -353,10 +353,10 @@ fn rename_setter_getter() {
|
||||
#[wasm_bindgen(constructor)]
|
||||
fn new() -> Foo;
|
||||
|
||||
#[wasm_bindgen(getter = "a", method)]
|
||||
#[wasm_bindgen(getter = a, method)]
|
||||
fn test(this: &Foo) -> i32;
|
||||
|
||||
#[wasm_bindgen(setter = "a", method)]
|
||||
#[wasm_bindgen(setter = a, method)]
|
||||
fn another(this: &Foo, a: i32);
|
||||
}
|
||||
|
||||
|
@ -310,3 +310,42 @@ fn import_a_field() {
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename() {
|
||||
test_support::project()
|
||||
.file("src/lib.rs", r#"
|
||||
#![feature(proc_macro)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(module = "./test")]
|
||||
extern {
|
||||
#[wasm_bindgen(js_name = baz)]
|
||||
fn foo();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn run() {
|
||||
foo();
|
||||
}
|
||||
"#)
|
||||
.file("test.ts", r#"
|
||||
import * as wasm from "./out";
|
||||
import * as assert from "assert";
|
||||
|
||||
let called = false;
|
||||
|
||||
export function baz() {
|
||||
called = true;
|
||||
}
|
||||
|
||||
export function test() {
|
||||
wasm.run();
|
||||
assert.strictEqual(called, true);
|
||||
}
|
||||
"#)
|
||||
.test();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user