Support integer/float slices/vectors

Closes #5
This commit is contained in:
Alex Crichton 2018-02-16 18:58:37 -08:00
parent 7802535948
commit 3c58aa7310
6 changed files with 863 additions and 89 deletions

View File

@ -402,6 +402,7 @@ are:
* Imported types in a foreign module annotated with `#[wasm_bindgen]` * Imported types in a foreign module annotated with `#[wasm_bindgen]`
* Borrowed exported structs (`&Foo` or `&mut Bar`) * Borrowed exported structs (`&Foo` or `&mut Bar`)
* The `JsValue` type and `&JsValue` (not mutable references) * The `JsValue` type and `&JsValue` (not mutable references)
* Vectors and slices of supported integer types
All of the above can also be returned except borrowed references. Strings are All of the above can also be returned except borrowed references. Strings are
implemented with shim functions to copy data in/out of the Rust heap. That is, a implemented with shim functions to copy data in/out of the Rust heap. That is, a

View File

@ -467,6 +467,79 @@ impl<'a> Context<'a> {
} }
} }
fn expose_pass_array8_to_wasm(&mut self) {
if !self.exposed_globals.insert("pass_array8_to_wasm") {
return
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.expose_uint8_memory();
self.globals.push_str(&format!("
function passArray8ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
getUint8Memory().set(arg, ptr);
return [ptr, arg.length];
}}
"));
}
fn expose_pass_array16_to_wasm(&mut self) {
if !self.exposed_globals.insert("pass_array16_to_wasm") {
return
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.expose_uint16_memory();
self.globals.push_str(&format!("
function passArray16ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
getUint16Memory().set(arg, ptr / 2);
return [ptr, arg.length];
}}
"));
}
fn expose_pass_array32_to_wasm(&mut self) {
if !self.exposed_globals.insert("pass_array32_to_wasm") {
return
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.expose_uint32_memory();
self.globals.push_str(&format!("
function passArray32ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
getUint32Memory().set(arg, ptr / 4);
return [ptr, arg.length];
}}
"));
}
fn expose_pass_array_f32_to_wasm(&mut self) {
if !self.exposed_globals.insert("pass_array_f32_to_wasm") {
return
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.globals.push_str(&format!("
function passArrayF32ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
new Float32Array(wasm.memory.buffer).set(arg, ptr / 4);
return [ptr, arg.length];
}}
"));
}
fn expose_pass_array_f64_to_wasm(&mut self) {
if !self.exposed_globals.insert("pass_array_f64_to_wasm") {
return
}
self.required_internal_exports.insert("__wbindgen_malloc");
self.globals.push_str(&format!("
function passArrayF64ToWasm(arg) {{
const ptr = wasm.__wbindgen_malloc(arg.byteLength);
new Float64Array(wasm.memory.buffer).set(arg, ptr / 8);
return [ptr, arg.length];
}}
"));
}
fn expose_text_encoder(&mut self) { fn expose_text_encoder(&mut self) {
if !self.exposed_globals.insert("text_encoder") { if !self.exposed_globals.insert("text_encoder") {
return return
@ -523,6 +596,110 @@ impl<'a> Context<'a> {
} }
} }
fn expose_get_array_i8_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_i8_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayI8FromWasm(ptr, len) {{
const mem = getUint8Memory();
const slice = mem.slice(ptr, ptr + len);
return new Int8Array(slice);
}}
"));
}
fn expose_get_array_u8_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_u8_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayU8FromWasm(ptr, len) {{
const mem = getUint8Memory();
const slice = mem.slice(ptr, ptr + len);
return new Uint8Array(slice);
}}
"));
}
fn expose_get_array_i16_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_i16_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayI16FromWasm(ptr, len) {{
const mem = getUint16Memory();
const slice = mem.slice(ptr / 2, ptr / 2 + len);
return new Int16Array(slice);
}}
"));
}
fn expose_get_array_u16_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_u16_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayU16FromWasm(ptr, len) {{
const mem = getUint16Memory();
const slice = mem.slice(ptr / 2, ptr / 2 + len);
return new Uint16Array(slice);
}}
"));
}
fn expose_get_array_i32_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_i32_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayI32FromWasm(ptr, len) {{
const mem = getUint32Memory();
const slice = mem.slice(ptr / 4, ptr / 4 + len);
return new Int32Array(slice);
}}
"));
}
fn expose_get_array_u32_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_u32_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayU32FromWasm(ptr, len) {{
const mem = getUint32Memory();
const slice = mem.slice(ptr / 4, ptr / 4 + len);
return new Uint32Array(slice);
}}
"));
}
fn expose_get_array_f32_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_f32_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayF32FromWasm(ptr, len) {{
const mem = new Float32Array(wasm.memory.buffer);
const slice = mem.slice(ptr / 4, ptr / 4 + len);
return new Float32Array(slice);
}}
"));
}
fn expose_get_array_f64_from_wasm(&mut self) {
if !self.exposed_globals.insert("get_array_f64_from_wasm") {
return
}
self.globals.push_str(&format!("
function getArrayF64FromWasm(ptr, len) {{
const mem = new Float64Array(wasm.memory.buffer);
const slice = mem.slice(ptr / 8, ptr / 8 + len);
return new Float64Array(slice);
}}
"));
}
fn expose_uint8_memory(&mut self) { fn expose_uint8_memory(&mut self) {
if !self.exposed_globals.insert("uint8_memory") { if !self.exposed_globals.insert("uint8_memory") {
return return
@ -538,6 +715,21 @@ impl<'a> Context<'a> {
")); "));
} }
fn expose_uint16_memory(&mut self) {
if !self.exposed_globals.insert("uint16_memory") {
return
}
self.globals.push_str(&format!("
let cachedUint16Memory = null;
function getUint16Memory() {{
if (cachedUint16Memory === null ||
cachedUint16Memory.buffer !== wasm.memory.buffer)
cachedUint16Memory = new Uint16Array(wasm.memory.buffer);
return cachedUint16Memory;
}}
"));
}
fn expose_uint32_memory(&mut self) { fn expose_uint32_memory(&mut self) {
if !self.exposed_globals.insert("uint32_memory") { if !self.exposed_globals.insert("uint32_memory") {
return return
@ -640,6 +832,76 @@ impl<'a> Context<'a> {
let c = char::from_u32(c).unwrap(); let c = char::from_u32(c).unwrap();
&self.custom_type_names[&c] &self.custom_type_names[&c]
} }
fn pass_to_wasm_function(&mut self, ty: &VectorType) -> &'static str {
match ty.kind {
VectorKind::String => {
self.expose_pass_string_to_wasm();
"passStringToWasm"
}
VectorKind::I8 | VectorKind::U8 => {
self.expose_pass_array8_to_wasm();
"passArray8ToWasm"
}
VectorKind::I16 | VectorKind::U16 => {
self.expose_pass_array16_to_wasm();
"passArray16ToWasm"
}
VectorKind::I32 | VectorKind::U32 => {
self.expose_pass_array32_to_wasm();
"passArray32ToWasm"
}
VectorKind::F32 => {
self.expose_pass_array_f32_to_wasm();
"passArrayF32ToWasm"
}
VectorKind::F64 => {
self.expose_pass_array_f64_to_wasm();
"passArrayF64ToWasm"
}
}
}
fn expose_get_vector_from_wasm(&mut self, ty: &VectorType) -> &'static str {
match ty.kind {
VectorKind::String => {
self.expose_get_string_from_wasm();
"getStringFromWasm"
}
VectorKind::I8 => {
self.expose_get_array_i8_from_wasm();
"getArrayI8FromWasm"
}
VectorKind::U8 => {
self.expose_get_array_u8_from_wasm();
"getArrayU8FromWasm"
}
VectorKind::I16 => {
self.expose_get_array_i16_from_wasm();
"getArrayI16FromWasm"
}
VectorKind::U16 => {
self.expose_get_array_u16_from_wasm();
"getArrayU16FromWasm"
}
VectorKind::I32 => {
self.expose_get_array_i32_from_wasm();
"getArrayI32FromWasm"
}
VectorKind::U32 => {
self.expose_get_array_u32_from_wasm();
"getArrayU32FromWasm"
}
VectorKind::F32 => {
self.expose_get_array_f32_from_wasm();
"getArrayF32FromWasm"
}
VectorKind::F64 => {
self.expose_get_array_f64_from_wasm();
"getArrayF64FromWasm"
}
}
}
} }
impl<'a, 'b> SubContext<'a, 'b> { impl<'a, 'b> SubContext<'a, 'b> {
@ -743,15 +1005,33 @@ impl<'a, 'b> SubContext<'a, 'b> {
pass(&format!("arg{i} ? 1 : 0", i = i)) pass(&format!("arg{i} ? 1 : 0", i = i))
} }
shared::TYPE_BORROWED_STR | shared::TYPE_BORROWED_STR |
shared::TYPE_STRING => { shared::TYPE_STRING |
dst_ts.push_str(": string"); shared::TYPE_VECTOR_U8 |
self.cx.expose_pass_string_to_wasm(); shared::TYPE_VECTOR_I8 |
shared::TYPE_SLICE_U8 |
shared::TYPE_SLICE_I8 |
shared::TYPE_VECTOR_U16 |
shared::TYPE_VECTOR_I16 |
shared::TYPE_SLICE_U16 |
shared::TYPE_SLICE_I16 |
shared::TYPE_VECTOR_U32 |
shared::TYPE_VECTOR_I32 |
shared::TYPE_SLICE_U32 |
shared::TYPE_SLICE_I32 |
shared::TYPE_VECTOR_F32 |
shared::TYPE_VECTOR_F64 |
shared::TYPE_SLICE_F32 |
shared::TYPE_SLICE_F64 => {
let ty = VectorType::from(*arg);
dst_ts.push_str(": ");
dst_ts.push_str(ty.js_ty());
let func = self.cx.pass_to_wasm_function(&ty);
arg_conversions.push_str(&format!("\ arg_conversions.push_str(&format!("\
const [ptr{i}, len{i}] = passStringToWasm({arg}); const [ptr{i}, len{i}] = {func}({arg});
", i = i, arg = name)); ", i = i, func = func, arg = name));
pass(&format!("ptr{}", i)); pass(&format!("ptr{}", i));
pass(&format!("len{}", i)); pass(&format!("len{}", i));
if *arg == shared::TYPE_BORROWED_STR { if ty.owned {
destructors.push_str(&format!("\n\ destructors.push_str(&format!("\n\
wasm.__wbindgen_free(ptr{i}, len{i});\n\ wasm.__wbindgen_free(ptr{i}, len{i});\n\
", i = i)); ", i = i));
@ -823,19 +1103,29 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.expose_take_object(); self.cx.expose_take_object();
format!("return takeObject(ret);") format!("return takeObject(ret);")
} }
Some(shared::TYPE_STRING) => { Some(shared::TYPE_STRING) |
dst_ts.push_str(": string"); Some(shared::TYPE_VECTOR_U8) |
self.cx.expose_get_string_from_wasm(); Some(shared::TYPE_VECTOR_I8) |
Some(shared::TYPE_VECTOR_U16) |
Some(shared::TYPE_VECTOR_I16) |
Some(shared::TYPE_VECTOR_U32) |
Some(shared::TYPE_VECTOR_I32) |
Some(shared::TYPE_VECTOR_F32) |
Some(shared::TYPE_VECTOR_F64) => {
let ty = VectorType::from(function.ret.unwrap());
dst_ts.push_str(": ");
dst_ts.push_str(ty.js_ty());
let f = self.cx.expose_get_vector_from_wasm(&ty);
self.cx.required_internal_exports.insert("__wbindgen_boxed_str_ptr"); self.cx.required_internal_exports.insert("__wbindgen_boxed_str_ptr");
self.cx.required_internal_exports.insert("__wbindgen_boxed_str_len"); self.cx.required_internal_exports.insert("__wbindgen_boxed_str_len");
self.cx.required_internal_exports.insert("__wbindgen_boxed_str_free"); self.cx.required_internal_exports.insert("__wbindgen_boxed_str_free");
format!(" format!("
const ptr = wasm.__wbindgen_boxed_str_ptr(ret); const ptr = wasm.__wbindgen_boxed_str_ptr(ret);
const len = wasm.__wbindgen_boxed_str_len(ret); const len = wasm.__wbindgen_boxed_str_len(ret);
const realRet = getStringFromWasm(ptr, len); const realRet = {}(ptr, len);
wasm.__wbindgen_boxed_str_free(ret); wasm.__wbindgen_boxed_str_free(ret);
return realRet; return realRet;
") ", f)
} }
Some(shared::TYPE_JS_REF) | Some(shared::TYPE_JS_REF) |
Some(shared::TYPE_BORROWED_STR) => panic!(), Some(shared::TYPE_BORROWED_STR) => panic!(),
@ -925,23 +1215,40 @@ impl<'a, 'b> SubContext<'a, 'b> {
invoc_args.push(format!("arg{} != 0", i)); invoc_args.push(format!("arg{} != 0", i));
abi_args.push(format!("arg{}", i)); abi_args.push(format!("arg{}", i));
} }
shared::TYPE_BORROWED_STR => { shared::TYPE_BORROWED_STR |
self.cx.expose_get_string_from_wasm(); shared::TYPE_STRING |
invoc_args.push(format!("getStringFromWasm(ptr{0}, len{0})", i)); shared::TYPE_VECTOR_U8 |
abi_args.push(format!("ptr{}", i)); shared::TYPE_VECTOR_I8 |
abi_args.push(format!("len{}", i)); shared::TYPE_SLICE_U8 |
} shared::TYPE_SLICE_I8 |
shared::TYPE_STRING => { shared::TYPE_VECTOR_U16 |
self.cx.expose_get_string_from_wasm(); shared::TYPE_VECTOR_I16 |
shared::TYPE_SLICE_U16 |
shared::TYPE_SLICE_I16 |
shared::TYPE_VECTOR_U32 |
shared::TYPE_VECTOR_I32 |
shared::TYPE_SLICE_U32 |
shared::TYPE_SLICE_I32 |
shared::TYPE_VECTOR_F32 |
shared::TYPE_VECTOR_F64 |
shared::TYPE_SLICE_F32 |
shared::TYPE_SLICE_F64 => {
let ty = VectorType::from(*arg);
let f = self.cx.expose_get_vector_from_wasm(&ty);
abi_args.push(format!("ptr{}", i)); abi_args.push(format!("ptr{}", i));
abi_args.push(format!("len{}", i)); abi_args.push(format!("len{}", i));
extra.push_str(&format!(" extra.push_str(&format!("
let arg{0} = getStringFromWasm(ptr{0}, len{0}); let arg{0} = {func}(ptr{0}, len{0});
", i, func = f));
invoc_args.push(format!("arg{}", i));
if ty.owned {
extra.push_str(&format!("
wasm.__wbindgen_free(ptr{0}, len{0}); wasm.__wbindgen_free(ptr{0}, len{0});
", i)); ", i));
invoc_args.push(format!("arg{}", i));
self.cx.required_internal_exports.insert("__wbindgen_free"); self.cx.required_internal_exports.insert("__wbindgen_free");
} }
}
shared::TYPE_JS_OWNED => { shared::TYPE_JS_OWNED => {
self.cx.expose_take_object(); self.cx.expose_take_object();
invoc_args.push(format!("takeObject(arg{})", i)); invoc_args.push(format!("takeObject(arg{})", i));
@ -1001,15 +1308,24 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.expose_add_heap_object(); self.cx.expose_add_heap_object();
format!("return addHeapObject({});", invoc) format!("return addHeapObject({});", invoc)
} }
Some(shared::TYPE_STRING) => { Some(shared::TYPE_STRING) |
self.cx.expose_pass_string_to_wasm(); Some(shared::TYPE_VECTOR_U8) |
Some(shared::TYPE_VECTOR_I8) |
Some(shared::TYPE_VECTOR_U16) |
Some(shared::TYPE_VECTOR_I16) |
Some(shared::TYPE_VECTOR_U32) |
Some(shared::TYPE_VECTOR_I32) |
Some(shared::TYPE_VECTOR_F32) |
Some(shared::TYPE_VECTOR_F64) => {
let ty = VectorType::from(import.function.ret.unwrap());
let f = self.cx.pass_to_wasm_function(&ty);
self.cx.expose_uint32_memory(); self.cx.expose_uint32_memory();
abi_args.push("wasmretptr".to_string()); abi_args.push("wasmretptr".to_string());
format!(" format!("
const [retptr, retlen] = passStringToWasm({}); const [retptr, retlen] = {}({});
getUint32Memory()[wasmretptr / 4] = retlen; getUint32Memory()[wasmretptr / 4] = retlen;
return retptr; return retptr;
", invoc) ", f, invoc)
} }
None => invoc, None => invoc,
_ => unimplemented!(), _ => unimplemented!(),
@ -1042,3 +1358,96 @@ impl<'a, 'b> SubContext<'a, 'b> {
self.cx.globals.push_str("\n"); self.cx.globals.push_str("\n");
} }
} }
struct VectorType {
owned: bool,
kind: VectorKind,
}
enum VectorKind {
String,
I8,
U8,
I16,
U16,
I32,
U32,
F32,
F64,
}
impl VectorType {
fn from(desc: char) -> VectorType {
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 }
}
_ => panic!()
}
}
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",
}
}
}

