mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-15 17:50:51 +00:00
* Enable nested namespace (#951) * Specify the namespace as array (#951) * added an example to the document Co-authored-by: Alex Crichton <alex@alexcrichton.com>
This commit is contained in:
parent
e0ad7bfeac
commit
87663c6d2a
@ -75,7 +75,7 @@ pub enum MethodSelf {
|
||||
#[derive(Clone)]
|
||||
pub struct Import {
|
||||
pub module: ImportModule,
|
||||
pub js_namespace: Option<Ident>,
|
||||
pub js_namespace: Option<Vec<String>>,
|
||||
pub kind: ImportKind,
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use crate::util::ShortHash;
|
||||
use crate::Diagnostic;
|
||||
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Mutex;
|
||||
use syn;
|
||||
@ -32,10 +32,10 @@ impl TryToTokens for ast::Program {
|
||||
for s in self.structs.iter() {
|
||||
s.to_tokens(tokens);
|
||||
}
|
||||
let mut types = HashSet::new();
|
||||
let mut types = HashMap::new();
|
||||
for i in self.imports.iter() {
|
||||
if let ast::ImportKind::Type(t) = &i.kind {
|
||||
types.insert(t.rust_name.clone());
|
||||
types.insert(t.rust_name.to_string(), t.rust_name.clone());
|
||||
}
|
||||
}
|
||||
for i in self.imports.iter() {
|
||||
@ -43,8 +43,10 @@ impl TryToTokens for ast::Program {
|
||||
|
||||
// If there is a js namespace, check that name isn't a type. If it is,
|
||||
// this import might be a method on that type.
|
||||
if let Some(ns) = &i.js_namespace {
|
||||
if types.contains(ns) && i.kind.fits_on_impl() {
|
||||
if let Some(nss) = &i.js_namespace {
|
||||
// When the namespace is `A.B`, the type name should be `B`.
|
||||
if let Some(ns) = nss.last().and_then(|t| types.get(t)) {
|
||||
if i.kind.fits_on_impl() {
|
||||
let kind = match i.kind.try_to_token_stream() {
|
||||
Ok(kind) => kind,
|
||||
Err(e) => {
|
||||
@ -56,6 +58,7 @@ impl TryToTokens for ast::Program {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = i.kind.try_to_tokens(tokens) {
|
||||
errors.push(e);
|
||||
|
@ -241,7 +241,7 @@ fn shared_import<'a>(i: &'a ast::Import, intern: &'a Interner) -> Result<Import<
|
||||
ast::ImportModule::Inline(idx, _) => ImportModule::Inline(*idx as u32),
|
||||
ast::ImportModule::None => ImportModule::None,
|
||||
},
|
||||
js_namespace: i.js_namespace.as_ref().map(|s| intern.intern(s)),
|
||||
js_namespace: i.js_namespace.clone(),
|
||||
kind: shared_import_kind(&i.kind, intern)?,
|
||||
})
|
||||
}
|
||||
|
@ -1998,7 +1998,7 @@ impl<'a> Context<'a> {
|
||||
|
||||
JsImportName::VendorPrefixed { name, prefixes } => {
|
||||
self.imports_post.push_str("const l");
|
||||
self.imports_post.push_str(&name);
|
||||
self.imports_post.push_str(name);
|
||||
self.imports_post.push_str(" = ");
|
||||
switch(&mut self.imports_post, name, "", prefixes);
|
||||
self.imports_post.push_str(";\n");
|
||||
|
@ -898,7 +898,7 @@ impl<'a> Context<'a> {
|
||||
"import of `{}` through js namespace `{}` isn't supported \
|
||||
right now when it lists a polyfill",
|
||||
item,
|
||||
ns
|
||||
ns.join(".")
|
||||
);
|
||||
}
|
||||
return Ok(JsImport {
|
||||
@ -911,8 +911,12 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
|
||||
let (name, fields) = match import.js_namespace {
|
||||
Some(ns) => (ns, vec![item.to_string()]),
|
||||
None => (item, Vec::new()),
|
||||
Some(ref ns) => {
|
||||
let mut tail = (&ns[1..]).to_owned();
|
||||
tail.push(item.to_string());
|
||||
(ns[0].to_owned(), tail)
|
||||
}
|
||||
None => (item.to_owned(), Vec::new()),
|
||||
};
|
||||
|
||||
let name = match import.module {
|
||||
|
@ -17,7 +17,7 @@ extra-traits = ["syn/extra-traits"]
|
||||
strict-macro = []
|
||||
|
||||
[dependencies]
|
||||
syn = { version = '1.0.27', features = ['visit'] }
|
||||
syn = { version = '1.0.27', features = ['visit', 'full'] }
|
||||
quote = '1.0'
|
||||
proc-macro2 = "1.0"
|
||||
wasm-bindgen-backend = { path = "../backend", version = "=0.2.63" }
|
||||
|
@ -11,6 +11,7 @@ use quote::ToTokens;
|
||||
use shared;
|
||||
use syn;
|
||||
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
thread_local!(static ATTRS: AttributeParseState = Default::default());
|
||||
|
||||
@ -34,7 +35,7 @@ macro_rules! attrgen {
|
||||
(constructor, Constructor(Span)),
|
||||
(method, Method(Span)),
|
||||
(static_method_of, StaticMethodOf(Span, Ident)),
|
||||
(js_namespace, JsNamespace(Span, Ident)),
|
||||
(js_namespace, JsNamespace(Span, Vec<String>, Vec<Span>)),
|
||||
(module, Module(Span, String, Span)),
|
||||
(raw_module, RawModule(Span, String, Span)),
|
||||
(inline_js, InlineJs(Span, String, Span)),
|
||||
@ -116,6 +117,21 @@ macro_rules! methods {
|
||||
}
|
||||
};
|
||||
|
||||
(@method $name:ident, $variant:ident(Span, Vec<String>, Vec<Span>)) => {
|
||||
fn $name(&self) -> Option<(&[String], &[Span])> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match &a.1 {
|
||||
BindgenAttr::$variant(_, ss, spans) => {
|
||||
a.0.set(true);
|
||||
Some((&ss[..], &spans[..]))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
};
|
||||
|
||||
(@method $name:ident, $variant:ident(Span, $($other:tt)*)) => {
|
||||
#[allow(unused)]
|
||||
fn $name(&self) -> Option<&$($other)*> {
|
||||
@ -280,6 +296,36 @@ impl Parse for BindgenAttr {
|
||||
};
|
||||
return Ok(BindgenAttr::$variant(attr_span, val, span))
|
||||
});
|
||||
|
||||
(@parser $variant:ident(Span, Vec<String>, Vec<Span>)) => ({
|
||||
input.parse::<Token![=]>()?;
|
||||
let input_before_parse = input.fork();
|
||||
let (vals, spans) = match input.parse::<syn::ExprArray>() {
|
||||
Ok(exprs) => {
|
||||
let mut vals = vec![];
|
||||
let mut spans = vec![];
|
||||
|
||||
for expr in exprs.elems.iter() {
|
||||
if let syn::Expr::Lit(syn::ExprLit {
|
||||
lit: syn::Lit::Str(ref str),
|
||||
..
|
||||
}) = expr {
|
||||
vals.push(str.value());
|
||||
spans.push(str.span());
|
||||
} else {
|
||||
return Err(syn::Error::new(expr.span(), "expected string literals"));
|
||||
}
|
||||
}
|
||||
|
||||
(vals, spans)
|
||||
},
|
||||
Err(_) => {
|
||||
let ident = input_before_parse.parse::<AnyIdent>()?.0;
|
||||
(vec![ident.to_string()], vec![ident.span()])
|
||||
}
|
||||
};
|
||||
return Ok(BindgenAttr::$variant(attr_span, vals, spans))
|
||||
});
|
||||
}
|
||||
|
||||
attrgen!(parsers);
|
||||
@ -1270,7 +1316,7 @@ impl MacroParse<ast::ImportModule> for syn::ForeignItem {
|
||||
};
|
||||
BindgenAttrs::find(attrs)?
|
||||
};
|
||||
let js_namespace = item_opts.js_namespace().cloned();
|
||||
let js_namespace = item_opts.js_namespace().map(|(s, _)| s.to_owned());
|
||||
let kind = match self {
|
||||
syn::ForeignItem::Fn(f) => f.convert((item_opts, &module))?,
|
||||
syn::ForeignItem::Type(t) => t.convert(item_opts)?,
|
||||
|
@ -22,7 +22,7 @@ macro_rules! shared_api {
|
||||
|
||||
struct Import<'a> {
|
||||
module: ImportModule<'a>,
|
||||
js_namespace: Option<&'a str>,
|
||||
js_namespace: Option<Vec<String>>,
|
||||
kind: ImportKind<'a>,
|
||||
}
|
||||
|
||||
|
@ -24,3 +24,18 @@ Foo::new();
|
||||
This is an example of how to bind namespaced items in Rust. The `log` and `Foo::new` functions will
|
||||
be available in the Rust module and will be invoked as `console.log` and `new Bar.Foo` in
|
||||
JavaScript.
|
||||
|
||||
It is also possible to access the JavaScript object under the nested namespace.
|
||||
`js_namespace` also accepts the array of the string to specify the namespace.
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = ["window", "document"])]
|
||||
fn write(s: &str);
|
||||
}
|
||||
|
||||
write("hello, document!");
|
||||
```
|
||||
|
||||
This example shows how to bind `window.document.write` in Rust.
|
||||
|
@ -140,3 +140,31 @@ exports.StaticStructural = class {
|
||||
return x + 3;
|
||||
}
|
||||
};
|
||||
|
||||
class InnerClass {
|
||||
static inner_static_function(x) {
|
||||
return x + 5;
|
||||
}
|
||||
|
||||
static create_inner_instance() {
|
||||
const ret = new InnerClass();
|
||||
ret.internal_int = 3;
|
||||
return ret;
|
||||
}
|
||||
|
||||
get_internal_int() {
|
||||
return this.internal_int;
|
||||
}
|
||||
|
||||
append_to_internal_int(i) {
|
||||
this.internal_int += i;
|
||||
}
|
||||
|
||||
assert_internal_int(i) {
|
||||
assert.strictEqual(this.internal_int, i);
|
||||
}
|
||||
}
|
||||
|
||||
exports.nestedNamespace = {
|
||||
InnerClass: InnerClass
|
||||
}
|
||||
|
@ -96,6 +96,19 @@ extern "C" {
|
||||
type StaticStructural;
|
||||
#[wasm_bindgen(static_method_of = StaticStructural, structural)]
|
||||
fn static_structural(a: u32) -> u32;
|
||||
|
||||
#[derive(Clone)]
|
||||
type InnerClass;
|
||||
#[wasm_bindgen(js_namespace = ["nestedNamespace", "InnerClass"])]
|
||||
fn inner_static_function(a: u32) -> u32;
|
||||
#[wasm_bindgen(js_namespace = ["nestedNamespace", "InnerClass"])]
|
||||
fn create_inner_instance() -> InnerClass;
|
||||
#[wasm_bindgen(method)]
|
||||
fn get_internal_int(this: &InnerClass) -> u32;
|
||||
#[wasm_bindgen(method)]
|
||||
fn append_to_internal_int(this: &InnerClass, i: u32);
|
||||
#[wasm_bindgen(method)]
|
||||
fn assert_internal_int(this: &InnerClass, i: u32);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -237,3 +250,14 @@ fn catch_constructors() {
|
||||
fn static_structural() {
|
||||
assert_eq!(StaticStructural::static_structural(30), 33);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn nested_namespace() {
|
||||
assert_eq!(InnerClass::inner_static_function(15), 20);
|
||||
|
||||
let f = InnerClass::create_inner_instance();
|
||||
assert_eq!(f.get_internal_int(), 3);
|
||||
assert_eq!(f.clone().get_internal_int(), 3);
|
||||
f.append_to_internal_int(5);
|
||||
f.assert_internal_int(8);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user