Overhaul how type information gets to the CLI

This commit is a complete overhaul of how the `#[wasm_bindgen]` macro
communicates type information to the CLI tool, and it's done in a somewhat...
unconventional fashion.

Today we've got a problem where the generated JS needs to understand the types
of each function exported or imported. This understanding is what enables it to
generate the appropriate JS wrappers and such. We want to, however, be quite
flexible and extensible in types that are supported across the boundary, which
means that internally we rely on the trait system to resolve what's what.

Communicating the type information historically was done by creating a four byte
"descriptor" and using associated type projections to communicate that to the
CLI tool. Unfortunately four bytes isn't a lot of space to cram information like
arguments to a generic function, tuple types, etc. In general this just wasn't
flexible enough and the way custom references were treated was also already a
bit of a hack.

This commit takes a radical step of creating a **descriptor function** for each
function imported/exported. The really crazy part is that the `wasm-bindgen` CLI
tool now embeds a wasm interpreter and executes these functions when the CLI
tool is invoked. By allowing arbitrary functions to get executed it's now *much*
easier to inform `wasm-bindgen` about complicated structures of types. Rest
assured though that all these descriptor functions are automatically unexported
and gc'd away, so this should not have any impact on binary sizes

A new internal trait, `WasmDescribe`, is added to represent a description of all
types, sort of like a serialization of the structure of a type that
`wasm-bindgen` can understand. This works by calling a special exported function
with a `u32` value a bunch of times. This means that when we run a descriptor we
effectively get a `Vec<u32>` in the `wasm-bindgen` CLI tool. This list of
integers can then be parsed into a rich `enum` for the JS generation to work
with.

This commit currently only retains feature parity with the previous
implementation. I hope to soon solve issues like #123, #104, and #111 with this
support.
This commit is contained in:
Alex Crichton
2018-04-13 07:33:46 -07:00
parent eb9a6524b9
commit 3305621012
15 changed files with 1192 additions and 921 deletions

View File

@ -15,3 +15,4 @@ quote = '0.5'
proc-macro2 = { version = "0.3", features = ["nightly"] }
wasm-bindgen-shared = { path = "../shared", version = "=0.2.2" }
syn = { version = '0.13', features = ['full'] }
serde_json = "1.0"

View File