View File

@ -49,14 +49,25 @@ pub struct Struct {
pub enum Type { pub enum Type {
// special // special
BorrowedStr, Vector(VectorType, bool),
String,
ByRef(syn::Type), ByRef(syn::Type),
ByMutRef(syn::Type), ByMutRef(syn::Type),
ByValue(syn::Type), ByValue(syn::Type),
} }
pub enum VectorType {
String,
I8,
U8,
I16,
U16,
I32,
U32,
F32,
F64,
}
impl Program { impl Program {
pub fn push_item(&mut self, pub fn push_item(&mut self,
item: syn::Item, item: syn::Item,
@ -216,8 +227,8 @@ impl Program {
Type::ByMutRef(_) => { Type::ByMutRef(_) => {
panic!("first method argument cannot be mutable ref") panic!("first method argument cannot be mutable ref")
} }
Type::String | Type::BorrowedStr => { Type::Vector(..) => {
panic!("method receivers cannot be strings") panic!("method receivers cannot be vectors")
} }
}; };
let class_name = match *class { let class_name = match *class {
@ -422,10 +433,15 @@ impl Type {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => { syn::Type::Path(syn::TypePath { qself: None, ref path }) => {
let ident = extract_path_ident(path); let ident = extract_path_ident(path);
match ident.as_ref().map(|s| s.as_ref()) { match ident.as_ref().map(|s| s.as_ref()) {
Some("str") => return Type::BorrowedStr, Some("str") => return Type::Vector(VectorType::String, false),
_ => {} _ => {}
} }
} }
syn::Type::Slice(ref slice) => {
if let Some(ty) = VectorType::from(&slice.elem) {
return Type::Vector(ty, false)
}
}
_ => {} _ => {}
} }
return if r.mutability.is_some() { return if r.mutability.is_some() {
@ -434,10 +450,29 @@ impl Type {
Type::ByRef((*r.elem).clone()) Type::ByRef((*r.elem).clone())
} }
} }
syn::Type::Path(syn::TypePath { qself: None, ref path }) => { syn::Type::Path(syn::TypePath { qself: None, ref path })
let ident = extract_path_ident(path); if path.leading_colon.is_none() && path.segments.len() == 1 =>
match ident.as_ref().map(|s| s.as_ref()) { {
Some("String") => return Type::String, let seg = path.segments.first().unwrap().into_value();
match seg.arguments {
syn::PathArguments::None => {
match seg.ident.as_ref() {
"String" => return Type::Vector(VectorType::String, true),
_ => {}
}
}
syn::PathArguments::AngleBracketed(ref t)
if seg.ident == "Vec" && t.args.len() == 1 =>
{
match **t.args.first().unwrap().value() {
syn::GenericArgument::Type(ref t) => {
if let Some(ty) = VectorType::from(t) {
return Type::Vector(ty, true)
}
}
_ => {}
}
}
_ => {} _ => {}
} }
} }
@ -449,8 +484,24 @@ impl Type {
fn wbg_literal(&self, a: &mut LiteralBuilder) { fn wbg_literal(&self, a: &mut LiteralBuilder) {
match *self { match *self {
Type::BorrowedStr => a.char(shared::TYPE_BORROWED_STR), Type::Vector(VectorType::String, true) => a.char(shared::TYPE_STRING),
Type::String => a.char(shared::TYPE_STRING), Type::Vector(VectorType::String, false) => a.char(shared::TYPE_BORROWED_STR),
Type::Vector(VectorType::U8, true) => a.char(shared::TYPE_VECTOR_U8),
Type::Vector(VectorType::U8, false) => a.char(shared::TYPE_SLICE_U8),
Type::Vector(VectorType::I8, true) => a.char(shared::TYPE_VECTOR_I8),
Type::Vector(VectorType::I8, false) => a.char(shared::TYPE_SLICE_I8),
Type::Vector(VectorType::U16, true) => a.char(shared::TYPE_VECTOR_U16),
Type::Vector(VectorType::U16, false) => a.char(shared::TYPE_SLICE_U16),
Type::Vector(VectorType::I16, true) => a.char(shared::TYPE_VECTOR_I16),
Type::Vector(VectorType::I16, false) => a.char(shared::TYPE_SLICE_I16),
Type::Vector(VectorType::U32, true) => a.char(shared::TYPE_VECTOR_U32),
Type::Vector(VectorType::U32, false) => a.char(shared::TYPE_SLICE_U32),
Type::Vector(VectorType::I32, true) => a.char(shared::TYPE_VECTOR_I32),
Type::Vector(VectorType::I32, false) => a.char(shared::TYPE_SLICE_I32),
Type::Vector(VectorType::F32, true) => a.char(shared::TYPE_VECTOR_F32),
Type::Vector(VectorType::F32, false) => a.char(shared::TYPE_SLICE_F32),
Type::Vector(VectorType::F64, true) => a.char(shared::TYPE_VECTOR_F64),
Type::Vector(VectorType::F64, false) => a.char(shared::TYPE_SLICE_F64),
Type::ByValue(ref t) => { Type::ByValue(ref t) => {
a.as_char(my_quote! { a.as_char(my_quote! {
<#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR <#t as ::wasm_bindgen::convert::WasmBoundary>::DESCRIPTOR
@ -849,3 +900,61 @@ fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str)
} }
syn::parse_error() syn::parse_error()
} }
fn ungroup(input: &syn::Type) -> &syn::Type {
match *input {
syn::Type::Group(ref t) => &t.elem,
_ => input,
}
}
impl VectorType {
fn from(ty: &syn::Type) -> Option<VectorType> {
let path = match *ungroup(ty) {
syn::Type::Path(syn::TypePath { qself: None, ref path }) => path,
_ => return None,
};
match extract_path_ident(path)?.as_ref() {
"i8" => Some(VectorType::I8),
"u8" => Some(VectorType::U8),
"i16" => Some(VectorType::I16),
"u16" => Some(VectorType::U16),
"i32" => Some(VectorType::I32),
"u32" => Some(VectorType::U32),
"f32" => Some(VectorType::F32),
"f64" => Some(VectorType::F64),
_ => None,
}
}
pub fn abi_element(&self) -> syn::Ident {
match *self {
VectorType::String => syn::Ident::from("u8"),
VectorType::I8 => syn::Ident::from("i8"),
VectorType::U8 => syn::Ident::from("u8"),
VectorType::I16 => syn::Ident::from("i16"),
VectorType::U16 => syn::Ident::from("u16"),
VectorType::I32 => syn::Ident::from("i32"),
VectorType::U32 => syn::Ident::from("u32"),
VectorType::F32 => syn::Ident::from("f32"),
VectorType::F64 => syn::Ident::from("f64"),
}
}
}
impl ToTokens for VectorType {
fn to_tokens(&self, tokens: &mut Tokens) {
let me = match *self {
VectorType::String => my_quote! { String },
VectorType::I8 => my_quote! { Vec<i8> },
VectorType::U8 => my_quote! { Vec<u8> },
VectorType::I16 => my_quote! { Vec<i16> },
VectorType::U16 => my_quote! { Vec<u16> },
VectorType::I32 => my_quote! { Vec<i32> },
VectorType::U32 => my_quote! { Vec<u32> },
VectorType::F32 => my_quote! { Vec<f32> },
VectorType::F64 => my_quote! { Vec<f64> },
};
me.to_tokens(tokens);
}
}

View File

@ -144,29 +144,40 @@ fn bindgen_export(export: &ast::Export, into: &mut Tokens) {
let i = i + offset; let i = i + offset;
let ident = syn::Ident::from(format!("arg{}", i)); let ident = syn::Ident::from(format!("arg{}", i));
match *ty { match *ty {
ast::Type::BorrowedStr => { ast::Type::Vector(ref ty, owned) => {
let ptr = syn::Ident::from(format!("arg{}_ptr", i)); let ptr = syn::Ident::from(format!("arg{}_ptr", i));
let len = syn::Ident::from(format!("arg{}_len", i)); let len = syn::Ident::from(format!("arg{}_len", i));
args.push(my_quote! { #ptr: *const u8 }); let abi_ty = ty.abi_element();
args.push(my_quote! { #ptr: *const #abi_ty });
args.push(my_quote! { #len: usize }); args.push(my_quote! { #len: usize });
if owned {
arg_conversions.push(my_quote! { arg_conversions.push(my_quote! {
let #ident = unsafe { let #ident = unsafe {
let slice = ::std::slice::from_raw_parts(#ptr, #len); ::std::vec::Vec::from_raw_parts(#ptr, #len, #len)
::std::str::from_utf8_unchecked(slice) };
});
} else {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::slice::from_raw_parts(#ptr, #len)
}; };
}); });
} }
ast::Type::String => { if let ast::VectorType::String = *ty {
let ptr = syn::Ident::from(format!("arg{}_ptr", i)); if owned {
let len = syn::Ident::from(format!("arg{}_len", i));
args.push(my_quote! { #ptr: *mut u8 });
args.push(my_quote! { #len: usize });
arg_conversions.push(my_quote! { arg_conversions.push(my_quote! {
let #ident = unsafe { let #ident = unsafe {
let vec = ::std::vec::Vec::from_raw_parts(#ptr, #len, #len); ::std::string::String::from_utf8_unchecked(#ident)
::std::string::String::from_utf8_unchecked(vec)
}; };
}); });
} else {
arg_conversions.push(my_quote! {
let #ident = unsafe {
::std::str::from_utf8_unchecked(#ident)
};
});
}
}
} }
ast::Type::ByValue(ref t) => { ast::Type::ByValue(ref t) => {
args.push(my_quote! { args.push(my_quote! {
@ -209,8 +220,8 @@ fn bindgen_export(export: &ast::Export, into: &mut Tokens) {
let ret_ty; let ret_ty;
let convert_ret; let convert_ret;
match export.function.ret { match export.function.ret {
Some(ast::Type::String) => { Some(ast::Type::Vector(ref ty, true)) => {
ret_ty = my_quote! { -> *mut String }; ret_ty = my_quote! { -> *mut #ty };
convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) }; convert_ret = my_quote! { Box::into_raw(Box::new(#ret)) };
} }
Some(ast::Type::ByValue(ref t)) => { Some(ast::Type::ByValue(ref t)) => {
@ -221,7 +232,7 @@ fn bindgen_export(export: &ast::Export, into: &mut Tokens) {
<#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret) <#t as ::wasm_bindgen::convert::WasmBoundary>::into_js(#ret)
}; };
} }
Some(ast::Type::BorrowedStr) | Some(ast::Type::Vector(_, false)) |
Some(ast::Type::ByMutRef(_)) | Some(ast::Type::ByMutRef(_)) |
Some(ast::Type::ByRef(_)) => { Some(ast::Type::ByRef(_)) => {
panic!("can't return a borrowed ref"); panic!("can't return a borrowed ref");
@ -344,17 +355,21 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
for (i, (ty, name)) in import.function.arguments.iter().zip(names).enumerate() { for (i, (ty, name)) in import.function.arguments.iter().zip(names).enumerate() {
match *ty { match *ty {
ast::Type::BorrowedStr => { ast::Type::Vector(ref ty, owned) => {
let ptr = syn::Ident::from(format!("{}_ptr", name)); let ptr = syn::Ident::from(format!("{}_ptr", name));
let len = syn::Ident::from(format!("{}_len", name)); let len = syn::Ident::from(format!("{}_len", name));
abi_argument_names.push(ptr); abi_argument_names.push(ptr);
abi_argument_names.push(len); abi_argument_names.push(len);
abi_arguments.push(my_quote! { #ptr: *const u8 }); let abi_ty = ty.abi_element();
abi_arguments.push(my_quote! { #ptr: *const #abi_ty });
abi_arguments.push(my_quote! { #len: usize }); abi_arguments.push(my_quote! { #len: usize });
arg_conversions.push(my_quote! { arg_conversions.push(my_quote! {
let #ptr = #name.as_ptr(); let #ptr = #name.as_ptr();
let #len = #name.len(); let #len = #name.len();
}); });
if owned {
arg_conversions.push(my_quote! { ::std::mem::forget(#name); });
}
} }
ast::Type::ByValue(ref t) => { ast::Type::ByValue(ref t) => {
abi_argument_names.push(name); abi_argument_names.push(name);
@ -389,20 +404,6 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
}); });
} }
} }
// TODO: need to test this
ast::Type::String => {
let ptr = syn::Ident::from(format!("{}_ptr", name));
let len = syn::Ident::from(format!("{}_len", name));
abi_argument_names.push(ptr);
abi_argument_names.push(len);
abi_arguments.push(my_quote! { #ptr: *const u8 });
abi_arguments.push(my_quote! { #len: usize });
arg_conversions.push(my_quote! {
let #ptr = #name.as_ptr();
let #len = #name.len();
::std::mem::forget(#name);
});
}
} }
} }
let abi_ret; let abi_ret;
@ -417,25 +418,31 @@ fn bindgen_import(import: &ast::Import, tokens: &mut Tokens) {
}; };
} }
// TODO: add a test for this Some(ast::Type::Vector(ref ty, true)) => {
Some(ast::Type::String) => { let name = syn::Ident::from("__ret_len");
let name = syn::Ident::from("__ret_strlen"); let name_ptr = syn::Ident::from("__ret_len_ptr");
let name_ptr = syn::Ident::from("__ret_strlen_ptr");
abi_argument_names.push(name_ptr); abi_argument_names.push(name_ptr);
abi_arguments.push(my_quote! { #name_ptr: *mut usize }); abi_arguments.push(my_quote! { #name_ptr: *mut usize });
arg_conversions.push(my_quote! { arg_conversions.push(my_quote! {
let mut #name = 0; let mut #name = 0;
let mut #name_ptr = &mut #name as *mut usize; let mut #name_ptr = &mut #name as *mut usize;
}); });
abi_ret = my_quote! { *mut u8 }; let abi_ty = ty.abi_element();
abi_ret = my_quote! { *mut #abi_ty };
if let ast::VectorType::String = *ty {
convert_ret = my_quote! { convert_ret = my_quote! {
String::from_utf8_unchecked( String::from_utf8_unchecked(
Vec::from_raw_parts(#ret_ident, #name, #name) Vec::from_raw_parts(#ret_ident, #name, #name)
) )
}; };
} else {
convert_ret = my_quote! {
Vec::from_raw_parts(#ret_ident, #name, #name)
};
}
} }
Some(ast::Type::BorrowedStr) |
Some(ast::Type::ByRef(_)) | Some(ast::Type::ByRef(_)) |
Some(ast::Type::Vector(_, false)) |
Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"), Some(ast::Type::ByMutRef(_)) => panic!("can't return a borrowed ref"),
None => { None => {
abi_ret = my_quote! { () }; abi_ret = my_quote! { () };

View File

@ -81,8 +81,24 @@ pub const TYPE_NUMBER: char = '\u{5e}';
pub const TYPE_BORROWED_STR: char = '\u{5f}'; pub const TYPE_BORROWED_STR: char = '\u{5f}';
pub const TYPE_STRING: char = '\u{60}'; pub const TYPE_STRING: char = '\u{60}';
pub const TYPE_BOOLEAN: char = '\u{61}'; pub const TYPE_BOOLEAN: char = '\u{61}';
pub const TYPE_JS_OWNED: char = '\u{62}'; pub const TYPE_SLICE_U8: char = '\u{62}';
pub const TYPE_JS_REF: char = '\u{63}'; pub const TYPE_VECTOR_U8: char = '\u{63}';
pub const TYPE_SLICE_I8: char = '\u{64}';
pub const TYPE_VECTOR_I8: char = '\u{65}';
pub const TYPE_SLICE_U16: char = '\u{66}';
pub const TYPE_VECTOR_U16: char = '\u{67}';
pub const TYPE_SLICE_I16: char = '\u{68}';
pub const TYPE_VECTOR_I16: char = '\u{69}';
pub const TYPE_SLICE_U32: char = '\u{6a}';
pub const TYPE_VECTOR_U32: char = '\u{6b}';
pub const TYPE_SLICE_I32: char = '\u{6c}';
pub const TYPE_VECTOR_I32: char = '\u{6d}';
pub const TYPE_VECTOR_F32: char = '\u{6e}';
pub const TYPE_SLICE_F32: char = '\u{6f}';
pub const TYPE_VECTOR_F64: char = '\u{70}';
pub const TYPE_SLICE_F64: char = '\u{71}';
pub const TYPE_JS_OWNED: char = '\u{72}';
pub const TYPE_JS_REF: char = '\u{73}';
pub const TYPE_CUSTOM_START: u32 = 0x64; pub const TYPE_CUSTOM_START: u32 = 0x64;
pub const TYPE_CUSTOM_REF_FLAG: u32 = 1; pub const TYPE_CUSTOM_REF_FLAG: u32 = 1;

232
tests/slice.rs Normal file
View File

@ -0,0 +1,232 @@
extern crate test_support;
#[test]
fn export() {
test_support::project()
.file("src/lib.rs", r#"
#![feature(proc_macro)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
macro_rules! doit {
($($i:ident)*) => ($(
#[no_mangle]
#[wasm_bindgen]
pub extern fn $i(a: &[$i]) -> Vec<$i> {
assert_eq!(a.len(), 2);
assert_eq!(a[0], 1 as $i);
assert_eq!(a[1], 2 as $i);
a.to_vec()
}
)*)
}
doit! { i8 u8 i16 u16 i32 u32 f32 f64 }
"#)
.file("test.ts", r#"
import * as assert from "assert";
import * as wasm from "./out";
function assert_arrays_equal(a: any, b: any) {
console.log(a, b);
assert.strictEqual(a.length, b.length);
assert.strictEqual(a.byteLength, b.byteLength);
for (let i = 0; i < a.length; i++) {
assert.strictEqual(a[i], b[i]);
}
}
export function test() {
const i8 = new Int8Array(2);
i8[0] = 1;
i8[1] = 2;
assert_arrays_equal(wasm.i8(i8), i8);
const u8 = new Uint8Array(2);
u8[0] = 1;
u8[1] = 2;
assert_arrays_equal(wasm.u8(u8), u8);
const i16 = new Int16Array(2);
i16[0] = 1;
i16[1] = 2;
assert_arrays_equal(wasm.i16(i16), i16);
const u16 = new Uint16Array(2);
u16[0] = 1;
u16[1] = 2;
assert_arrays_equal(wasm.u16(u16), u16);
const i32 = new Int32Array(2);
i32[0] = 1;
i32[1] = 2;
wasm.i32(i32);
assert_arrays_equal(wasm.i32(i32), i32);
const u32 = new Uint32Array(2);
u32[0] = 1;
u32[1] = 2;
assert_arrays_equal(wasm.u32(u32), u32);
const f32 = new Float32Array(2);
f32[0] = 1;
f32[1] = 2;
assert_arrays_equal(wasm.f32(f32), f32);
const f64 = new Float64Array(2);
f64[0] = 1;
f64[1] = 2;
assert_arrays_equal(wasm.f64(f64), f64);
}
"#)
.test();
}
#[test]
fn import() {
test_support::project()
.file("src/lib.rs", r#"
#![feature(proc_macro)]
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
macro_rules! doit {
($(($rust:ident, $js:ident, $i:ident))*) => ($(
#[wasm_bindgen(module = "./test")]
extern {
fn $js(a: &[$i]) -> Vec<$i>;
}
#[no_mangle]
#[wasm_bindgen]
pub extern fn $rust(a: &[$i]) -> Vec<$i> {
assert_eq!(a.len(), 2);
assert_eq!(a[0], 1 as $i);
assert_eq!(a[1], 2 as $i);
$js(a)
}
)*)
}
doit! {
(rust_i8, js_i8, i8)
(rust_u8, js_u8, u8)
(rust_i16, js_i16, i16)
(rust_u16, js_u16, u16)
(rust_i32, js_i32, i32)
(rust_u32, js_u32, u32)
(rust_f32, js_f32, f32)
(rust_f64, js_f64, f64)
}
"#)
.file("test.ts", r#"
import * as assert from "assert";
import * as wasm from "./out";
export function js_i8(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
export function js_u8(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
export function js_i16(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
export function js_u16(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
export function js_i32(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
export function js_u32(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
export function js_f32(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
export function js_f64(a: any): any {
assert.strictEqual(a.length, 2);
assert.strictEqual(a[0], 1);
assert.strictEqual(a[1], 2);
return a;
}
function assert_arrays_equal(a: any, b: any) {
console.log(a, b);
assert.strictEqual(a.length, b.length);
assert.strictEqual(a.byteLength, b.byteLength);
for (let i = 0; i < a.length; i++) {
assert.strictEqual(a[i], b[i]);
}
}
export function test() {
const i8 = new Int8Array(2);
i8[0] = 1;
i8[1] = 2;
assert_arrays_equal(wasm.rust_i8(i8), i8);
const u8 = new Uint8Array(2);
u8[0] = 1;
u8[1] = 2;
assert_arrays_equal(wasm.rust_u8(u8), u8);
const i16 = new Int16Array(2);
i16[0] = 1;
i16[1] = 2;
assert_arrays_equal(wasm.rust_i16(i16), i16);
const u16 = new Uint16Array(2);
u16[0] = 1;
u16[1] = 2;
assert_arrays_equal(wasm.rust_u16(u16), u16);
const i32 = new Int32Array(2);
i32[0] = 1;
i32[1] = 2;
assert_arrays_equal(wasm.rust_i32(i32), i32);
const u32 = new Uint32Array(2);
u32[0] = 1;
u32[1] = 2;
assert_arrays_equal(wasm.rust_u32(u32), u32);
const f32 = new Float32Array(2);
f32[0] = 1;
f32[1] = 2;
assert_arrays_equal(wasm.rust_f32(f32), f32);
const f64 = new Float64Array(2);
f64[0] = 1;
f64[1] = 2;
assert_arrays_equal(wasm.rust_f64(f64), f64);
}
"#)
.test();
}