mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-15 05:51:23 +00:00
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:
285
crates/cli-support/src/descriptor.rs
Normal file
285
crates/cli-support/src/descriptor.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user