Rename static to namespace

This commit renames the `static` attribute to `namespace` and simultaneously
reduces and expands the scope. The `namespace` attribute can now be applied to
all imports in addition to functions, and it no longer recognizes full typed
paths but rather just a bare identifier. The `namespace` attribute will generate
a Rust namespace to invoke the item through if one doesn't already exist (aka
bindign a type).
This commit is contained in:
Alex Crichton
2018-03-21 09:55:16 -07:00
parent dd054fa357
commit 4a4f8b18b6
9 changed files with 267 additions and 176 deletions

View File

@ -9,9 +9,7 @@ pub struct Program {
pub exports: Vec<Export>,
pub imports: Vec<Import>,
pub enums: Vec<Enum>,
pub imported_types: Vec<ImportedType>,
pub structs: Vec<Struct>,
pub imported_fields: Vec<ImportField>,
}
pub struct Export {
@ -23,17 +21,44 @@ pub struct Export {
pub struct Import {
pub module: Option<String>,
pub namespace: Option<syn::Ident>,
pub kind: ImportKind,
pub function: Function,
}
pub enum ImportKind {
Method { class: String, ty: syn::Type },
Static { class: String, ty: syn::Type },
JsConstructor { class: String, ty: syn::Type },
Function(ImportFunction),
Static(ImportStatic),
Type(ImportType),
}
pub struct ImportFunction {
pub function: Function,
pub kind: ImportFunctionKind,
}
pub enum ImportFunctionKind {
Method {
class: String,
ty: syn::Type,
},
JsConstructor {
class: String,
ty: syn::Type,
},
Normal,
}
pub struct ImportStatic {
pub vis: syn::Visibility,
pub ty: syn::Type,
pub name: syn::Ident,
}
pub struct ImportType {
pub vis: syn::Visibility,
pub name: syn::Ident,
}
pub struct Function {
pub name: syn::Ident,
pub arguments: Vec<Type>,
@ -58,18 +83,6 @@ pub struct Variant {
pub value: u32,
}
pub struct ImportedType {
pub vis: syn::Visibility,
pub name: syn::Ident,
}
pub struct ImportField {
pub vis: syn::Visibility,
pub ty: syn::Type,
pub module: Option<String>,
pub name: syn::Ident,
}
pub enum Type {
// special
Vector(VectorType, bool),
@ -206,22 +219,6 @@ impl Program {
});
}
pub fn push_foreign_mod(&mut self, f: syn::ItemForeignMod, opts: BindgenAttrs) {
match f.abi.name {
Some(ref l) if l.value() == "C" => {}
None => {}
_ => panic!("only foreign mods with the `C` ABI are allowed"),
}
for item in f.items.into_iter() {
match item {
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, &opts),
syn::ForeignItem::Type(t) => self.push_foreign_ty(t, &opts),
syn::ForeignItem::Static(s) => self.push_foreign_static(s, &opts),
_ => panic!("only foreign functions/types allowed for now"),
}
}
}
pub fn push_enum(&mut self, item: syn::ItemEnum, _opts: BindgenAttrs) {
match item.vis {
syn::Visibility::Public(_) => {}
@ -255,11 +252,38 @@ impl Program {
});
}
pub fn push_foreign_fn(&mut self,
mut f: syn::ForeignItemFn,
module_opts: &BindgenAttrs) {
let opts = BindgenAttrs::find(&mut f.attrs);
pub fn push_foreign_mod(&mut self, f: syn::ItemForeignMod, opts: BindgenAttrs) {
match f.abi.name {
Some(ref l) if l.value() == "C" => {}
None => {}
_ => panic!("only foreign mods with the `C` ABI are allowed"),
}
for mut item in f.items.into_iter() {
let item_opts = {
let attrs = match item {
syn::ForeignItem::Fn(ref mut f) => &mut f.attrs,
syn::ForeignItem::Type(ref mut t) => &mut t.attrs,
syn::ForeignItem::Static(ref mut s) => &mut s.attrs,
_ => panic!("only foreign functions/types allowed for now"),
};
BindgenAttrs::find(attrs)
};
let module = item_opts.module().or(opts.module()).map(|s| s.to_string());
let namespace = item_opts.namespace().or(opts.namespace());
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),
_ => panic!("only foreign functions/types allowed for now"),
};
self.imports.push(Import { module, namespace, kind });
}
}
pub fn push_foreign_fn(&mut self, f: syn::ForeignItemFn, opts: BindgenAttrs)
-> ImportKind
{
let mut wasm = Function::from_decl(f.ident,
f.decl,
f.attrs,
@ -298,7 +322,7 @@ impl Program {
let class_name = extract_path_ident(class_name)
.expect("first argument of method must be a bare type");
ImportKind::Method {
ImportFunctionKind::Method {
class: class_name.as_ref().to_string(),
ty: class.clone(),
}
@ -314,54 +338,40 @@ impl Program {
let class_name = extract_path_ident(class_name)
.expect("first argument of method must be a bare type");
ImportKind::JsConstructor {
ImportFunctionKind::JsConstructor {
class: class_name.as_ref().to_string(),
ty: class.clone(),
}
} else if let Some(class) = wasm.opts.static_receiver() {
let class_name = match *class {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
_ => panic!("first argument of method must be a path"),
};
let class_name = extract_path_ident(class_name)
.expect("first argument of method must be a bare type");
ImportKind::Static {
class: class_name.to_string(),
ty: class.clone(),
}
} else {
ImportKind::Normal
ImportFunctionKind::Normal
};
self.imports.push(Import {
module: module_opts.module().map(|s| s.to_string()),
kind,
ImportKind::Function(ImportFunction {
function: wasm,
});
kind,
})
}
pub fn push_foreign_ty(&mut self,
f: syn::ForeignItemType,
_module_opts: &BindgenAttrs) {
self.imported_types.push(ImportedType {
pub fn push_foreign_ty(&mut self, f: syn::ForeignItemType)
-> ImportKind
{
ImportKind::Type(ImportType {
vis: f.vis,
name: f.ident
});
})
}
pub fn push_foreign_static(&mut self,
f: syn::ForeignItemStatic,
module_opts: &BindgenAttrs) {
pub fn push_foreign_static(&mut self, f: syn::ForeignItemStatic)
-> ImportKind
{
if f.mutability.is_some() {
panic!("cannot import mutable globals yet")
}
self.imported_fields.push(ImportField {
module: module_opts.module().map(|s| s.to_string()),
ImportKind::Static(ImportStatic {
ty: *f.ty,
vis: f.vis,
name: f.ident
});
name: f.ident,
})
}
pub fn literal(&self, dst: &mut Tokens) -> usize {
@ -552,7 +562,7 @@ impl Export {
}
}
impl Import {
impl ImportFunction {
pub fn infer_getter_property(&self) -> String {
self.function.name.as_ref().to_string()
}
@ -630,11 +640,11 @@ impl BindgenAttrs {
})
}
fn static_receiver(&self) -> Option<&syn::Type> {
fn namespace(&self) -> Option<syn::Ident> {
self.attrs.iter()
.filter_map(|a| {
match *a {
BindgenAttr::Static(ref s) => Some(s),
BindgenAttr::Namespace(s) => Some(s),
_ => None,
}
})
@ -681,7 +691,7 @@ enum BindgenAttr {
Catch,
Constructor,
Method,
Static(syn::Type),
Namespace(syn::Ident),
Module(String),
Getter,
Setter,
@ -700,11 +710,11 @@ impl syn::synom::Synom for BindgenAttr {
call!(term, "setter") => { |_| BindgenAttr::Setter }
|
do_parse!(
call!(term, "static") >>
call!(term, "namespace") >>
punct!(=) >>
s: syn!(syn::Type) >>
(s)
)=> { BindgenAttr::Static }
ns: syn!(syn::Ident) >>
(ns)
)=> { BindgenAttr::Namespace }
|
do_parse!(
call!(term, "module") >>
@ -815,12 +825,3 @@ impl ToTokens for VectorType {
me.to_tokens(tokens);
}
}
impl ImportField {
pub fn shared(&self) -> shared::ImportField {
shared::ImportField {
module: self.module.clone(),
name: self.name.to_string(),
}
}
}

View File

@ -13,6 +13,7 @@ extern crate wasm_bindgen_shared as shared;
use std::borrow::Cow;
use std::env;
use std::sync::atomic::*;
use std::collections::{BTreeMap, HashSet};
use proc_macro::TokenStream;
use proc_macro2::Span;
@ -69,18 +70,44 @@ impl ToTokens for ast::Program {
for s in self.structs.iter() {
s.to_tokens(tokens);
}
let mut types = HashSet::new();
let mut buckets = BTreeMap::new();
for i in self.imports.iter() {
i.to_tokens(tokens);
buckets.entry(i.namespace)
.or_insert(Vec::new())
.push(i);
if let ast::ImportKind::Type(ref t) = i.kind {
types.insert(t.name);
}
}
for (namespace, imports) in buckets {
let mut sub_tokens = Tokens::new();
for import in imports {
import.kind.to_tokens(&mut sub_tokens);
}
match namespace {
Some(ns) if types.contains(&ns) => {
(quote! { impl #ns { #sub_tokens } }).to_tokens(tokens);
}
Some(ns) => {
(quote! {
// TODO: allow controlling `pub` here.
//
// TODO: we don't really want to generate a type here,
// it'd be preferrable to generate a namespace indicator
// or something like that (but modules interact weirdly
// with imports and such)
pub struct #ns { _priv: () }
impl #ns { #sub_tokens }
}).to_tokens(tokens);
}
None => sub_tokens.to_tokens(tokens),
}
}
for e in self.enums.iter() {
e.to_tokens(tokens);
}
for it in self.imported_types.iter() {
it.to_tokens(tokens);
}
for it in self.imported_fields.iter() {
it.to_tokens(tokens);
}
// Generate a static which will eventually be what lives in a custom section
// of the wasm executable. For now it's just a plain old static, but we'll
@ -311,7 +338,7 @@ impl ToTokens for ast::Export {
}
}
impl ToTokens for ast::ImportedType {
impl ToTokens for ast::ImportType {
fn to_tokens(&self, tokens: &mut Tokens) {
let vis = &self.vis;
let name = &self.name;
@ -344,23 +371,32 @@ impl ToTokens for ast::ImportedType {
}
}
impl ToTokens for ast::Import {
impl ToTokens for ast::ImportKind {
fn to_tokens(&self, tokens: &mut Tokens) {
match *self {
ast::ImportKind::Function(ref f) => f.to_tokens(tokens),
ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
ast::ImportKind::Type(ref t) => t.to_tokens(tokens),
}
}
}
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::ImportKind::Method { ref ty, ref class } => {
ast::ImportFunctionKind::Method { ref ty, ref class } => {
is_method = true;
class_ty = Some(ty);
class_name = Some(class);
}
ast::ImportKind::Static { ref ty, ref class } |
ast::ImportKind::JsConstructor { ref ty, ref class } => {
ast::ImportFunctionKind::JsConstructor { ref ty, ref class } => {
class_ty = Some(ty);
class_name = Some(class);
}
ast::ImportKind::Normal => {}
ast::ImportFunctionKind::Normal => {}
}
let import_name = shared::mangled_import_name(
class_name.map(|s| &**s),
@ -595,11 +631,12 @@ impl ToTokens for ast::Enum {
}
}
impl ToTokens for ast::ImportField {
impl ToTokens for ast::ImportStatic {
fn to_tokens(&self, into: &mut Tokens) {
let name = self.name;
let ty = &self.ty;
let shim_name = syn::Ident::from(self.shared().shim_name());
let shim_name = shared::static_import_shim_name(name.as_ref());
let shim_name = syn::Ident::from(shim_name);
let vis = &self.vis;
(my_quote! {
#vis static #name: ::wasm_bindgen::JsStatic<#ty> = {

View File

@ -107,8 +107,7 @@ impl Literal for ast::Program {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("exports", &|a| a.list_of(&self.exports)),
("imported_functions", &|a| a.list_of(&self.imports)),
("imported_fields", &|a| a.list_of(&self.imported_fields)),
("imports", &|a| a.list_of(&self.imports)),
("enums", &|a| a.list_of(&self.enums)),
("custom_type_names", &|a| {
let names = self.exports
@ -199,25 +198,46 @@ impl Literal for ast::Export {
}
impl Literal for ast::Import {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("module", &|a| match self.module {
Some(ref s) => a.str(s),
None => a.append("null"),
}),
("namespace", &|a| match self.namespace {
Some(ref s) => a.str(s.as_ref()),
None => a.append("null"),
}),
("kind", &|a| self.kind.literal(a)),
]);
}
}
impl Literal for ast::ImportKind {
fn literal(&self, a: &mut LiteralBuilder) {
match *self {
ast::ImportKind::Function(ref f) => f.literal(a),
ast::ImportKind::Static(ref s) => s.literal(a),
ast::ImportKind::Type(ref t) => t.literal(a),
}
}
}
impl Literal for ast::ImportFunction {
fn literal(&self, a: &mut LiteralBuilder) {
let mut method = false;
let mut js_new = false;
let mut statik = false;
let mut class_name = None;
match self.kind {
ast::ImportKind::Method { ref class, .. } => {
ast::ImportFunctionKind::Method { ref class, .. } => {
method = true;
class_name = Some(class);
}
ast::ImportKind::JsConstructor { ref class, .. } => {
ast::ImportFunctionKind::JsConstructor { ref class, .. } => {
js_new = true;
class_name = Some(class);
}
ast::ImportKind::Static { ref class, .. } => {
statik = true;
class_name = Some(class);
}
ast::ImportKind::Normal => {}
ast::ImportFunctionKind::Normal => {}
}
let mut getter = None;
@ -230,14 +250,10 @@ impl Literal for ast::Import {
setter = Some(self.infer_setter_property());
}
a.fields(&[
("module", &|a| match self.module {
Some(ref s) => a.str(s),
None => a.append("null"),
}),
("kind", &|a| a.str("function")),
("catch", &|a| a.bool(self.function.opts.catch())),
("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"),
@ -273,14 +289,19 @@ impl Literal for ast::Variant {
}
}
impl Literal for ast::ImportField {
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())),
("module", &|a| match self.module {
Some(ref s) => a.str(s),
None => a.append("null"),
}),
])
}
}
impl Literal for ast::ImportType {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("kind", &|a| a.str("type")),
])
}
}