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

View File

@ -16,3 +16,4 @@ parity-wasm = "0.27"
serde_json = "1.0"
wasm-bindgen-shared = { path = "../shared", version = '=0.2.2' }
wasm-gc-api = "0.1"
wasmi = "0.1"

View File

@ -0,0 +1,285 @@
use std::char;
macro_rules! tys {
($($a:ident)*) => (tys! { @ ($($a)*) 0 });
(@ () $v:expr) => {};
(@ ($a:ident $($b:ident)*) $v:expr) => {
const $a: u32 = $v;
tys!(@ ($($b)*) $v+1);
}
}
// NB: this list must be kept in sync with `src/describe.rs`
tys! {
I8
U8
I16
U16
I32
U32
I64
U64
F32
F64
BOOLEAN
FUNCTION
CLOSURE
STRING
REF
REFMUT
SLICE
VECTOR
ANYREF
ENUM
RUST_STRUCT
}
#[derive(Debug)]
pub enum Descriptor {
I8,
U8,
I16,
U16,
I32,
U32,
I64,
U64,
F32,
F64,
Boolean,
Function(Box<Function>),
Closure(Box<Function>),
Ref(Box<Descriptor>),
RefMut(Box<Descriptor>),
Slice(Box<Descriptor>),
Vector(Box<Descriptor>),
String,
Anyref,
Enum,
RustStruct(String),
}
#[derive(Debug)]
pub struct Function {
pub arguments: Vec<Descriptor>,
pub ret: Option<Descriptor>,
}
#[derive(Copy, Clone)]
pub enum VectorKind {
I8,
U8,
I16,
U16,
I32,
U32,
F32,
F64,
String,
Anyref,
}
impl Descriptor {
pub fn decode(mut data: &[u32]) -> Descriptor {
let descriptor = Descriptor::_decode(&mut data);
assert!(data.is_empty());
descriptor
}
fn _decode(data: &mut &[u32]) -> Descriptor {
match get(data) {
I8 => Descriptor::I8,
I16 => Descriptor::I16,
I32 => Descriptor::I32,
I64 => Descriptor::I64,
U8 => Descriptor::U8,
U16 => Descriptor::U16,
U32 => Descriptor::U32,
U64 => Descriptor::U64,
F32 => Descriptor::F32,
F64 => Descriptor::F64,
BOOLEAN => Descriptor::Boolean,
FUNCTION => Descriptor::Function(Box::new(Function::decode(data))),
CLOSURE => {
assert_eq!(get(data), FUNCTION);
Descriptor::Closure(Box::new(Function::decode(data)))
}
REF => Descriptor::Ref(Box::new(Descriptor::_decode(data))),
REFMUT => Descriptor::RefMut(Box::new(Descriptor::_decode(data))),
SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data))),
VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data))),
STRING => Descriptor::String,
ANYREF => Descriptor::Anyref,
ENUM => Descriptor::Enum,
RUST_STRUCT => {
let name = (0..get(data))
.map(|_| char::from_u32(get(data)).unwrap())
.collect();
Descriptor::RustStruct(name)
}
other => panic!("unknown descriptor: {}", other),
}
}
pub fn unwrap_function(&self) -> &Function {
match *self {
Descriptor::Function(ref f) => f,
_ => panic!("not a function"),
}
}
pub fn is_number(&self) -> bool {
match *self {
Descriptor::I8 |
Descriptor::U8 |
Descriptor::I16 |
Descriptor::U16 |
Descriptor::I32 |
Descriptor::U32 |
Descriptor::I64 |
Descriptor::U64 |
Descriptor::F32 |
Descriptor::F64 |
Descriptor::Enum => true,
_ => return false,
}
}
pub fn is_ref_anyref(&self) -> bool {
match *self {
Descriptor::Ref(ref s) => s.is_anyref(),
_ => return false,
}
}
pub fn ref_closure(&self) -> Option<&Function> {
match *self {
Descriptor::Ref(ref s) => s.closure(),
_ => None,
}
}
pub fn closure(&self) -> Option<&Function> {
match *self {
Descriptor::Closure(ref s) => Some(s),
_ => None,
}
}
pub fn is_anyref(&self) -> bool {
match *self {
Descriptor::Anyref => true,
_ => false,
}
}
pub fn vector_kind(&self) -> Option<VectorKind> {
let inner = match *self {
Descriptor::String => return Some(VectorKind::String),
Descriptor::Vector(ref d) => &**d,
Descriptor::Ref(ref d) => {
match **d {
Descriptor::Slice(ref d) => &**d,
Descriptor::String => return Some(VectorKind::String),
_ => return None,
}
}
_ => return None,
};
match *inner {
Descriptor::I8 => Some(VectorKind::I8),
Descriptor::I16 => Some(VectorKind::I16),
Descriptor::I32 => Some(VectorKind::I32),
Descriptor::U8 => Some(VectorKind::U8),
Descriptor::U16 => Some(VectorKind::U16),
Descriptor::U32 => Some(VectorKind::U32),
Descriptor::F32 => Some(VectorKind::F32),
Descriptor::F64 => Some(VectorKind::F64),
Descriptor::Anyref => Some(VectorKind::Anyref),
_ => None
}
}
pub fn rust_struct(&self) -> Option<&str> {
let inner = match *self {
Descriptor::Ref(ref d) => &**d,
Descriptor::RefMut(ref d) => &**d,
ref d => d,
};
match *inner {
Descriptor::RustStruct(ref s) => Some(s),
_ => None,
}
}
pub fn stack_closure(&self) -> Option<&Function> {
let inner = match *self {
Descriptor::Ref(ref d) => &**d,
_ => return None,
};
match *inner {
Descriptor::Function(ref f) => Some(f),
_ => None,
}
}
pub fn is_by_ref(&self) -> bool {
match *self {
Descriptor::Ref(_) |
Descriptor::RefMut(_) => true,
_ => false,
}
}
}
fn get(a: &mut &[u32]) -> u32 {
let ret = a[0];
*a = &a[1..];
ret
}
impl Function {
fn decode(data: &mut &[u32]) -> Function {
let arguments = (0..get(data))
.map(|_| Descriptor::_decode(data))
.collect::<Vec<_>>();
let ret = if get(data) == 0 {
None
} else {
Some(Descriptor::_decode(data))
};
Function { arguments, ret }
}
}
impl VectorKind {
pub fn js_ty(&self) -> &str {
match *self {
VectorKind::String => "string",
VectorKind::I8 => "Int8Array",
VectorKind::U8 => "Uint8Array",
VectorKind::I16 => "Int16Array",
VectorKind::U16 => "Uint16Array",
VectorKind::I32 => "Int32Array",
VectorKind::U32 => "Uint32Array",
VectorKind::F32 => "Float32Array",
VectorKind::F64 => "Float64Array",
VectorKind::Anyref => "any[]",
}
}
pub fn size(&self) -> usize {
match *self {
VectorKind::String => 1,
VectorKind::I8 => 1,
VectorKind::U8 => 1,
VectorKind::I16 => 2,
VectorKind::U16 => 2,
VectorKind::I32 => 4,
VectorKind::U32 => 4,
VectorKind::F32 => 4,
VectorKind::F64 => 8,
VectorKind::Anyref => 4,
}
}
}

View File

