Alex Crichton 02b7021053 Leverage new rustc wasm features
This commit leverages two new attributes in the Rust compiler,
`#[wasm_custom_section]` and `#[wasm_import_module]`. These two attributes allow
removing a lot of hacks found in wasm-bindgen and also allows removing the
requirement of `wasm-opt` to remove the unused data sections.

This does require two new nightly features but we already required the
`proc_macro` nightly feature and these will hopefully be stabilized before that
feature!
2018-03-24 10:36:19 -07:00

324 lines
10 KiB
Rust

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 byte(&mut self, b: u8) {
::syn::token::Comma::default().to_tokens(self.dst);
self.cnt += 1;
b.to_tokens(self.dst);
}
fn char_lit(&mut self, c: char) {
let c = c as u32;
self.byte(c as u8);
self.byte((c >> 8) as u8);
self.byte((c >> 16) as u8);
self.byte((c >> 24) as u8);
}
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("\"");
(quote! {
, (((#tokens) as u32) >> 0) as u8
, (((#tokens) as u32) >> 8) as u8
, (((#tokens) as u32) >> 16) as u8
, (((#tokens) as u32) >> 24) as u8
}).to_tokens(self.dst);
self.cnt += 4;
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) {
a.fields(&[
("module", &|a| match self.module {
Some(ref s) => a.str(s),
None => a.append("null"),
}),
("js_namespace", &|a| match self.js_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 class_name = None;
match self.kind {
ast::ImportFunctionKind::Method { ref class, .. } => {
method = true;
class_name = Some(class);
}
ast::ImportFunctionKind::JsConstructor { ref class, .. } => {
js_new = true;
class_name = Some(class);
}
ast::ImportFunctionKind::Normal => {}
}
let mut getter = None;
let mut setter = None;
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(&[
("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)),
("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"),
}),
("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))),
])
}
}
impl Literal for ast::ImportStatic {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("kind", &|a| a.str("static")),
("name", &|a| a.str(self.js_name.as_ref())),
("shim", &|a| a.str(self.shim.as_ref())),
])
}
}
impl Literal for ast::ImportType {
fn literal(&self, a: &mut LiteralBuilder) {
a.fields(&[
("kind", &|a| a.str("type")),
])
}
}