Merge pull request #63 from fitzgen/literals

wasm-bindgen-macro: Formalize building literals with a trait
This commit is contained in:
Alex Crichton 2018-03-07 19:26:09 -06:00 committed by GitHub
commit 8b74c6c6ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 295 additions and 251 deletions

View File

@ -1,5 +1,4 @@
use std::collections::BTreeSet;
use literal::{self, Literal};
use proc_macro2::Span;
use quote::{Tokens, ToTokens};
use shared;
@ -50,7 +49,12 @@ pub struct Struct {
pub struct Enum {
pub name: syn::Ident,
pub variants: Vec<(syn::Ident, u32)>
pub variants: Vec<Variant>
}
pub struct Variant {
pub name: syn::Ident,
pub value: u32,
}
pub struct ImportedType {
@ -231,7 +235,10 @@ impl Program {
_ => panic!("Enums may only have number literal values")
};
(v.ident, value)
Variant {
name: v.ident,
value,
}
}).collect();
self.enums.push(Enum {
name: item.ident,
@ -334,34 +341,12 @@ impl Program {
});
}
pub fn wbg_literal(&self, dst: &mut Tokens) -> usize {
pub fn literal(&self, dst: &mut Tokens) -> usize {
let mut tmp = Tokens::new();
let cnt = {
let mut a = LiteralBuilder {
dst: &mut tmp,
cnt: 0,
};
a.fields(&[
("exports", &|a| a.list(&self.exports, Export::wbg_literal)),
("imports", &|a| a.list(&self.imports, Import::wbg_literal)),
("enums", &|a| a.list(&self.enums, Enum::wbg_literal)),
("custom_type_names", &|a| {
let names = self.exports.iter()
.filter_map(|e| e.class)
.chain(self.structs.iter().map(|s| s.name))
.collect::<BTreeSet<_>>();
a.list(&names, |s, a| {
let val = shared::name_to_descriptor(s.as_ref());
a.fields(&[
("descriptor", &|a| a.char(val)),
("name", &|a| a.str(s.as_ref()))
]);
})
}),
("version", &|a| a.str(&shared::version())),
("schema_version", &|a| a.str(&shared::SCHEMA_VERSION)),
]);
a.cnt
let mut a = literal::LiteralBuilder::new(&mut tmp);
Literal::literal(self, &mut a);
a.finish()
};
let cnt = cnt as u32;
(quote! {
@ -442,19 +427,6 @@ impl Function {
rust_attrs: attrs,
}, mutable)
}
fn wbg_literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("name", &|a| a.str(self.name.as_ref())),
("arguments", &|a| a.list(&self.arguments, Type::wbg_literal)),
("ret", &|a| {
match self.ret {
Some(ref s) => s.wbg_literal(a),
None => a.append("null"),
}
}),
]);
}
}
pub fn extract_path_ident(path: &syn::Path) -> Option<syn::Ident> {
@ -527,43 +499,6 @@ impl Type {
Type::ByValue(ty.clone())
}
fn wbg_literal(&self, a: &mut LiteralBuilder) {
match *self {
Type::Vector(VectorType::String, true) => a.char(shared::TYPE_STRING),
Type::Vector(VectorType::String, false) => a.char(shared::TYPE_BORROWED_STR),
Type::Vector(VectorType::U8, true) => a.char(shared::TYPE_VECTOR_U8),
Type::Vector(VectorType::U8, false) => a.char(shared::TYPE_SLICE_U8),
Type::Vector(VectorType::I8, true) => a.char(shared::TYPE_VECTOR_I8),
Type::Vector(VectorType::I8, false) => a.char(shared::TYPE_SLICE_I8),
Type::Vector(VectorType::U16, true) => a.char(shared::TYPE_VECTOR_U16),
Type::Vector(VectorType::U16, false) => a.char(shared::TYPE_SLICE_U16),
Type::Vector(VectorType::I16, true) => a.char(shared::TYPE_VECTOR_I16),
Type::Vector(VectorType::I16, false) => a.char(shared::TYPE_SLICE_I16),
Type::Vector(VectorType::U32, true) => a.char(shared::TYPE_VECTOR_U32),
Type::Vector(VectorType::U32, false) => a.char(shared::TYPE_SLICE_U32),
Type::Vector(VectorType::I32, true) => a.char(shared::TYPE_VECTOR_I32),
Type::Vector(VectorType::I32, false) => a.char(shared::TYPE_SLICE_I32),
Type::Vector(VectorType::F32, true) => a.char(shared::TYPE_VECTOR_F32),
Type::Vector(VectorType::F32, false) => a.char(shared::TYPE_SLICE_F32),
Type::Vector(VectorType::F64, true) => a.char(shared::TYPE_VECTOR_F64),
Type::Vector(VectorType::F64, false) => a.char(shared::TYPE_SLICE_F64),
Type::Vector(VectorType::JsValue, true) => a.char(shared::TYPE_VECTOR_JSVALUE),
Type::Vector(VectorType::JsValue, false) => panic!("Slices of JsValues not supported"),
Type::ByValue(ref t) => {
a.as_char(my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR
});
}
Type::ByRef(ref ty) |
Type::ByMutRef(ref ty) => {
a.as_char(my_quote! {
(<#ty as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR |
::wasm_bindgen::convert::DESCRIPTOR_CUSTOM_REF_FLAG)
});
}
}
}
}
impl Export {
@ -592,191 +527,26 @@ impl Export {
};
syn::LitStr::new(&name, Span::def_site())
}
fn wbg_literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("class", &|a| {
match self.class {
Some(ref s) => a.str(s.as_ref()),
None => a.append("null"),
}
}),
("method", &|a| a.bool(self.method)),
("function", &|a| self.function.wbg_literal(a)),
]);
}
}
impl Import {
fn wbg_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 {
ImportKind::Method { ref class, .. } => {
method = true;
class_name = Some(class);
}
ImportKind::JsConstructor { ref class, .. } => {
js_new = true;
class_name = Some(class);
}
ImportKind::Static { ref class, .. } => {
statik = true;
class_name = Some(class);
}
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 {
Some(ref s) => a.str(s),
None => a.append("null"),
}
}),
("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"),
}
}),
("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 {
Some(s) => a.str(s),
None => a.append("null"),
}
}),
]);
}
fn infer_getter_property(&self) -> String {
pub fn infer_getter_property(&self) -> String {
self.function.name.as_ref().to_string()
}
fn infer_setter_property(&self) -> String {
pub 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 Enum {
fn wbg_literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("name", &|a| a.str(self.name.as_ref())),
("variants", &|a| a.list(&self.variants, |v, a| {
let &(name, value) = v;
a.fields(&[("name", &|a| a.str(name.as_ref())),
("value", &|a| a.append(&format!("{}", value)))])
})),
]);
}
}
impl Struct {
fn from(s: syn::ItemStruct, _opts: BindgenAttrs) -> Struct {
Struct { name: s.ident }
}
}
struct LiteralBuilder<'a> {
dst: &'a mut Tokens,
cnt: usize,
}
impl<'a> LiteralBuilder<'a> {
fn char_lit(&mut self, c: char) {
if self.cnt > 0 {
::syn::token::Comma::default().to_tokens(self.dst);
}
self.cnt += 1;
(c as u32).to_tokens(self.dst);
}
fn append(&mut self, s: &str) {
for c in s.chars() {
self.char_lit(c);
}
}
fn str(&mut self, s: &str) {
self.append("\"");
self.append(s);
self.append("\"");
}
fn bool(&mut self, v: bool) {
if v {
self.append("true")
} else {
self.append("false")
}
}
fn char(&mut self, s: char) {
self.append("\"");
self.char_lit(s);
self.append("\"");
}
fn as_char(&mut self, tokens: Tokens) {
self.append("\"");
::syn::token::Comma::default().to_tokens(self.dst);
tokens.to_tokens(self.dst);
self.cnt += 1;
self.append("\"");
}
fn fields(&mut self, fields: &[(&str, &Fn(&mut Self))]) {
self.append("{");
for (i, &(field, cb)) in fields.iter().enumerate() {
if i > 0 {
self.append(",");
}
self.str(field);
self.append(":");
cb(self);
}
self.append("}");
}
fn list<T, F>(&mut self, list: T, mut cb: F)
where F: FnMut(T::Item, &mut Self),
T: IntoIterator,
{
self.append("[");
for (i, element) in list.into_iter().enumerate() {
if i > 0 {
self.append(",");
}
cb(element, self);
}
self.append("]");
}
}
#[derive(Default)]
pub struct BindgenAttrs {
attrs: Vec<BindgenAttr>,
@ -848,7 +618,7 @@ impl BindgenAttrs {
.next()
}
fn getter(&self) -> bool {
pub fn getter(&self) -> bool {
self.attrs.iter()
.any(|a| {
match *a {
@ -858,7 +628,7 @@ impl BindgenAttrs {
})
}
fn setter(&self) -> bool {
pub fn setter(&self) -> bool {
self.attrs.iter()
.any(|a| {
match *a {

View File

@ -23,6 +23,7 @@ macro_rules! my_quote {
}
mod ast;
mod literal;
#[proc_macro_attribute]
pub fn wasm_bindgen(attr: TokenStream, input: TokenStream) -> TokenStream {
@ -96,7 +97,7 @@ impl ToTokens for ast::Program {
let generated_static_name = syn::Ident::from(generated_static_name);
let mut generated_static_value = Tokens::new();
let generated_static_length = self.wbg_literal(&mut generated_static_value);
let generated_static_length = self.literal(&mut generated_static_value);
(my_quote! {
#[no_mangle]
@ -559,7 +560,7 @@ impl ToTokens for ast::Enum {
let incoming_u32 = quote! { n };
let enum_name_as_string = enum_name.to_string();
let cast_clauses = self.variants.iter().map(|variant| {
let &(variant_name, _) = variant;
let variant_name = &variant.name;
quote! {
if #incoming_u32 == #enum_name::#variant_name as u32 {
#enum_name::#variant_name

View File

@ -0,0 +1,273 @@
use ast;
use proc_macro2::Span;
use quote::{ToTokens, Tokens};
use shared;
use std::collections::BTreeSet;
pub struct LiteralBuilder<'a> {
dst: &'a mut Tokens,
cnt: usize,
}
impl<'a> LiteralBuilder<'a> {
pub fn new(dst: &'a mut Tokens) -> LiteralBuilder<'a> {
LiteralBuilder {
dst,
cnt: 0,
}
}
pub fn finish(self) -> usize {
self.cnt
}
fn char_lit(&mut self, c: char) {
if self.cnt > 0 {
::syn::token::Comma::default().to_tokens(self.dst);
}
self.cnt += 1;
(c as u32).to_tokens(self.dst);
}
fn append(&mut self, s: &str) {
for c in s.chars() {
self.char_lit(c);
}
}
fn str(&mut self, s: &str) {
self.append("\"");
self.append(s);
self.append("\"");
}
fn bool(&mut self, v: bool) {
if v {
self.append("true")
} else {
self.append("false")
}
}
fn char(&mut self, s: char) {
self.append("\"");
self.char_lit(s);
self.append("\"");
}
fn as_char(&mut self, tokens: Tokens) {
self.append("\"");
::syn::token::Comma::default().to_tokens(self.dst);
tokens.to_tokens(self.dst);
self.cnt += 1;
self.append("\"");
}
pub fn fields(&mut self, fields: &[(&str, &Fn(&mut Self))]) {
self.append("{");
for (i, &(field, cb)) in fields.iter().enumerate() {
if i > 0 {
self.append(",");
}
self.str(field);
self.append(":");
cb(self);
}
self.append("}");
}
pub fn list_of<'b, T, U>(&mut self, list: T)
where
T: IntoIterator<Item = &'b U>,
U: 'b + Literal,
{
self.list(list, U::literal)
}
fn list<T, F>(&mut self, list: T, mut cb: F)
where F: FnMut(T::Item, &mut Self),
T: IntoIterator,
{
self.append("[");
for (i, element) in list.into_iter().enumerate() {
if i > 0 {
self.append(",");
}
cb(element, self);
}
self.append("]");
}
}
pub trait Literal {
fn literal(&self, a: &mut LiteralBuilder);
}
impl Literal for ast::Program {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("exports", &|a| a.list_of(&self.exports)),
("imports", &|a| a.list_of(&self.imports)),
("enums", &|a| a.list_of(&self.enums)),
("custom_type_names", &|a| {
let names = self.exports
.iter()
.filter_map(|e| e.class)
.chain(self.structs.iter().map(|s| s.name))
.collect::<BTreeSet<_>>();
a.list(&names, |s, a| {
let val = shared::name_to_descriptor(s.as_ref());
a.fields(&[
("descriptor", &|a| a.char(val)),
("name", &|a| a.str(s.as_ref())),
]);
})
}),
("version", &|a| a.str(&shared::version())),
("schema_version", &|a| a.str(&shared::SCHEMA_VERSION)),
]);
}
}
impl Literal for ast::Function {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("name", &|a| a.str(self.name.as_ref())),
("arguments", &|a| a.list_of(&self.arguments)),
("ret", &|a| match self.ret {
Some(ref s) => s.literal(a),
None => a.append("null"),
}),
]);
}
}
impl Literal for ast::Type {
fn literal(&self, a: &mut LiteralBuilder) {
match *self {
ast::Type::Vector(ast::VectorType::String, true) => a.char(shared::TYPE_STRING),
ast::Type::Vector(ast::VectorType::String, false) => a.char(shared::TYPE_BORROWED_STR),
ast::Type::Vector(ast::VectorType::U8, true) => a.char(shared::TYPE_VECTOR_U8),
ast::Type::Vector(ast::VectorType::U8, false) => a.char(shared::TYPE_SLICE_U8),
ast::Type::Vector(ast::VectorType::I8, true) => a.char(shared::TYPE_VECTOR_I8),
ast::Type::Vector(ast::VectorType::I8, false) => a.char(shared::TYPE_SLICE_I8),
ast::Type::Vector(ast::VectorType::U16, true) => a.char(shared::TYPE_VECTOR_U16),
ast::Type::Vector(ast::VectorType::U16, false) => a.char(shared::TYPE_SLICE_U16),
ast::Type::Vector(ast::VectorType::I16, true) => a.char(shared::TYPE_VECTOR_I16),
ast::Type::Vector(ast::VectorType::I16, false) => a.char(shared::TYPE_SLICE_I16),
ast::Type::Vector(ast::VectorType::U32, true) => a.char(shared::TYPE_VECTOR_U32),
ast::Type::Vector(ast::VectorType::U32, false) => a.char(shared::TYPE_SLICE_U32),
ast::Type::Vector(ast::VectorType::I32, true) => a.char(shared::TYPE_VECTOR_I32),
ast::Type::Vector(ast::VectorType::I32, false) => a.char(shared::TYPE_SLICE_I32),
ast::Type::Vector(ast::VectorType::F32, true) => a.char(shared::TYPE_VECTOR_F32),
ast::Type::Vector(ast::VectorType::F32, false) => a.char(shared::TYPE_SLICE_F32),
ast::Type::Vector(ast::VectorType::F64, true) => a.char(shared::TYPE_VECTOR_F64),
ast::Type::Vector(ast::VectorType::F64, false) => a.char(shared::TYPE_SLICE_F64),
ast::Type::Vector(ast::VectorType::JsValue, true) => {
a.char(shared::TYPE_VECTOR_JSVALUE)
}
ast::Type::Vector(ast::VectorType::JsValue, false) => {
panic!("Slices of JsValues not supported")
}
ast::Type::ByValue(ref t) => {
a.as_char(my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR
});
}
ast::Type::ByRef(ref ty) | ast::Type::ByMutRef(ref ty) => {
a.as_char(my_quote! {
(<#ty as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR |
::wasm_bindgen::convert::DESCRIPTOR_CUSTOM_REF_FLAG)
});
}
}
}
}
impl Literal for ast::Export {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("class", &|a| match self.class {
Some(ref s) => a.str(s.as_ref()),
None => a.append("null"),
}),
("method", &|a| a.bool(self.method)),
("function", &|a| self.function.literal(a)),
]);
}
}
impl Literal for ast::Import {
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, .. } => {
method = true;
class_name = Some(class);
}
ast::ImportKind::JsConstructor { ref class, .. } => {
js_new = true;
class_name = Some(class);
}
ast::ImportKind::Static { ref class, .. } => {
statik = true;
class_name = Some(class);
}
ast::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 {
Some(ref s) => a.str(s),
None => a.append("null"),
}),
("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"),
}),
("setter", &|a| match setter {
Some(ref s) => a.str(s),
None => a.append("null"),
}),
("function", &|a| self.function.literal(a)),
("class", &|a| match class_name {
Some(s) => a.str(s),
None => a.append("null"),
}),
]);
}
}
impl Literal for ast::Enum {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("name", &|a| a.str(self.name.as_ref())),
("variants", &|a| a.list_of(&self.variants)),
]);
}
}
impl Literal for ast::Variant {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("name", &|a| a.str(self.name.as_ref())),
("value", &|a| a.append(&format!("{}", self.value))),
])
}
}