@ -8,6 +8,7 @@ use shared;
use wasm_gc;
use super::Bindgen;
use descriptor::{Descriptor, VectorKind};
pub struct Context<'a> {
pub globals: String,
@ -18,10 +19,10 @@ pub struct Context<'a> {
pub required_internal_exports: HashSet<&'static str>,
pub config: &'a Bindgen,
pub module: &'a mut Module,
pub custom_type_names: HashMap<u32, String>,
pub imported_names: HashSet<String>,
pub exported_classes: HashMap<String, ExportedClass>,
pub function_table_needed: bool,
pub run_descriptor: &'a Fn(&str) -> Vec<u32>,
}
#[derive(Default)]
@ -36,16 +37,6 @@ pub struct SubContext<'a, 'b: 'a> {
}
impl<'a> Context<'a> {
pub fn add_custom_type_names(&mut self, program: &shared::Program) {
for custom in program.custom_type_names.iter() {
let prev = self.custom_type_names.insert(custom.descriptor,
custom.name.clone());
if let Some(prev) = prev {
assert_eq!(prev, custom.name);
}
}
}
fn export(&mut self, name: &str, contents: &str) {
let contents = contents.trim();
let global = if self.config.nodejs {
@ -794,16 +785,16 @@ impl<'a> Context<'a> {
self.expose_get_array_u32_from_wasm();
self.expose_get_object();
self.globals.push_str(&format!("
function getArrayJsValueFromWasm(ptr, len) {{
const mem = getUint32Memory();
const slice = mem.slice(ptr / 4, ptr / 4 + len);
const result = []
for (ptr in slice) {{
result.push(getObject(ptr))
}}
return result;
function getArrayJsValueFromWasm(ptr, len) {{
const mem = getUint32Memory();
const slice = mem.slice(ptr / 4, ptr / 4 + len);
const result = [];
for (ptr in slice) {{
result.push(getObject(ptr))
}}
"));
return result;
}}
"));
}
fn expose_get_array_i8_from_wasm(&mut self) {
@ -1043,26 +1034,24 @@ impl<'a> Context<'a> {
})
}
fn custom_type_name(&self, c: u32) -> &str {
let c = c & !shared::TYPE_CUSTOM_REF_FLAG;
&self.custom_type_names[&c]
}
fn pass_to_wasm_function(&mut self, ty: &VectorType) -> &'static str {
match ty.kind {
fn pass_to_wasm_function(&mut self, t: VectorKind) -> &'static str {
match t {
VectorKind::String => {
self.expose_pass_string_to_wasm();
"passStringToWasm"
}
VectorKind::I8 | VectorKind::U8 => {
VectorKind::I8 |
VectorKind::U8 => {
self.expose_pass_array8_to_wasm();
"passArray8ToWasm"
}
VectorKind::I16 | VectorKind::U16 => {
VectorKind::U16 |
VectorKind::I16 => {
self.expose_pass_array16_to_wasm();
"passArray16ToWasm"
}
VectorKind::I32 | VectorKind::U32 => {
VectorKind::I32 |
VectorKind::U32 => {
self.expose_pass_array32_to_wasm();
"passArray32ToWasm"
}
@ -1074,12 +1063,14 @@ impl<'a> Context<'a> {
self.expose_pass_array_f64_to_wasm();
"passArrayF64ToWasm"
}
VectorKind::JsValue => panic!("Cannot pass Vec<JsValue> to function")
VectorKind::Anyref => {
panic!("cannot pass list of JsValue to wasm yet")
}
}
}
fn expose_get_vector_from_wasm(&mut self, ty: &VectorType) -> &'static str {
match ty.kind {
fn expose_get_vector_from_wasm(&mut self, ty: VectorKind) -> &'static str {
match ty {
VectorKind::String => {
self.expose_get_string_from_wasm();
"getStringFromWasm"
@ -1116,7 +1107,7 @@ impl<'a> Context<'a> {
self.expose_get_array_f64_from_wasm();
"getArrayF64FromWasm"
}
VectorKind::JsValue => {
VectorKind::Anyref => {
self.expose_get_array_js_value_from_wasm();
"getArrayJsValueFromWasm"
}
@ -1200,6 +1191,107 @@ impl<'a> Context<'a> {
.unwrap();
*self.module = deserialize_buffer(&bytes).unwrap();
}
fn describe(&self, name: &str) -> Descriptor {
let name = format!("__wbindgen_describe_{}", name);
let ret = (self.run_descriptor)(&name);
Descriptor::decode(&ret)
}
fn return_from_rust(&mut self, ty: &Option<Descriptor>, dst_ts: &mut String)
-> String
{
let ty = match *ty {
Some(ref t) => t,
None => {
dst_ts.push_str(": void");
return format!("return ret;")
}
};
if ty.is_ref_anyref() {
dst_ts.push_str(": any");
self.expose_get_object();
return format!("return getObject(ret);")
}
if ty.is_by_ref() {
panic!("cannot return references from Rust to JS yet")
}
if let Some(ty) = ty.vector_kind() {
dst_ts.push_str(": ");
dst_ts.push_str(ty.js_ty());
let f = self.expose_get_vector_from_wasm(ty);
self.expose_get_global_argument();
self.required_internal_exports.insert("__wbindgen_free");
return format!("
const len = getGlobalArgument(0);
const realRet = {}(ret, len);
wasm.__wbindgen_free(ret, len * {});
return realRet;
", f, ty.size())
}
if let Some(name) = ty.rust_struct() {
dst_ts.push_str(": ");
dst_ts.push_str(name);
return if self.config.debug {
format!("return new {name}(ret, token);", name = name)
} else {
format!("return new {name}(ret);", name = name)
}
}
if ty.is_number() {
dst_ts.push_str(": number");
return format!("return ret;")
}
match *ty {
Descriptor::Boolean => {
dst_ts.push_str(": boolean");
format!("return ret !== 0;")
}
Descriptor::Anyref => {
dst_ts.push_str(": any");
self.expose_take_object();
format!("return takeObject(ret);")
}
_ => panic!("unsupported return from Rust to JS {:?}", ty),
}
}
fn return_from_js(&mut self, ty: &Option<Descriptor>, invoc: &str) -> String {
let ty = match *ty {
Some(ref t) => t,
None => return invoc.to_string(),
};
if ty.is_by_ref() {
panic!("cannot return a reference from JS to Rust")
}
if let Some(ty) = ty.vector_kind() {
let f = self.pass_to_wasm_function(ty);
self.expose_uint32_memory();
self.expose_set_global_argument();
return format!("
const [retptr, retlen] = {}({});
setGlobalArgument(retlen, 0);
return retptr;
", f, invoc)
}
if ty.is_number() {
return format!("return {};", invoc)
}
match *ty {
Descriptor::Boolean => format!("return {} ? 1 : 0;", invoc),
Descriptor::Anyref => {
self.expose_add_heap_object();
format!("return addHeapObject({});", invoc)
}
_ => panic!("unimplemented return from JS to Rust: {:?}", ty),
}
}
}
impl<'a, 'b> SubContext<'a, 'b> {
@ -1255,6 +1347,8 @@ impl<'a, 'b> SubContext<'a, 'b> {
wasm_name: &str,
is_method: bool,
function: &shared::Function) -> (String, String) {
let descriptor = self.cx.describe(wasm_name);
let desc_function = descriptor.unwrap_function();
let mut dst = String::from("(");
let mut dst_ts = format!("{}(", function.name);
let mut passed_args = String::new();
@ -1266,7 +1360,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
}
let mut global_idx = 0;
for (i, arg) in function.arguments.iter().enumerate() {
for (i, arg) in desc_function.arguments.iter().enumerate() {
let name = format!("arg{}", i);
if i > 0 {
dst.push_str(", ");
@ -1281,163 +1375,97 @@ impl<'a, 'b> SubContext<'a, 'b> {
}
passed_args.push_str(arg);
};
if let Some(kind) = arg.vector_kind() {
dst_ts.push_str(": ");
dst_ts.push_str(kind.js_ty());
let func = self.cx.pass_to_wasm_function(kind);
self.cx.expose_set_global_argument();
arg_conversions.push_str(&format!("\
const [ptr{i}, len{i}] = {func}({arg});
setGlobalArgument(len{i}, {global_idx});
", i = i, func = func, arg = name, global_idx = global_idx));
global_idx += 1;
pass(&format!("ptr{}", i));
if arg.is_by_ref() {
destructors.push_str(&format!("\n\
wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\
", i = i, size = kind.size()));
self.cx.required_internal_exports.insert(
"__wbindgen_free",
);
}
continue
}
if let Some(s) = arg.rust_struct() {
dst_ts.push_str(&format!(": {}", s));
if self.cx.config.debug {
self.cx.expose_assert_class();
arg_conversions.push_str(&format!("\
_assertClass({arg}, {struct_});
", arg = name, struct_ = s));
}
if arg.is_by_ref() {
pass(&format!("{}.ptr", name));
} else {
arg_conversions.push_str(&format!("\
const ptr{i} = {arg}.ptr;
{arg}.ptr = 0;
", i = i, arg = name));
pass(&format!("ptr{}", i));
}
continue
}
match *arg {
shared::TYPE_ENUM | shared::TYPE_NUMBER => {
ref d if d.is_number() => {
dst_ts.push_str(": number");
if self.cx.config.debug {
self.cx.expose_assert_num();
arg_conversions.push_str(&format!("_assertNum({});\n", name));
}
pass(&name)
pass(&name);
continue
}
shared::TYPE_BOOLEAN => {
Descriptor::Boolean => {
dst_ts.push_str(": boolean");
if self.cx.config.debug {
self.cx.expose_assert_bool();
arg_conversions.push_str(&format!("\
_assertBoolean({name});
", name = name));
} else {}
pass(&format!("arg{i} ? 1 : 0", i = i))
}
pass(&format!("arg{i} ? 1 : 0", i = i));
continue
}
shared::TYPE_JS_OWNED => {
Descriptor::Anyref => {
dst_ts.push_str(": any");
self.cx.expose_add_heap_object();
arg_conversions.push_str(&format!("\
const idx{i} = addHeapObject({arg});
", i = i, arg = name));
pass(&format!("idx{}", i));
pass(&format!("addHeapObject({})", name));
continue
}
shared::TYPE_JS_REF => {
ref r if r.is_ref_anyref() => {
dst_ts.push_str(": any");
self.cx.expose_borrowed_objects();
arg_conversions.push_str(&format!("\
const idx{i} = addBorrowedObject({arg});
", i = i, arg = name));
destructors.push_str("stack.pop();\n");
pass(&format!("idx{}", i));
}
other => {
match VectorType::from(other) {
Some(ty) => {
dst_ts.push_str(": ");
dst_ts.push_str(ty.js_ty());
let func = self.cx.pass_to_wasm_function(&ty);
self.cx.expose_set_global_argument();
arg_conversions.push_str(&format!("\
const [ptr{i}, len{i}] = {func}({arg});
setGlobalArgument(len{i}, {global_idx});
", i = i, func = func, arg = name, global_idx = global_idx));
global_idx += 1;
pass(&format!("ptr{}", i));
if !ty.owned {
destructors.push_str(&format!("\n\
wasm.__wbindgen_free(ptr{i}, len{i} * {size});\n\
", i = i, size = ty.size()));
self.cx.required_internal_exports.insert(
"__wbindgen_free",
);
}
}
None => {
let s = self.cx.custom_type_name(other).to_string();
dst_ts.push_str(&format!(": {}", s));
if self.cx.config.debug {
self.cx.expose_assert_class();
arg_conversions.push_str(&format!("\
_assertClass({arg}, {struct_});
", arg = name, struct_ = s));
}
if other & shared::TYPE_CUSTOM_REF_FLAG != 0 {
pass(&format!("{}.ptr", name));
} else {
arg_conversions.push_str(&format!("\
const ptr{i} = {arg}.ptr;
{arg}.ptr = 0;
", i = i, arg = name));
pass(&format!("ptr{}", i));
}
}
}
pass(&format!("addBorrowedObject({})", name));
continue
}
_ => {}
}
panic!("unsupported argument to rust function {:?}", arg)
}
dst.push_str(")");
dst_ts.push_str(")");
let convert_ret = match function.ret {
None => {
dst_ts.push_str(": void");
format!("return ret;")
}
Some(shared::TYPE_ENUM) => {
dst_ts.push_str(": number");
format!("return ret;")
}
Some(shared::TYPE_NUMBER) => {
dst_ts.push_str(": number");
format!("return ret;")
}
Some(shared::TYPE_BOOLEAN) => {
dst_ts.push_str(": boolean");
format!("return ret !== 0;")
}
Some(shared::TYPE_JS_OWNED) => {
dst_ts.push_str(": any");
self.cx.expose_take_object();
format!("return takeObject(ret);")
}
Some(shared::TYPE_JS_REF) => {
dst_ts.push_str(": any");
self.cx.expose_get_object();
format!("return getObject(ret);")
}
Some(other) => {
match VectorType::from(other) {
Some(ty) => {
if !ty.owned {
panic!("cannot return slices yet");
}
dst_ts.push_str(": ");
dst_ts.push_str(ty.js_ty());
let f = self.cx.expose_get_vector_from_wasm(&ty);
self.cx.expose_get_global_argument();
self.cx.required_internal_exports.insert(
"__wbindgen_free",
);
format!("
const len = getGlobalArgument(0);
const realRet = {}(ret, len);
wasm.__wbindgen_free(ret, len * {});
return realRet;
", f, ty.size())
}
None => {
if other & shared::TYPE_CUSTOM_REF_FLAG != 0 {
panic!("cannot return references yet");
}
let name = self.cx.custom_type_name(other);
dst_ts.push_str(": ");
dst_ts.push_str(name);
if self.cx.config.debug {
format!("\
return new {name}(ret, token);
", name = name)
} else {
format!("\
return new {name}(ret);
", name = name)
}
}
}
}
};
let convert_ret = self.cx.return_from_rust(&desc_function.ret, &mut dst_ts);
dst_ts.push_str(";");
dst.push_str(" {\n ");
dst.push_str(&arg_conversions);
if destructors.len() == 0 {
dst.push_str(&format!("\
const ret = wasm.{}({passed});
const ret = wasm.{f}({passed});
{convert_ret}
",
f = wasm_name,
@ -1491,6 +1519,9 @@ impl<'a, 'b> SubContext<'a, 'b> {
pub fn generate_import_function(&mut self,
info: &shared::Import,
import: &shared::ImportFunction) {
let descriptor = self.cx.describe(&import.shim);
let desc_function = descriptor.unwrap_function();
let mut dst = String::new();
dst.push_str("function(");
@ -1501,89 +1532,86 @@ impl<'a, 'b> SubContext<'a, 'b> {
let mut finally = String::new();
let mut next_global = 0;
for (i, arg) in import.function.arguments.iter().enumerate() {
for (i, arg) in desc_function.arguments.iter().enumerate() {
abi_args.push(format!("arg{}", i));
if let Some(ty) = arg.vector_kind() {
let f = self.cx.expose_get_vector_from_wasm(ty);
self.cx.expose_get_global_argument();
extra.push_str(&format!("
let len{0} = getGlobalArgument({next_global});
let v{0} = {func}(arg{0}, len{0});
", i, func = f, next_global = next_global));
next_global += 1;
if !arg.is_by_ref() {
extra.push_str(&format!("
wasm.__wbindgen_free(arg{0}, len{0} * {size});
", i, size = ty.size()));
self.cx.required_internal_exports.insert(
"__wbindgen_free"
);
}
invoc_args.push(format!("v{}", i));
continue
}
if let Some(s) = arg.rust_struct() {
if arg.is_by_ref() {
panic!("cannot invoke JS functions with custom ref types yet")
}
let assign = if self.cx.config.debug {
format!("let c{0} = new {class}(arg{0}, token);", i, class = s)
} else {
format!("let c{0} = new {class}(arg{0});", i, class = s)
};
extra.push_str(&assign);
invoc_args.push(format!("c{}", i));
continue
}
if let Some(f) = arg.stack_closure() {
let args = (0..f.arguments.len())
.map(|i| format!("arg{}", i))
.collect::<Vec<_>>()
.join(", ");
self.cx.expose_get_global_argument();
self.cx.function_table_needed = true;
let sep = if f.arguments.len() == 0 {""} else {","};
extra.push_str(&format!("
let cb{0} = function({args}) {{
return this.f(this.a, this.b {sep} {args});
}};
cb{0}.f = wasm.__wbg_function_table.get(arg{0});
cb{0}.a = getGlobalArgument({next_global});
cb{0}.b = getGlobalArgument({next_global} + 1);
", i, next_global = next_global, args = args, sep = sep));
next_global += 2;
finally.push_str(&format!("
cb{0}.a = cb{0}.b = 0;
", i));
invoc_args.push(format!("cb{0}.bind(cb{0})", i));
continue
}
if let Some(_f) = arg.ref_closure() {
self.cx.expose_get_object();
invoc_args.push(format!("getObject(arg{})", i));
continue
}
let invoc_arg = match *arg {
shared::TYPE_NUMBER => format!("arg{}", i),
shared::TYPE_BOOLEAN => format!("arg{} !== 0", i),
shared::TYPE_JS_OWNED => {
ref d if d.is_number() => format!("arg{}", i),
Descriptor::Boolean => format!("arg{} !== 0", i),
Descriptor::Anyref => {
self.cx.expose_take_object();
format!("takeObject(arg{})", i)
}
shared::TYPE_JS_REF => {
ref d if d.is_ref_anyref() => {
self.cx.expose_get_object();
format!("getObject(arg{})", i)
}
shared::TYPE_FUNC => {
self.cx.expose_get_object();
format!("getObject(arg{})", i)
}
shared::TYPE_STACK_FUNC0 |
shared::TYPE_STACK_FUNC1 |
shared::TYPE_STACK_FUNC2 |
shared::TYPE_STACK_FUNC3 |
shared::TYPE_STACK_FUNC4 |
shared::TYPE_STACK_FUNC5 |
shared::TYPE_STACK_FUNC6 |
shared::TYPE_STACK_FUNC7 => {
let nargs = *arg - shared::TYPE_STACK_FUNC0;
let args = (0..nargs)
.map(|i| format!("arg{}", i))
.collect::<Vec<_>>()
.join(", ");
self.cx.expose_get_global_argument();
self.cx.function_table_needed = true;
let sep = if nargs == 0 {""} else {","};
extra.push_str(&format!("
let cb{0} = function({args}) {{
return this.f(this.a, this.b {sep} {args});
}};
cb{0}.f = wasm.__wbg_function_table.get(arg{0});
cb{0}.a = getGlobalArgument({next_global});
cb{0}.b = getGlobalArgument({next_global} + 1);
", i, next_global = next_global, args = args, sep = sep));
next_global += 2;
finally.push_str(&format!("
cb{0}.a = cb{0}.b = 0;
", i));
format!("cb{0}.bind(cb{0})", i)
}
other => {
match VectorType::from(other) {
Some(ty) => {
let f = self.cx.expose_get_vector_from_wasm(&ty);
self.cx.expose_get_global_argument();
extra.push_str(&format!("
let len{0} = getGlobalArgument({next_global});
let v{0} = {func}(arg{0}, len{0});
", i, func = f, next_global = next_global));
next_global += 1;
if ty.owned {
extra.push_str(&format!("
wasm.__wbindgen_free(arg{0}, len{0} * {size});
", i, size = ty.size()));
self.cx.required_internal_exports.insert(
"__wbindgen_free"
);
}
format!("v{}", i)
}
None => {
if other & shared::TYPE_CUSTOM_REF_FLAG != 0 {
panic!("cannot import custom ref types yet")
}
let s = self.cx.custom_type_name(other).to_string();
let assign = if self.cx.config.debug {
format!("let c{0} = new {class}(arg{0}, token);", i, class = s)
} else {
format!("let c{0} = new {class}(arg{0});", i, class = s)
};
extra.push_str(&assign);
format!("c{}", i)
}
}
}
_ => panic!("unimplemented argument type in imported function: {:?}", arg),
};
invoc_args.push(invoc_arg);
}
@ -1670,33 +1698,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
}
};
let invoc = format!("{}({})", invoc, invoc_args);
let invoc = match import.function.ret {
Some(shared::TYPE_NUMBER) => format!("return {};", invoc),
Some(shared::TYPE_BOOLEAN) => format!("return {} ? 1 : 0;", invoc),
Some(shared::TYPE_JS_OWNED) => {
self.cx.expose_add_heap_object();
format!("return addHeapObject({});", invoc)
}
Some(other) => {
match VectorType::from(other) {
Some(ty) => {
if !ty.owned {
panic!("cannot return borrowed slices in imports");
}
let f = self.cx.pass_to_wasm_function(&ty);
self.cx.expose_uint32_memory();
self.cx.expose_set_global_argument();
format!("
const [retptr, retlen] = {}({});
setGlobalArgument(retlen, 0);
return retptr;
", f, invoc)
}
None => panic!("unimplemented return type in import"),
}
}
None => invoc,
};
let invoc = self.cx.return_from_js(&desc_function.ret, &invoc);
let invoc = if import.catch {
self.cx.expose_uint32_memory();
@ -1776,119 +1778,3 @@ impl<'a, 'b> SubContext<'a, 'b> {
}
}
}
#[derive(Debug)]
struct VectorType {
owned: bool,
kind: VectorKind,
}
#[derive(Debug)]
enum VectorKind {
String,
I8,
U8,
I16,
U16,
I32,
U32,
F32,
F64,
JsValue,
}
impl VectorType {
fn from(desc: u32) -> Option<VectorType> {
let ty = match desc {
shared::TYPE_BORROWED_STR => {
VectorType { owned: false, kind: VectorKind::String }
}
shared::TYPE_STRING => {
VectorType { owned: true, kind: VectorKind::String }
}
shared::TYPE_VECTOR_U8 => {
VectorType { owned: true, kind: VectorKind::U8 }
}
shared::TYPE_VECTOR_I8 => {
VectorType { owned: true, kind: VectorKind::I8 }
}
shared::TYPE_SLICE_U8 => {
VectorType { owned: false, kind: VectorKind::U8 }
}
shared::TYPE_SLICE_I8 => {
VectorType { owned: false, kind: VectorKind::I8 }
}
shared::TYPE_VECTOR_U16 => {
VectorType { owned: true, kind: VectorKind::U16 }
}
shared::TYPE_VECTOR_I16 => {
VectorType { owned: true, kind: VectorKind::I16 }
}
shared::TYPE_SLICE_U16 => {
VectorType { owned: false, kind: VectorKind::U16 }
}
shared::TYPE_SLICE_I16 => {
VectorType { owned: false, kind: VectorKind::I16 }
}
shared::TYPE_VECTOR_U32 => {
VectorType { owned: true, kind: VectorKind::U32 }
}
shared::TYPE_VECTOR_I32 => {
VectorType { owned: true, kind: VectorKind::I32 }
}
shared::TYPE_SLICE_U32 => {
VectorType { owned: false, kind: VectorKind::U32 }
}
shared::TYPE_SLICE_I32 => {
VectorType { owned: false, kind: VectorKind::I32 }
}
shared::TYPE_VECTOR_F32 => {
VectorType { owned: true, kind: VectorKind::F32 }
}
shared::TYPE_VECTOR_F64 => {
VectorType { owned: true, kind: VectorKind::F64 }
}
shared::TYPE_SLICE_F32 => {
VectorType { owned: false, kind: VectorKind::F32 }
}
shared::TYPE_SLICE_F64 => {
VectorType { owned: false, kind: VectorKind::F64 }
}
shared::TYPE_VECTOR_JSVALUE => {
VectorType { owned: true, kind: VectorKind::JsValue }
}
_ => return None
};
Some(ty)
}
fn js_ty(&self) -> &str {
match self.kind {
VectorKind::String => "string",
VectorKind::I8 => "Int8Array",
VectorKind::U8 => "Uint8Array",
VectorKind::I16 => "Int16Array",
VectorKind::U16 => "Uint16Array",
VectorKind::I32 => "Int32Array",
VectorKind::U32 => "Uint32Array",
VectorKind::F32 => "Float32Array",
VectorKind::F64 => "Float64Array",
VectorKind::JsValue => "any[]",
}
}
fn size(&self) -> usize {
match self.kind {
VectorKind::String => 1,
VectorKind::I8 => 1,
VectorKind::U8 => 1,
VectorKind::I16 => 2,
VectorKind::U16 => 2,
VectorKind::I32 => 4,
VectorKind::U32 => 4,
VectorKind::F32 => 4,
VectorKind::F64 => 8,
VectorKind::JsValue => 4,
}
}
}

View File

@ -2,8 +2,10 @@ extern crate parity_wasm;
extern crate wasm_bindgen_shared as shared;
extern crate serde_json;
extern crate wasm_gc;
extern crate wasmi;
use std::collections::BTreeSet;
use std::fmt;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
@ -11,6 +13,7 @@ use std::path::{Path, PathBuf};
use parity_wasm::elements::*;
mod js;
mod descriptor;
pub mod wasm2es6js;
pub struct Bindgen {
@ -90,11 +93,26 @@ impl Bindgen {
None => panic!("must have a path input for now"),
};
let stem = input.file_stem().unwrap().to_str().unwrap();
let mut module = parity_wasm::deserialize_file(input).map_err(|e| {
Error(format!("{:?}", e))
})?;
let mut module = parity_wasm::deserialize_file(input)?;
let programs = extract_programs(&mut module);
// Here we're actually instantiating the module we've parsed above for
// execution. Why, you might be asking, are we executing wasm code? A
// good question!
//
// Transmitting information from `#[wasm_bindgen]` here to the CLI tool
// is pretty tricky. Specifically information about the types involved
// with a function signature (especially generic ones) can be hefty to
// translate over. As a result, the macro emits a bunch of shims which,
// when executed, will describe to us what the types look like.
//
// This means that whenever we encounter an import or export we'll
// execute a shim function which informs us about its type so we can
// then generate the appropriate bindings.
let instance = wasmi::Module::from_parity_wasm_module(module.clone())?;
let instance = wasmi::ModuleInstance::new(&imodule, &MyResolver)?;
let instance = instance.not_started_instance();
let (js, ts) = {
let mut cx = js::Context {
globals: String::new(),
@ -103,16 +121,20 @@ impl Bindgen {
typescript: format!("/* tslint:disable */\n"),
exposed_globals: Default::default(),
required_internal_exports: Default::default(),
custom_type_names: Default::default(),
imported_names: Default::default(),
exported_classes: Default::default(),
config: &self,
module: &mut module,
function_table_needed: false,
run_descriptor: &|name| {
let mut v = MyExternals(Vec::new());
let ret = imodulei
.invoke_export(name, &[], &mut v)
.expect("failed to run export");
assert!(ret.is_none());
v.0
},
};
for program in programs.iter() {
cx.add_custom_type_names(program);
}
for program in programs.iter() {
js::SubContext {
program,
@ -233,3 +255,102 @@ to open an issue at https://github.com/alexcrichton/wasm-bindgen/issues!
});
return ret
}
struct MyResolver;
impl wasmi::ImportResolver for MyResolver {
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
signature: &wasmi::Signature
) -> Result<wasmi::FuncRef, wasmi::Error> {
// Route our special "describe" export to 1 and everything else to 0.
// That way whenever the function 1 is invoked we know what to do and
// when 0 is invoked (by accident) we'll trap and produce an error.
let idx = (module_name == "__wbindgen_placeholder__" &&
field_name == "__wbindgen_describe") as usize;
Ok(wasmi::FuncInstance::alloc_host(signature.clone(), idx))
}
fn resolve_global(
&self,
_module_name: &str,
_field_name: &str,
descriptor: &wasmi::GlobalDescriptor
) -> Result<wasmi::GlobalRef, wasmi::Error> {
// dummy implementation to ensure instantiation succeeds
let val = match descriptor.value_type() {
wasmi::ValueType::I32 => wasmi::RuntimeValue::I32(0),
wasmi::ValueType::I64 => wasmi::RuntimeValue::I64(0),
wasmi::ValueType::F32 => wasmi::RuntimeValue::F32(0.0),
wasmi::ValueType::F64 => wasmi::RuntimeValue::F64(0.0),
};
Ok(wasmi::GlobalInstance::alloc(val, descriptor.is_mutable()))
}
fn resolve_memory(
&self,
_module_name: &str,
_field_name: &str,
descriptor: &wasmi::MemoryDescriptor,
) -> Result<wasmi::MemoryRef, wasmi::Error> {
// dummy implementation to ensure instantiation succeeds
use wasmi::memory_units::Pages;
let initial = Pages(descriptor.initial() as usize);
let maximum = descriptor.maximum().map(|i| Pages(i as usize));
wasmi::MemoryInstance::alloc(initial, maximum)
}
fn resolve_table(
&self,
_module_name: &str,
_field_name: &str,
descriptor: &wasmi::TableDescriptor
) -> Result<wasmi::TableRef, wasmi::Error> {
// dummy implementation to ensure instantiation succeeds
let initial = descriptor.initial();
let maximum = descriptor.maximum();
wasmi::TableInstance::alloc(initial, maximum)
}
}
struct MyExternals(Vec<u32>);
#[derive(Debug)]
struct MyError(String);
impl wasmi::Externals for MyExternals {
fn invoke_index(
&mut self,
index: usize,
args: wasmi::RuntimeArgs
) -> Result<Option<wasmi::RuntimeValue>, wasmi::Trap> {
macro_rules! bail {
($($t:tt)*) => ({
let s = MyError(format!($($t)*));
return Err(wasmi::Trap::new(wasmi::TrapKind::Host(Box::new(s))))
})
}
// We only recognize one function here which was mapped to the index 1
// by the resolver above.
if index != 1 {
bail!("only __wbindgen_describe can be run at this time")
}
if args.len() != 1 {
bail!("must have exactly one argument");
}
match args.nth_value_checked(0)? {
wasmi::RuntimeValue::I32(i) => self.0.push(i as u32),
_ => bail!("expected one argument of i32 type"),
}
Ok(None)
}
}
impl wasmi::HostError for MyError {}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}

View File

@ -14,4 +14,3 @@ dependency.
[dependencies]
serde_derive = "1"
serde = "1"
fnv = "1"

View File

@ -1,29 +1,25 @@
#[macro_use]
extern crate serde_derive;
extern crate fnv;
use std::hash::{Hash, Hasher};
pub const SCHEMA_VERSION: &str = "2";
pub const SCHEMA_VERSION: &str = "1";
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct Program {
pub exports: Vec<Export>,
pub enums: Vec<Enum>,
pub imports: Vec<Import>,
pub custom_type_names: Vec<CustomTypeName>,
pub version: String,
pub schema_version: String,
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct Import {
pub module: Option<String>,
pub js_namespace: Option<String>,
pub kind: ImportKind,
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
#[serde(tag = "kind", rename_all = "lowercase")]
pub enum ImportKind {
Function(ImportFunction),
@ -31,10 +27,9 @@ pub enum ImportKind {
Type(ImportType),
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct ImportFunction {
pub shim: String,
pub module: Option<String>,
pub catch: bool,
pub method: bool,
pub js_new: bool,
@ -45,47 +40,38 @@ pub struct ImportFunction {
pub function: Function,
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct ImportStatic {
pub module: Option<String>,
pub name: String,
pub shim: String,
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct ImportType {
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct Export {
pub class: Option<String>,
pub method: bool,
pub function: Function,
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct Enum {
pub name: String,
pub variants: Vec<EnumVariant>,
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct EnumVariant {
pub name: String,
pub value: u32
}
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
pub struct Function {
pub name: String,
pub arguments: Vec<Type>,
pub ret: Option<Type>,
}
#[derive(Deserialize)]
pub struct CustomTypeName {
pub descriptor: u32,
pub name: String,
}
pub fn new_function(struct_name: &str) -> String {
@ -120,53 +106,6 @@ pub fn struct_function_export_name(struct_: &str, f: &str) -> String {
return name
}
pub type Type = u32;
pub const TYPE_VECTOR_JSVALUE: u32 = 0;
pub const TYPE_ENUM: u32 = 1;
pub const TYPE_NUMBER: u32 = 2;
pub const TYPE_BORROWED_STR: u32 = 3;
pub const TYPE_STRING: u32 = 4;
pub const TYPE_BOOLEAN: u32 = 5;
pub const TYPE_SLICE_U8: u32 = 6;
pub const TYPE_VECTOR_U8: u32 = 7;
pub const TYPE_SLICE_I8: u32 = 8;
pub const TYPE_VECTOR_I8: u32 = 9;
pub const TYPE_SLICE_U16: u32 = 10;
pub const TYPE_VECTOR_U16: u32 = 11;
pub const TYPE_SLICE_I16: u32 = 12;
pub const TYPE_VECTOR_I16: u32 = 13;
pub const TYPE_SLICE_U32: u32 = 14;
pub const TYPE_VECTOR_U32: u32 = 15;
pub const TYPE_SLICE_I32: u32 = 16;
pub const TYPE_VECTOR_I32: u32 = 17;
pub const TYPE_SLICE_F32: u32 = 18;
pub const TYPE_VECTOR_F32: u32 = 19;
pub const TYPE_SLICE_F64: u32 = 20;
pub const TYPE_VECTOR_F64: u32 = 21;
pub const TYPE_JS_OWNED: u32 = 22;
pub const TYPE_JS_REF: u32 = 23;
pub const TYPE_STACK_FUNC0: u32 = 24;
pub const TYPE_STACK_FUNC1: u32 = 25;
pub const TYPE_STACK_FUNC2: u32 = 26;
pub const TYPE_STACK_FUNC3: u32 = 27;
pub const TYPE_STACK_FUNC4: u32 = 28;
pub const TYPE_STACK_FUNC5: u32 = 29;
pub const TYPE_STACK_FUNC6: u32 = 30;
pub const TYPE_STACK_FUNC7: u32 = 31;
pub const TYPE_FUNC: u32 = 32;
pub const TYPE_CUSTOM_START: u32 = 40;
pub const TYPE_CUSTOM_REF_FLAG: u32 = 1;
pub fn name_to_descriptor(name: &str) -> u32 {
const MAX: u32 = 10_000;
let mut h = fnv::FnvHasher::default();
name.hash(&mut h);
(((h.finish() as u32) % (MAX - TYPE_CUSTOM_START)) + TYPE_CUSTOM_START) & !1
}
pub fn version() -> String {
let mut v = env!("CARGO_PKG_VERSION").to_string();
if let Some(s) = option_env!("WBG_VERSION") {

View File

@ -9,6 +9,7 @@ use std::marker::Unsize;
use {throw, JsValue};
use convert::*;
use describe::*;
use __rt::WasmRefCell;
/// A handle to both a closure in Rust as well as JS closure which will invoke
@ -122,12 +123,20 @@ impl<T> Closure<T>
}
}
impl<T> WasmDescribe for Closure<T>
where T: WasmShim + ?Sized,
{
fn describe() {
inform(CLOSURE);
T::describe();
}
}
// `Closure` can only be passed by reference to imports.
impl<T> ToRefWasmBoundary for Closure<T>
where T: WasmShim + ?Sized,
{
type Abi = u32;
const DESCRIPTOR: Descriptor = T::DESCRIPTOR;
fn to_abi_ref(&self, extra: &mut Stack) -> u32 {
self.js.to_abi_ref(extra)
@ -149,9 +158,7 @@ impl<T> Drop for Closure<T>
///
/// This trait is not stable and it's not recommended to use this in bounds or
/// implement yourself.
pub unsafe trait WasmShim {
#[doc(hidden)]
const DESCRIPTOR: Descriptor;
pub unsafe trait WasmShim: WasmDescribe {
#[doc(hidden)]
type Wrapper;
#[doc(hidden)]
@ -174,8 +181,9 @@ macro_rules! doit {
($($var:ident)*) => $arity:ident
)*) => ($(
// Fn with no return
unsafe impl<$($var: WasmAbi),*> WasmShim for Fn($($var),*) {
const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC;
unsafe impl<$($var),*> WasmShim for Fn($($var),*)
where $($var: WasmAbi + WasmDescribe,)*
{
type Wrapper = Box<Fn($($var),*)>;
fn shim() -> u32 {
@ -209,8 +217,10 @@ macro_rules! doit {
}
// Fn with a return
unsafe impl<$($var: WasmAbi,)* R: WasmAbi> WasmShim for Fn($($var),*) -> R {
const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC;
unsafe impl<$($var,)* R> WasmShim for Fn($($var),*) -> R
where $($var: WasmAbi + WasmDescribe,)*
R: WasmAbi + WasmDescribe,
{
type Wrapper = Box<Fn($($var),*) -> R>;
fn shim() -> u32 {
@ -244,8 +254,9 @@ macro_rules! doit {
}
// FnMut with no return
unsafe impl<$($var: WasmAbi),*> WasmShim for FnMut($($var),*) {
const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC;
unsafe impl<$($var),*> WasmShim for FnMut($($var),*)
where $($var: WasmAbi + WasmDescribe,)*
{
type Wrapper = Box<WasmRefCell<FnMut($($var),*)>>;
fn shim() -> u32 {
@ -283,8 +294,10 @@ macro_rules! doit {
}
// FnMut with a return
unsafe impl<$($var: WasmAbi,)* R: WasmAbi> WasmShim for FnMut($($var),*) -> R {
const DESCRIPTOR: Descriptor = DESCRIPTOR_FUNC;
unsafe impl<$($var,)* R> WasmShim for FnMut($($var),*) -> R
where $($var: WasmAbi + WasmDescribe,)*
R: WasmAbi + WasmDescribe,
{
type Wrapper = Box<WasmRefCell<FnMut($($var),*) -> R>>;
fn shim() -> u32 {

View File

@ -1,9 +1,13 @@
//! This is mostly an internal module, no stability guarantees are provied. Use
//! at your own risk.
use std::mem::{self, ManuallyDrop};
use std::ops::{Deref, DerefMut};
use std::slice;
use std::str;
use {JsValue, throw};
use describe::*;
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct Descriptor {
@ -11,54 +15,29 @@ pub struct Descriptor {
pub __x: [u8; 4],
}
// keep in sync with shared/src/lib.rs TYPE constants
// pub const DESCRIPTOR_CUSTOM_REF_FLAG: Descriptor = Descriptor { __x: *b" 1", };
pub const DESCRIPTOR_NUMBER: Descriptor = Descriptor { __x: *b" 2", };
pub const DESCRIPTOR_BORROWED_STR: Descriptor = Descriptor { __x: *b" 3", };
pub const DESCRIPTOR_STRING: Descriptor = Descriptor { __x: *b" 4", };
pub const DESCRIPTOR_BOOLEAN: Descriptor = Descriptor { __x: *b" 5", };
pub const DESCRIPTOR_JS_OWNED: Descriptor = Descriptor { __x: *b" 22", };
pub const DESCRIPTOR_JS_REF: Descriptor = Descriptor { __x: *b" 23", };
pub const DESCRIPTOR_STACK_FUNC0: Descriptor = Descriptor { __x: *b" 24", };
pub const DESCRIPTOR_STACK_FUNC1: Descriptor = Descriptor { __x: *b" 25", };
pub const DESCRIPTOR_STACK_FUNC2: Descriptor = Descriptor { __x: *b" 26", };
pub const DESCRIPTOR_STACK_FUNC3: Descriptor = Descriptor { __x: *b" 27", };
pub const DESCRIPTOR_STACK_FUNC4: Descriptor = Descriptor { __x: *b" 28", };
pub const DESCRIPTOR_STACK_FUNC5: Descriptor = Descriptor { __x: *b" 29", };
pub const DESCRIPTOR_STACK_FUNC6: Descriptor = Descriptor { __x: *b" 30", };
pub const DESCRIPTOR_STACK_FUNC7: Descriptor = Descriptor { __x: *b" 31", };
pub const DESCRIPTOR_FUNC: Descriptor = Descriptor { __x: *b" 32", };
pub trait WasmBoundary {
pub trait WasmBoundary: WasmDescribe {
type Abi: WasmAbi;
const DESCRIPTOR: Descriptor;
fn into_abi(self, extra: &mut Stack) -> Self::Abi;
unsafe fn from_abi(js: Self::Abi, extra: &mut Stack) -> Self;
}
pub trait FromRefWasmBoundary {
pub trait FromRefWasmBoundary: WasmDescribe {
type Abi: WasmAbi;
const DESCRIPTOR: Descriptor;
type RefAnchor: Deref<Target = Self>;
unsafe fn from_abi_ref(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor;
}
pub trait FromRefMutWasmBoundary {
pub trait FromRefMutWasmBoundary: WasmDescribe {
type Abi: WasmAbi;
const DESCRIPTOR: Descriptor;
type RefAnchor: DerefMut<Target = Self>;
unsafe fn from_abi_ref_mut(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor;
}
pub trait ToRefWasmBoundary {
pub trait ToRefWasmBoundary: WasmDescribe {
type Abi: WasmAbi;
const DESCRIPTOR: Descriptor;
fn to_abi_ref(&self, extra: &mut Stack) -> u32;
}
@ -78,6 +57,8 @@ pub unsafe trait WasmAbi {}
unsafe impl WasmAbi for u32 {}
unsafe impl WasmAbi for u64 {}
unsafe impl WasmAbi for i32 {}
unsafe impl WasmAbi for i64 {}
unsafe impl WasmAbi for f32 {}
unsafe impl WasmAbi for f64 {}
@ -85,7 +66,6 @@ macro_rules! simple {
($($t:tt)*) => ($(
impl WasmBoundary for $t {
type Abi = $t;
const DESCRIPTOR: Descriptor = DESCRIPTOR_NUMBER;
fn into_abi(self, _extra: &mut Stack) -> $t { self }
unsafe fn from_abi(js: $t, _extra: &mut Stack) -> $t { js }
@ -93,13 +73,12 @@ macro_rules! simple {
)*)
}
simple!(u32 u64 f32 f64);
simple!(u32 u64 i32 i64 f32 f64);
macro_rules! as_u32 {
($($t:tt)*) => ($(
impl WasmBoundary for $t {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_NUMBER;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> $t { js as $t }
@ -107,11 +86,10 @@ macro_rules! as_u32 {
)*)
}
as_u32!(i8 u8 i16 u16 i32 isize usize);
as_u32!(i8 u8 i16 u16 isize usize);
impl WasmBoundary for bool {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_BOOLEAN;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> bool { js != 0 }
@ -119,7 +97,6 @@ impl WasmBoundary for bool {
impl<T> WasmBoundary for *const T {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_NUMBER;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *const T { js as *const T }
@ -127,17 +104,15 @@ impl<T> WasmBoundary for *const T {
impl<T> WasmBoundary for *mut T {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_NUMBER;
fn into_abi(self, _extra: &mut Stack) -> u32 { self as u32 }
unsafe fn from_abi(js: u32, _extra: &mut Stack) -> *mut T { js as *mut T }
}
macro_rules! vectors {
($($t:ident => ($slice:expr, $owned:expr))*) => ($(
($($t:ident)*) => ($(
impl WasmBoundary for Box<[$t]> {
type Abi = u32;
const DESCRIPTOR: Descriptor = Descriptor { __x: *$owned };
fn into_abi(self, extra: &mut Stack) -> u32 {
let ptr = self.as_ptr();
@ -152,11 +127,11 @@ macro_rules! vectors {
let len = extra.pop() as usize;
Vec::from_raw_parts(ptr, len, len).into_boxed_slice()
}
}
impl ToRefWasmBoundary for [$t] {
type Abi = u32;
const DESCRIPTOR: Descriptor = Descriptor { __x: *$slice };
fn to_abi_ref(&self, extra: &mut Stack) -> u32 {
let ptr = self.as_ptr();
@ -168,7 +143,6 @@ macro_rules! vectors {
impl FromRefWasmBoundary for [$t] {
type Abi = u32;
const DESCRIPTOR: Descriptor = Descriptor { __x: *$slice };
type RefAnchor = SliceAnchor<$t>;
unsafe fn from_abi_ref(js: u32, extra: &mut Stack) -> SliceAnchor<$t> {
@ -194,19 +168,11 @@ impl<T> Deref for SliceAnchor<T> {
}
vectors! {
u8 => (b" 6", b" 7")
i8 => (b" 8", b" 9")
u16 => (b" 10", b" 11")
i16 => (b" 12", b" 13")
u32 => (b" 14", b" 15")
i32 => (b" 16", b" 17")
f32 => (b" 18", b" 19")
f64 => (b" 20", b" 21")
u8 i8 u16 i16 u32 i32 f32 f64
}
impl<T> WasmBoundary for Vec<T> where Box<[T]>: WasmBoundary {
type Abi = <Box<[T]> as WasmBoundary>::Abi;
const DESCRIPTOR: Descriptor = <Box<[T]> as WasmBoundary>::DESCRIPTOR;
fn into_abi(self, extra: &mut Stack) -> Self::Abi {
self.into_boxed_slice().into_abi(extra)
@ -219,7 +185,6 @@ impl<T> WasmBoundary for Vec<T> where Box<[T]>: WasmBoundary {
impl WasmBoundary for String {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_STRING;
fn into_abi(self, extra: &mut Stack) -> u32 {
self.into_bytes().into_abi(extra)
@ -232,7 +197,6 @@ impl WasmBoundary for String {
impl ToRefWasmBoundary for str {
type Abi = <[u8] as ToRefWasmBoundary>::Abi;
const DESCRIPTOR: Descriptor = DESCRIPTOR_BORROWED_STR;
fn to_abi_ref(&self, extra: &mut Stack) -> Self::Abi {
self.as_bytes().to_abi_ref(extra)
@ -241,7 +205,6 @@ impl ToRefWasmBoundary for str {
impl FromRefWasmBoundary for str {
type Abi = <[u8] as ToRefWasmBoundary>::Abi;
const DESCRIPTOR: Descriptor = DESCRIPTOR_BORROWED_STR;
type RefAnchor = StrAnchor;
unsafe fn from_abi_ref(js: Self::Abi, extra: &mut Stack) -> Self::RefAnchor {
@ -263,7 +226,6 @@ impl Deref for StrAnchor {
impl WasmBoundary for JsValue {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_JS_OWNED;
fn into_abi(self, _extra: &mut Stack) -> u32 {
let ret = self.idx;
@ -278,7 +240,6 @@ impl WasmBoundary for JsValue {
impl ToRefWasmBoundary for JsValue {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_JS_REF;
fn to_abi_ref(&self, _extra: &mut Stack) -> u32 {
self.idx
}
@ -286,7 +247,6 @@ impl ToRefWasmBoundary for JsValue {
impl FromRefWasmBoundary for JsValue {
type Abi = u32;
const DESCRIPTOR: Descriptor = DESCRIPTOR_JS_REF;
type RefAnchor = ManuallyDrop<JsValue>;
unsafe fn from_abi_ref(js: u32, _extra: &mut Stack) -> ManuallyDrop<JsValue> {
@ -296,7 +256,6 @@ impl FromRefWasmBoundary for JsValue {
impl WasmBoundary for Box<[JsValue]> {
type Abi = u32;
const DESCRIPTOR: Descriptor = Descriptor { __x: *b" 0" };
fn into_abi(self, extra: &mut Stack) -> u32 {
let ptr = self.as_ptr();
@ -350,15 +309,12 @@ pub unsafe extern fn __wbindgen_global_argument_ptr() -> *mut u32 {
}
macro_rules! stack_closures {
($(
($($var:ident)*) => $descriptor:ident
)*) => ($(
($( ($($var:ident)*) )*) => ($(
impl<'a, $($var,)* R> ToRefWasmBoundary for Fn($($var),*) -> R + 'a
where $($var: WasmAbi,)*
R: WasmAbi
where $($var: WasmAbi + WasmDescribe,)*
R: WasmAbi + WasmDescribe
{
type Abi = u32;
const DESCRIPTOR: Descriptor = $descriptor;
fn to_abi_ref(&self, extra: &mut Stack) -> u32 {
#[allow(non_snake_case)]
@ -383,10 +339,9 @@ macro_rules! stack_closures {
}
impl<'a, $($var,)*> ToRefWasmBoundary for Fn($($var),*) + 'a
where $($var: WasmAbi,)*
where $($var: WasmAbi + WasmDescribe,)*
{
type Abi = u32;
const DESCRIPTOR: Descriptor = $descriptor;
fn to_abi_ref(&self, extra: &mut Stack) -> u32 {
#[allow(non_snake_case)]
@ -413,12 +368,12 @@ macro_rules! stack_closures {
}
stack_closures! {
() => DESCRIPTOR_STACK_FUNC0
(A) => DESCRIPTOR_STACK_FUNC1
(A B) => DESCRIPTOR_STACK_FUNC2
(A B C) => DESCRIPTOR_STACK_FUNC3
(A B C D) => DESCRIPTOR_STACK_FUNC4
(A B C D E) => DESCRIPTOR_STACK_FUNC5
(A B C D E F) => DESCRIPTOR_STACK_FUNC6
(A B C D E F G) => DESCRIPTOR_STACK_FUNC7
()
(A)
(A B)
(A B C)
(A B C D)
(A B C D E)
(A B C D E F)
(A B C D E F G)
}

184
src/describe.rs Normal file
View File

@ -0,0 +1,184 @@
//! This is an internal module, no stability guarantees are provided. Use at
//! your own risk.
#![doc(hidden)]
use JsValue;
macro_rules! tys {
($($a:ident)*) => (tys! { @ ($($a)*) 0 });
(@ () $v:expr) => {};
(@ ($a:ident $($b:ident)*) $v:expr) => {
pub const $a: u32 = $v;
tys!(@ ($($b)*) $v+1);
}
}
// NB: this list must be kept in sync with `crates/cli-support/src/descriptor.rs`
tys! {
I8
U8
I16
U16
I32
U32
I64
U64
F32
F64
BOOLEAN
FUNCTION
CLOSURE
STRING
REF
REFMUT
SLICE
VECTOR
ANYREF
ENUM
RUST_STRUCT
}
pub fn inform(a: u32) {
unsafe {
super::__wbindgen_describe(a)
}
}
pub trait WasmDescribe {
fn describe();
}
macro_rules! simple {
($($t:ident => $d:ident)*) => ($(
impl WasmDescribe for $t {
fn describe() { inform($d) }
}
)*)
}
simple! {
i8 => I8
u8 => U8
i16 => I16
u16 => U16
i32 => I32
u32 => U32
i64 => I64
u64 => U64
isize => I32
usize => U32
f32 => F32
f64 => F64
bool => BOOLEAN
str => STRING
String => STRING
JsValue => ANYREF
}
impl<T> WasmDescribe for *const T {
fn describe() { inform(I32) }
}
impl<T> WasmDescribe for *mut T {
fn describe() { inform(I32) }
}
impl<T: WasmDescribe> WasmDescribe for [T] {
fn describe() {
inform(SLICE);
T::describe();
}
}
impl<'a, T: WasmDescribe + ?Sized> WasmDescribe for &'a T {
fn describe() {
inform(REF);
T::describe();
}
}
impl<'a, T: WasmDescribe + ?Sized> WasmDescribe for &'a mut T {
fn describe() {
inform(REFMUT);
T::describe();
}
}
impl<T: WasmDescribe> WasmDescribe for Box<[T]> {
fn describe() {
inform(VECTOR);
T::describe();
}
}
impl<T> WasmDescribe for Vec<T> where Box<[T]>: WasmDescribe {
fn describe() {
<Box<[T]>>::describe();
}
}
fn _cnt<T: WasmDescribe>() -> u32 { 1 }
macro_rules! doit {
($( ($($var:ident)*))*) => ($(
impl<'a, $($var,)* R> WasmDescribe for Fn($($var),*) -> R + 'a
where $($var: WasmDescribe,)*
R: WasmDescribe
{
fn describe() {
inform(FUNCTION);
inform(0 $(+ _cnt::<$var>())*);
$(<$var as WasmDescribe>::describe();)*
inform(1);
<R as WasmDescribe>::describe();
}
}
impl<'a, $($var,)* > WasmDescribe for Fn($($var),*) + 'a
where $($var: WasmDescribe,)*
{
fn describe() {
inform(FUNCTION);
inform(0 $(+ _cnt::<$var>())*);
$(<$var as WasmDescribe>::describe();)*
inform(0);
}
}
impl<'a, $($var,)* R> WasmDescribe for FnMut($($var),*) -> R + 'a
where $($var: WasmDescribe,)*
R: WasmDescribe
{
fn describe() {
inform(FUNCTION);
inform(0 $(+ _cnt::<$var>())*);
$(<$var as WasmDescribe>::describe();)*
inform(1);
<R as WasmDescribe>::describe();
}
}
impl<'a, $($var,)* > WasmDescribe for FnMut($($var),*) + 'a
where $($var: WasmDescribe,)*
{
fn describe() {
inform(FUNCTION);
inform(0 $(+ _cnt::<$var>())*);
$(<$var as WasmDescribe>::describe();)*
inform(0);
}
}
)*)
}
doit! {
()
(A)
(A B)
(A B C)
(A B C D)
(A B C D E)
(A B C D E F)
(A B C D E F G)
}

View File

@ -28,6 +28,7 @@ pub mod prelude {
pub mod convert;
pub mod closure;
pub mod describe;
/// Representation of an object owned by JS.
///
@ -243,6 +244,8 @@ extern {
fn __wbindgen_cb_arity7(a: u32, b: u32, c: u32) -> u32;
fn __wbindgen_cb_drop(idx: u32);
fn __wbindgen_cb_forget(idx: u32);
fn __wbindgen_describe(v: u32);
}
impl Clone for JsValue {
@ -297,8 +300,6 @@ impl<T: WasmBoundary> Deref for JsStatic<T> {
fn deref(&self) -> &T {
unsafe {
(*self.__inner.get()).get_or_insert_with(|| {
assert!(T::DESCRIPTOR == JsValue::DESCRIPTOR,
"only JS values can be imported as statics for now");
(self.__init)()
})
}