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

@ -658,7 +658,7 @@ extern {
#[wasm_bindgen(constructor)]
fn new(arg: i32) -> Bar;
#[wasm_bindgen(static = Bar)]
#[wasm_bindgen(namespace = Bar)]
fn another_function() -> i32;
#[wasm_bindgen(method)]
@ -698,8 +698,8 @@ let's go through one-by-one:
* `#[wasm_bindgen(constructor)]` - this indicates that the binding's name isn't
actually used in JS but rather translates to `new Bar()`. The return value of
this function must be a bare type, like `Bar`.
* `#[wasm_bindgen(static = Bar)]` - this attribute indicates that the function
declaration is a static function accessed through the `Bar` class in JS.
* `#[wasm_bindgen(namespace = Bar)]` - this attribute indicates that the
function declaration is namespaced through the `Bar` class in JS.
* `#[wasm_bindgen(method)]` - and finally, this attribute indicates that a
method call is going to happen. The first argument must be a JS struct, like
`Bar`, and the call in JS looks like `Bar.prototype.set.call(...)`.

View File

@ -1044,12 +1044,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
for f in self.program.exports.iter() {
self.generate_export(f);
}
for f in self.program.imported_functions.iter() {
for f in self.program.imports.iter() {
self.generate_import(f);
}
for f in self.program.imported_fields.iter() {
self.generate_import_field(f);
}
for e in self.program.enums.iter() {
self.generate_enum(e);
}
@ -1322,35 +1319,36 @@ impl<'a, 'b> SubContext<'a, 'b> {
(format!("{} {}", prefix, dst), format!("{} {}", prefix, dst_ts))
}
pub fn generate_import_field(&mut self, import: &shared::ImportField) {
let name = import.shim_name();
self.cx.imports_to_rewrite.insert(name.clone());
if let Some(ref module) = import.module {
self.cx.imports.push_str(&format!("
import {{ {} }} from '{}';
", import.name, module));
pub fn generate_import(&mut self, import: &shared::Import) {
match import.kind {
shared::ImportKind::Function(ref f) => {
self.generate_import_function(import, f)
}
shared::ImportKind::Static(ref s) => {
self.generate_import_static(import, s)
}
shared::ImportKind::Type(_) => {}
}
}
pub fn generate_import_static(&mut self,
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());
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, import.name));
", name, obj));
}
pub fn generate_import(&mut self, import: &shared::Import) {
if let Some(ref module) = import.module {
let name_to_import = import.class.as_ref().unwrap_or(&import.function.name);
if self.cx.imported_names.insert(name_to_import.clone()) {
self.cx.imports.push_str(&format!("
import {{ {} }} from '{}';
", name_to_import, module));
}
}
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());
@ -1361,12 +1359,6 @@ impl<'a, 'b> SubContext<'a, 'b> {
let mut invoc_args = Vec::new();
let mut abi_args = Vec::new();
// if import.method {
// abi_args.push("ptr".to_string());
// invoc_args.push("getObject(ptr)".to_string());
// self.cx.expose_get_object();
// }
let mut extra = String::new();
for (i, arg) in import.function.arguments.iter().enumerate() {
@ -1444,9 +1436,10 @@ impl<'a, 'b> SubContext<'a, 'b> {
let function_name = &import.function.name;
let invoc = match import.class {
Some(ref class) if import.js_new => {
format!("new {}", class)
format!("new {}", self.import_name(info, class))
}
Some(ref class) if import.method => {
let class = self.import_name(info, class);
let target = if let Some(ref g) = import.getter {
format!(
"Object.getOwnPropertyDescriptor({}.prototype, '{}').get;",
@ -1468,12 +1461,23 @@ impl<'a, 'b> SubContext<'a, 'b> {
format!("{}_target.call", name)
}
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)
}
None => function_name.to_string(),
None => {
let import = self.import_name(info, function_name);
if import.contains(".") {
self.cx.globals.push_str(&format!("
const {}_target = {};
", name, import));
format!("{}_target", name)
} else {
import
}
}
};
let invoc = format!("{}({})", invoc, invoc_args);
let invoc = match import.function.ret {
@ -1551,6 +1555,22 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.typescript.push_str(&variants);
self.cx.typescript.push_str("}\n");
}
fn import_name(&mut self, import: &shared::Import, item: &str) -> String {
if let Some(ref module) = import.module {
let name = import.namespace.as_ref().map(|s| &**s).unwrap_or(item);
if self.cx.imported_names.insert(name.to_string()) {
self.cx.imports.push_str(&format!("
import {{ {} }} from '{}';
", name, module));
}
}
match import.namespace {
Some(ref s) => format!("{}.{}", s, item),
None => item.to_string(),
}
}
}
struct VectorType {

View File

@ -168,6 +168,7 @@ fn extract_programs(module: &mut Module) -> Vec<shared::Program> {
let json = a[3..].iter()
.map(|i| char::from_u32(i.0).unwrap())
.collect::<String>();
println!("{}", json);
let p: shared::Program = match serde_json::from_str(&json) {
Ok(f) => f,
Err(e) => {

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")),
])
}
}

View File

@ -11,8 +11,7 @@ pub const SCHEMA_VERSION: &str = "0";
pub struct Program {
pub exports: Vec<Export>,
pub enums: Vec<Enum>,
pub imported_functions: Vec<Import>,
pub imported_fields: Vec<ImportField>,
pub imports: Vec<Import>,
pub custom_type_names: Vec<CustomTypeName>,
pub version: String,
pub schema_version: String,
@ -20,11 +19,25 @@ pub struct Program {
#[derive(Deserialize)]
pub struct Import {
pub module: Option<String>,
pub namespace: Option<String>,
pub kind: ImportKind,
}
#[derive(Deserialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum ImportKind {
Function(ImportFunction),
Static(ImportStatic),
Type(ImportType),
}
#[derive(Deserialize)]
pub struct ImportFunction {
pub module: Option<String>,
pub catch: bool,
pub method: bool,
pub js_new: bool,
pub statik: bool,
pub getter: Option<String>,
pub setter: Option<String>,
pub class: Option<String>,
@ -32,11 +45,15 @@ pub struct Import {
}
#[derive(Deserialize)]
pub struct ImportField {
pub struct ImportStatic {
pub module: Option<String>,
pub name: String,
}
#[derive(Deserialize)]
pub struct ImportType {
}
#[derive(Deserialize)]
pub struct Export {
pub class: Option<String>,
@ -99,6 +116,10 @@ pub fn mangled_import_name(struct_: Option<&str>, f: &str) -> String {
}
}
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}';
@ -155,9 +176,3 @@ pub fn version() -> String {
}
return v
}
impl ImportField {
pub fn shim_name(&self) -> String {
format!("__wbg_field_import_shim_{}", self.name)
}
}

View File

@ -6,9 +6,7 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern {
type console;
#[wasm_bindgen(static = console)]
#[wasm_bindgen(namespace = console)]
fn log(s: &str);
}

View File

@ -22,10 +22,9 @@ fn simple() {
#[wasm_bindgen]
extern {
type Math;
#[wasm_bindgen(static = Math)]
#[wasm_bindgen(namespace = Math)]
fn random() -> f64;
#[wasm_bindgen(static = Math)]
#[wasm_bindgen(namespace = Math)]
fn log(a: f64) -> f64;
}
"#)
@ -53,8 +52,7 @@ fn import_class() {
#[wasm_bindgen(module = "./another")]
extern {
type Foo;
#[wasm_bindgen(static = Foo)]
#[wasm_bindgen(namespace = Foo)]
fn bar();
}
@ -98,7 +96,7 @@ fn construct() {
#[wasm_bindgen(module = "./another")]
extern {
type Foo;
#[wasm_bindgen(static = Foo)]
#[wasm_bindgen(namespace = Foo)]
fn create() -> Foo;
#[wasm_bindgen(method)]
fn get_internal_string(this: &Foo) -> String;
@ -219,7 +217,7 @@ fn switch_methods() {
#[wasm_bindgen(constructor)]
fn new() -> Foo;
#[wasm_bindgen(static = Foo)]
#[wasm_bindgen(namespace = Foo)]
fn a();
#[wasm_bindgen(method)]