@ -1,5 +1,3 @@
use literal::{self, Literal};
use proc_macro2::Span;
use quote::{ToTokens, Tokens};
use shared;
use syn;
@ -404,22 +402,14 @@ impl Program {
})
}
pub fn literal(&self, dst: &mut Tokens) -> usize {
let mut tmp = Tokens::new();
let cnt = {
let mut a = literal::LiteralBuilder::new(&mut tmp);
Literal::literal(self, &mut a);
a.finish()
};
let cnt = cnt as u32;
(quote! {
(#cnt >> 0) as u8,
(#cnt >> 8) as u8,
(#cnt >> 16) as u8,
(#cnt >> 24) as u8
}).to_tokens(dst);
tmp.to_tokens(dst);
(cnt as usize) + 4
pub fn shared(&self) -> shared::Program {
shared::Program {
exports: self.exports.iter().map(|a| a.shared()).collect(),
enums: self.enums.iter().map(|a| a.shared()).collect(),
imports: self.imports.iter().map(|a| a.shared()).collect(),
version: shared::version(),
schema_version: shared::SCHEMA_VERSION.to_string(),
}
}
}
@ -511,6 +501,12 @@ impl Function {
mutable,
)
}
fn shared(&self) -> shared::Function {
shared::Function {
name: self.name.as_ref().to_string(),
}
}
}
pub fn extract_path_ident(path: &syn::Path) -> Option<syn::Ident> {
@ -539,14 +535,59 @@ impl Export {
syn::Ident::from(generated_name)
}
pub fn export_name(&self) -> syn::LitStr {
let name = match self.class {
pub fn export_name(&self) -> String {
match self.class {
Some(class) => {
shared::struct_function_export_name(class.as_ref(), self.function.name.as_ref())
}
None => shared::free_function_export_name(self.function.name.as_ref()),
};
syn::LitStr::new(&name, Span::call_site())
}
}
fn shared(&self) -> shared::Export {
shared::Export {
class: self.class.map(|s| s.as_ref().to_string()),
method: self.method,
function: self.function.shared(),
}
}
}
impl Enum {
fn shared(&self) -> shared::Enum {
shared::Enum {
name: self.name.as_ref().to_string(),
variants: self.variants.iter().map(|v| v.shared()).collect(),
}
}
}
impl Variant {
fn shared(&self) -> shared::EnumVariant {
shared::EnumVariant {
name: self.name.as_ref().to_string(),
value: self.value,
}
}
}
impl Import {
fn shared(&self) -> shared::Import {
shared::Import {
module: self.module.clone(),
js_namespace: self.js_namespace.map(|s| s.as_ref().to_string()),
kind: self.kind.shared(),
}
}
}
impl ImportKind {
fn shared(&self) -> shared::ImportKind {
match *self {
ImportKind::Function(ref f) => shared::ImportKind::Function(f.shared()),
ImportKind::Static(ref f) => shared::ImportKind::Static(f.shared()),
ImportKind::Type(ref f) => shared::ImportKind::Type(f.shared()),
}
}
}
@ -560,6 +601,60 @@ impl ImportFunction {
assert!(name.starts_with("set_"), "setters must start with `set_`");
name[4..].to_string()
}
fn shared(&self) -> shared::ImportFunction {
let mut method = false;
let mut js_new = false;
let mut class_name = None;
match self.kind {
ImportFunctionKind::Method { ref class, .. } => {
method = true;
class_name = Some(class);
}
ImportFunctionKind::JsConstructor { ref class, .. } => {
js_new = true;
class_name = Some(class);
}
ImportFunctionKind::Normal => {}
}
let mut getter = None;
let mut setter = None;
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()));
}
shared::ImportFunction {
shim: self.shim.as_ref().to_string(),
catch: self.function.opts.catch(),
method,
js_new,
structural: self.function.opts.structural(),
getter,
setter,
class: class_name.cloned(),
function: self.function.shared(),
}
}
}
impl ImportStatic {
fn shared(&self) -> shared::ImportStatic {
shared::ImportStatic {
name: self.js_name.as_ref().to_string(),
shim: self.shim.as_ref().to_string(),
}
}
}
impl ImportType {
fn shared(&self) -> shared::ImportType {
shared::ImportType { }
}
}
impl Struct {

View File

@ -4,8 +4,9 @@ use std::env;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use ast;
use proc_macro2::Span;
use quote::{ToTokens, Tokens};
use proc_macro2::Literal;
use serde_json;
use shared;
use syn;
@ -50,6 +51,7 @@ impl ToTokens for ast::Program {
}
_ => i.kind.to_tokens(tokens),
}
DescribeImport(&i.kind).to_tokens(tokens);
}
for e in self.enums.iter() {
e.to_tokens(tokens);
@ -73,14 +75,26 @@ 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.literal(&mut generated_static_value);
let description = serde_json::to_string(&self.shared()).unwrap();
// Each JSON blob is prepended with the length of the JSON blob so when
// all these sections are concatenated in the final wasm file we know
// how to extract all the JSON pieces, so insert the byte length here.
let generated_static_length = description.len() + 4;
let mut bytes = vec![
(description.len() >> 0) as u8,
(description.len() >> 8) as u8,
(description.len() >> 16) as u8,
(description.len() >> 24) as u8,
];
bytes.extend_from_slice(description.as_bytes());
let generated_static_value = syn::LitByteStr::new(&bytes, Span::call_site());
(quote! {
#[allow(non_upper_case_globals)]
#[wasm_custom_section = "__wasm_bindgen_unstable"]
const #generated_static_name: [u8; #generated_static_length] =
[#generated_static_value];
*#generated_static_value;
}).to_tokens(tokens);
}
}
@ -88,18 +102,22 @@ impl ToTokens for ast::Program {
impl ToTokens for ast::Struct {
fn to_tokens(&self, tokens: &mut Tokens) {
let name = &self.name;
let name_len = name.as_ref().len() as u32;
let name_chars = name.as_ref().chars().map(|c| c as u32);
let new_fn = syn::Ident::from(shared::new_function(self.name.as_ref()));
let free_fn = syn::Ident::from(shared::free_function(self.name.as_ref()));
let c = shared::name_to_descriptor(name.as_ref());
let descriptor = Literal::byte_string(format!("{:4}", c).as_bytes());
let borrowed_descriptor = Literal::byte_string(format!("{:4}", c + 1).as_bytes());
(quote! {
impl ::wasm_bindgen::describe::WasmDescribe for #name {
fn describe() {
use wasm_bindgen::describe::*;
inform(RUST_STRUCT);
inform(#name_len);
#(inform(#name_chars);)*
}
}
impl ::wasm_bindgen::convert::WasmBoundary for #name {
type Abi = u32;
const DESCRIPTOR: ::wasm_bindgen::convert::Descriptor =
::wasm_bindgen::convert::Descriptor {
__x: *#descriptor
};
fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack)
-> u32
@ -120,10 +138,6 @@ impl ToTokens for ast::Struct {
impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name {
type Abi = u32;
const DESCRIPTOR: ::wasm_bindgen::convert::Descriptor =
::wasm_bindgen::convert::Descriptor {
__x: *#borrowed_descriptor
};
type RefAnchor = ::wasm_bindgen::__rt::Ref<'static, #name>;
unsafe fn from_abi_ref(
@ -138,10 +152,6 @@ impl ToTokens for ast::Struct {
impl ::wasm_bindgen::convert::FromRefMutWasmBoundary for #name {
type Abi = u32;
const DESCRIPTOR: ::wasm_bindgen::convert::Descriptor =
::wasm_bindgen::convert::Descriptor {
__x: *#borrowed_descriptor
};
type RefAnchor = ::wasm_bindgen::__rt::RefMut<'static, #name>;
unsafe fn from_abi_ref_mut(
@ -273,6 +283,15 @@ impl ToTokens for ast::Export {
convert_ret = quote!{};
}
}
let describe_ret = match self.function.ret {
Some(ast::Type { ref ty, .. }) => {
quote! {
inform(1);
<#ty as WasmDescribe>::describe();
}
}
None => quote! { inform(0); },
};
let name = self.function.name;
let receiver = match self.class {
@ -286,6 +305,10 @@ impl ToTokens for ast::Export {
Some(class) => quote! { #class::#name },
None => quote!{ #name },
};
let descriptor_name = format!("__wbindgen_describe_{}", export_name);
let descriptor_name = syn::Ident::from(descriptor_name);
let nargs = self.function.arguments.len() as u32;
let argtys = self.function.arguments.iter();
let tokens = quote! {
#[export_name = #export_name]
@ -301,11 +324,46 @@ impl ToTokens for ast::Export {
};
#convert_ret
}
// In addition to generating the shim function above which is what
// our generated JS will invoke, we *also* generate a "descriptor"
// shim. This descriptor shim uses the `WasmDescribe` trait to
// programmatically describe the type signature of the generated
// shim above. This in turn is then used to inform the
// `wasm-bindgen` CLI tool exactly what types and such it should be
// using in JS.
//
// Note that this descriptor function is a purely an internal detail
// of `#[wasm_bindgen]` and isn't intended to be exported to anyone
// or actually part of the final was binary. Additionally, this is
// literally executed when the `wasm-bindgen` tool executes.
//
// In any case, there's complications in `wasm-bindgen` to handle
// this, but the tl;dr; is that this is stripped from the final wasm
// binary along with anything it references.
#[no_mangle]
pub extern fn #descriptor_name() {
use wasm_bindgen::describe::*;
inform(FUNCTION);
inform(#nargs);
#(<#argtys as WasmDescribe>::describe();)*
#describe_ret
}
};
tokens.to_tokens(into);
}
}
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::ImportType {
fn to_tokens(&self, tokens: &mut Tokens) {
let vis = &self.vis;
@ -316,12 +374,15 @@ impl ToTokens for ast::ImportType {
obj: ::wasm_bindgen::JsValue,
}
impl ::wasm_bindgen::describe::WasmDescribe for #name {
fn describe() {
::wasm_bindgen::JsValue::describe();
}
}
impl ::wasm_bindgen::convert::WasmBoundary for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::WasmBoundary>::Abi;
const DESCRIPTOR: ::wasm_bindgen::convert::Descriptor =
<::wasm_bindgen::JsValue as ::wasm_bindgen::convert::WasmBoundary>
::DESCRIPTOR;
fn into_abi(self, extra: &mut ::wasm_bindgen::convert::Stack) -> Self::Abi {
self.obj.into_abi(extra)
@ -338,9 +399,6 @@ impl ToTokens for ast::ImportType {
impl ::wasm_bindgen::convert::ToRefWasmBoundary for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::ToRefWasmBoundary>::Abi;
const DESCRIPTOR: ::wasm_bindgen::convert::Descriptor =
<::wasm_bindgen::JsValue as ::wasm_bindgen::convert::ToRefWasmBoundary>
::DESCRIPTOR;
fn to_abi_ref(&self, extra: &mut ::wasm_bindgen::convert::Stack) -> u32 {
self.obj.to_abi_ref(extra)
@ -350,9 +408,6 @@ impl ToTokens for ast::ImportType {
impl ::wasm_bindgen::convert::FromRefWasmBoundary for #name {
type Abi = <::wasm_bindgen::JsValue as
::wasm_bindgen::convert::ToRefWasmBoundary>::Abi;
const DESCRIPTOR: ::wasm_bindgen::convert::Descriptor =
<::wasm_bindgen::JsValue as ::wasm_bindgen::convert::ToRefWasmBoundary>
::DESCRIPTOR;
type RefAnchor = ::std::mem::ManuallyDrop<#name>;
unsafe fn from_abi_ref(
@ -381,16 +436,6 @@ impl ToTokens for ast::ImportType {
}
}
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;
@ -552,6 +597,7 @@ impl ToTokens for ast::ImportFunction {
#convert_ret
}
}
};
if let Some(class) = class_ty {
@ -566,36 +612,52 @@ impl ToTokens for ast::ImportFunction {
}
}
// See comment above in ast::Export for what's going on here.
struct DescribeImport<'a>(&'a ast::ImportKind);
impl<'a> ToTokens for DescribeImport<'a> {
fn to_tokens(&self, tokens: &mut Tokens) {
let f = match *self.0 {
ast::ImportKind::Function(ref f) => f,
ast::ImportKind::Static(_) => return,
ast::ImportKind::Type(_) => return,
};
let describe_name = format!("__wbindgen_describe_{}", f.shim);
let describe_name = syn::Ident::from(describe_name);
let argtys = f.function.arguments.iter();
let nargs = f.function.arguments.len() as u32;
let inform_ret = match f.function.ret {
Some(ref t) => quote! { inform(1); <#t as WasmDescribe>::describe(); },
None => quote! { inform(0); },
};
(quote! {
#[no_mangle]
#[allow(non_snake_case)]
pub extern fn #describe_name() {
use wasm_bindgen::describe::*;
inform(FUNCTION);
inform(#nargs);
#(<#argtys as WasmDescribe>::describe();)*
#inform_ret
}
}).to_tokens(tokens);
}
}
impl ToTokens for ast::Enum {
fn to_tokens(&self, into: &mut Tokens) {
let enum_name = &self.name;
let descriptor = format!("{:4}", shared::TYPE_ENUM);
let descriptor = Literal::byte_string(descriptor.as_bytes());
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.name;
quote! {
if #incoming_u32 == #enum_name::#variant_name as u32 {
if js == #enum_name::#variant_name as u32 {
#enum_name::#variant_name
}
}
});
(quote! {
impl #enum_name {
fn from_u32(#incoming_u32: u32) -> #enum_name {
#(#cast_clauses else)* {
wasm_bindgen::throw(&format!("Could not cast {} as {}", #incoming_u32, #enum_name_as_string));
}
}
}
impl ::wasm_bindgen::convert::WasmBoundary for #enum_name {
type Abi = u32;
const DESCRIPTOR: ::wasm_bindgen::convert::Descriptor =
::wasm_bindgen::convert::Descriptor {
__x: *#descriptor,
};
fn into_abi(self, _extra: &mut ::wasm_bindgen::convert::Stack) -> u32 {
self as u32
@ -605,7 +667,16 @@ impl ToTokens for ast::Enum {
js: u32,
_extra: &mut ::wasm_bindgen::convert::Stack,
) -> Self {
#enum_name::from_u32(js)
#(#cast_clauses else)* {
wasm_bindgen::throw("invalid enum value passed")
}
}
}
impl ::wasm_bindgen::describe::WasmDescribe for #enum_name {
fn describe() {
use wasm_bindgen::describe::*;
inform(ENUM);
}
}
}).to_tokens(into);
@ -641,3 +712,19 @@ impl ToTokens for ast::ImportStatic {
}).to_tokens(into);
}
}
impl ToTokens for ast::Type {
fn to_tokens(&self, into: &mut Tokens) {
match self.kind {
ast::TypeKind::ByValue => {}
ast::TypeKind::ByRef => {
syn::token::And::default().to_tokens(into);
}
ast::TypeKind::ByMutRef => {
syn::token::And::default().to_tokens(into);
syn::token::Mut::default().to_tokens(into);
}
}
self.ty.to_tokens(into);
}
}

View File

@ -5,9 +5,9 @@ extern crate proc_macro2;
extern crate quote;
#[macro_use]
extern crate syn;
extern crate serde_json;
extern crate wasm_bindgen_shared as shared;
pub mod ast;
mod codegen;
mod literal;

View File

@ -1,296 +0,0 @@
use ast;
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 append(&mut self, s: &str) {
for &b in s.as_bytes() {
self.byte(b);
}
}
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 u32(&mut self, s: u32) {
self.append(&s.to_string())
}
fn as_char(&mut self, tokens: Tokens) {
(quote! {
,(#tokens).__x[0]
,(#tokens).__x[1]
,(#tokens).__x[2]
,(#tokens).__x[3]
}).to_tokens(self.dst);
self.cnt += 4;
}
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.u32(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) {
let t = &self.ty;
match self.kind {
ast::TypeKind::ByValue => {
a.as_char(quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR
});
}
ast::TypeKind::ByRef |
ast::TypeKind::ByMutRef => {
match self.loc {
ast::TypeLocation::ImportArgument |
ast::TypeLocation::ExportRet => {
a.as_char(quote! {
<#t as ::wasm_bindgen::convert::ToRefWasmBoundary>
::DESCRIPTOR
});
}
ast::TypeLocation::ImportRet |
ast::TypeLocation::ExportArgument => {
a.as_char(quote! {
<#t as ::wasm_bindgen::convert::FromRefWasmBoundary>
::DESCRIPTOR
});
}
}
}
}
}
}
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"))])
}
}