From 3271d40cc9d76bbcabe2b09b75903e1f341899b1 Mon Sep 17 00:00:00 2001 From: Robert Bastian <4706271+robertbastian@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:14:52 +0100 Subject: [PATCH 1/4] jss --- example/js/lib/api/diplomat-runtime.js | 97 ++++++++++--------- feature_tests/js/api/Float64Vec.js | 6 +- feature_tests/js/api/Foo.js | 11 ++- feature_tests/js/api/diplomat-runtime.js | 97 ++++++++++--------- feature_tests/js/docs/source/slices_ffi.rst | 6 -- tool/src/js/conversions.rs | 21 ++-- tool/src/js/docs.rs | 13 --- tool/src/js/runtime.mjs | 97 ++++++++++--------- ...ons__tests__str_borrowing@MyStruct.js.snap | 8 +- 9 files changed, 181 insertions(+), 175 deletions(-) diff --git a/example/js/lib/api/diplomat-runtime.js b/example/js/lib/api/diplomat-runtime.js index afb69ef0a..a63b24995 100644 --- a/example/js/lib/api/diplomat-runtime.js +++ b/example/js/lib/api/diplomat-runtime.js @@ -67,68 +67,73 @@ export function enumDiscriminant(wasm, ptr) { // or we can manually free the WASM memory if they don't. export class DiplomatBuf { static str8 = (wasm, string) => { - var utf8_len = 0; - for (const codepoint_string of string) { - let codepoint = codepoint_string.codePointAt(0); + var utf8Length = 0; + for (const codepointString of string) { + let codepoint = codepointString.codePointAt(0); if (codepoint < 0x80) { - utf8_len += 1 + utf8Length += 1 } else if (codepoint < 0x800) { - utf8_len += 2 + utf8Length += 2 } else if (codepoint < 0x10000) { - utf8_len += 3 + utf8Length += 3 } else { - utf8_len += 4 + utf8Length += 4 } } - return new DiplomatBuf(wasm, utf8_len, 1, buf => { - const result = (new TextEncoder()).encodeInto(string, buf); - console.assert(string.length == result.read && utf8_len == result.written, "UTF-8 write error"); - }) + + const ptr = wasm.diplomat_alloc(utf8Length, 1); + + const result = (new TextEncoder()).encodeInto(string, new Uint8Array(wasm.memory.buffer, ptr, utf8Length)); + console.assert(string.length == result.read && utf8Length == result.written, "UTF-8 write error"); + + return new DiplomatBuf(ptr, utf8Length, () => wasm.diplomat_free(ptr, utf8Length, 1)); } static str16 = (wasm, string) => { - return new DiplomatBuf(wasm, string.length, 2, buf => { - for (var i; i < string.length; i++) { - buf[i] = string.charCodeAt(i); - } - }) - } + const byteLength = string.length * 2; + const ptr = wasm.diplomat_alloc(byteLength, 2); - static slice = (wasm, slice, align) => { - // If the slice is not a Uint8Array, we have to convert to one, as that's the only - // thing we can write into the wasm buffer. - const bytes = slice.constructor.name == "Uint8Array" ? slice : new Uint8Array(slice); - return new DiplomatBuf(wasm, bytes.length, align, buf => buf.set(bytes)); + const destination = new Uint16Array(wasm.memory.buffer, ptr, byteLength); + for (var i; i < string.length; i++) { + destination[i] = string.charCodeAt(i); + } + + return new DiplomatBuf(ptr, string.length, () => wasm.diplomat_free(ptr, byteLength, 2)); } - constructor(wasm, size, align, encodeCallback) { - const ptr = wasm.diplomat_alloc(size, align); - encodeCallback(new Uint8Array(wasm.memory.buffer, ptr, size)); + static slice = (wasm, list, rustType) => { + const elementSize = rustType == "u8" || rustType == "i8" || rustType == "bool" ? 1 : + rustType == "u16" || rustType == "i16" ? 2 : + rustType == "u64" || rustType == "i64" || rustType == "f64" ? 8 : + 4; - this.ptr = ptr; - this.size = size; - this.free = () => { - const successfully_unregistered = DiplomatBuf_finalizer.unregister(this); - if (successfully_unregistered) { - wasm.diplomat_free(this.ptr, this.size, align); - } else { - console.error(`Failed to unregister DiplomatBuf at ${ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`); - } - } + const byteLength = list.length * elementSize; + const ptr = wasm.diplomat_alloc(byteLength, elementSize); + + // Create an array view of the buffer. This gives us the `set` method which correctly handles untyped values + const destination = + rustType == "u8" || rustType == "bool" ? new Uint8Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i8" ? new Int8Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u16" ? new Uint16Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i16" ? new Int16Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u32" || rustType == "usize" || rustType == "char" ? new Uint32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : + Float64Array(wasm.memory.buffer, ptr, byteLength); + destination.set(list); - DiplomatBuf_finalizer.register(this, { wasm, ptr, size, align }, this); + return new DiplomatBuf(ptr, list.length, () => wasm.diplomat_free(ptr, byteLength, elementSize)); } - leak = () => { - const successfully_unregistered = DiplomatBuf_finalizer.unregister(this); - if (successfully_unregistered) { - // leak - } else { - console.error(`Failed to unregister DiplomatBuf at ${this.ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`); - } + constructor(ptr, size, free) { + this.ptr = ptr; + this.size = size; + this.free = free; + this.leak = () => { }; + this.garbageCollect = () => DiplomatBufferFinalizer(this, this.free); } } -const DiplomatBuf_finalizer = new FinalizationRegistry(({ wasm, ptr, size, align }) => { - wasm.diplomat_free(ptr, size, align); -}); +const DiplomatBufferFinalizer = new FinalizationRegistry(free => free()); diff --git a/feature_tests/js/api/Float64Vec.js b/feature_tests/js/api/Float64Vec.js index c08874e3a..055baa22d 100644 --- a/feature_tests/js/api/Float64Vec.js +++ b/feature_tests/js/api/Float64Vec.js @@ -16,20 +16,20 @@ export class Float64Vec { } static new(arg_v) { - const buf_arg_v = diplomatRuntime.DiplomatBuf.slice(wasm, arg_v, 8); + const buf_arg_v = diplomatRuntime.DiplomatBuf.slice(wasm, arg_v, "f64"); const diplomat_out = new Float64Vec(wasm.Float64Vec_new(buf_arg_v.ptr, buf_arg_v.size), true, []); buf_arg_v.free(); return diplomat_out; } fill_slice(arg_v) { - const buf_arg_v = diplomatRuntime.DiplomatBuf.slice(wasm, arg_v, 8); + const buf_arg_v = diplomatRuntime.DiplomatBuf.slice(wasm, arg_v, "f64"); wasm.Float64Vec_fill_slice(this.underlying, buf_arg_v.ptr, buf_arg_v.size); buf_arg_v.free(); } set_value(arg_new_slice) { - const buf_arg_new_slice = diplomatRuntime.DiplomatBuf.slice(wasm, arg_new_slice, 8); + const buf_arg_new_slice = diplomatRuntime.DiplomatBuf.slice(wasm, arg_new_slice, "f64"); wasm.Float64Vec_set_value(this.underlying, buf_arg_new_slice.ptr, buf_arg_new_slice.size); buf_arg_new_slice.free(); } diff --git a/feature_tests/js/api/Foo.js b/feature_tests/js/api/Foo.js index 9179a21e9..82649f655 100644 --- a/feature_tests/js/api/Foo.js +++ b/feature_tests/js/api/Foo.js @@ -19,7 +19,9 @@ export class Foo { static new(arg_x) { const buf_arg_x = diplomatRuntime.DiplomatBuf.str8(wasm, arg_x); - return new Foo(wasm.Foo_new(buf_arg_x.ptr, buf_arg_x.size), true, [buf_arg_x]); + const diplomat_out = new Foo(wasm.Foo_new(buf_arg_x.ptr, buf_arg_x.size), true, [buf_arg_x]); + buf_arg_x.garbageCollect(); + return diplomat_out; } get_bar() { @@ -45,9 +47,12 @@ export class Foo { static extract_from_fields(arg_fields) { const field_a_arg_fields = arg_fields["a"]; - const buf_field_a_arg_fields = diplomatRuntime.DiplomatBuf.str16(wasm, field_a_arg_fields, 2); + const buf_field_a_arg_fields = diplomatRuntime.DiplomatBuf.str16(wasm, field_a_arg_fields); const field_b_arg_fields = arg_fields["b"]; const buf_field_b_arg_fields = diplomatRuntime.DiplomatBuf.str8(wasm, field_b_arg_fields); - return new Foo(wasm.Foo_extract_from_fields(buf_field_a_arg_fields.ptr, buf_field_a_arg_fields.size, buf_field_b_arg_fields.ptr, buf_field_b_arg_fields.size), true, [buf_field_a_arg_fields, buf_field_b_arg_fields]); + const diplomat_out = new Foo(wasm.Foo_extract_from_fields(buf_field_a_arg_fields.ptr, buf_field_a_arg_fields.size, buf_field_b_arg_fields.ptr, buf_field_b_arg_fields.size), true, [buf_field_a_arg_fields, buf_field_b_arg_fields]); + buf_field_a_arg_fields.garbageCollect(); + buf_field_b_arg_fields.garbageCollect(); + return diplomat_out; } } diff --git a/feature_tests/js/api/diplomat-runtime.js b/feature_tests/js/api/diplomat-runtime.js index afb69ef0a..a63b24995 100644 --- a/feature_tests/js/api/diplomat-runtime.js +++ b/feature_tests/js/api/diplomat-runtime.js @@ -67,68 +67,73 @@ export function enumDiscriminant(wasm, ptr) { // or we can manually free the WASM memory if they don't. export class DiplomatBuf { static str8 = (wasm, string) => { - var utf8_len = 0; - for (const codepoint_string of string) { - let codepoint = codepoint_string.codePointAt(0); + var utf8Length = 0; + for (const codepointString of string) { + let codepoint = codepointString.codePointAt(0); if (codepoint < 0x80) { - utf8_len += 1 + utf8Length += 1 } else if (codepoint < 0x800) { - utf8_len += 2 + utf8Length += 2 } else if (codepoint < 0x10000) { - utf8_len += 3 + utf8Length += 3 } else { - utf8_len += 4 + utf8Length += 4 } } - return new DiplomatBuf(wasm, utf8_len, 1, buf => { - const result = (new TextEncoder()).encodeInto(string, buf); - console.assert(string.length == result.read && utf8_len == result.written, "UTF-8 write error"); - }) + + const ptr = wasm.diplomat_alloc(utf8Length, 1); + + const result = (new TextEncoder()).encodeInto(string, new Uint8Array(wasm.memory.buffer, ptr, utf8Length)); + console.assert(string.length == result.read && utf8Length == result.written, "UTF-8 write error"); + + return new DiplomatBuf(ptr, utf8Length, () => wasm.diplomat_free(ptr, utf8Length, 1)); } static str16 = (wasm, string) => { - return new DiplomatBuf(wasm, string.length, 2, buf => { - for (var i; i < string.length; i++) { - buf[i] = string.charCodeAt(i); - } - }) - } + const byteLength = string.length * 2; + const ptr = wasm.diplomat_alloc(byteLength, 2); - static slice = (wasm, slice, align) => { - // If the slice is not a Uint8Array, we have to convert to one, as that's the only - // thing we can write into the wasm buffer. - const bytes = slice.constructor.name == "Uint8Array" ? slice : new Uint8Array(slice); - return new DiplomatBuf(wasm, bytes.length, align, buf => buf.set(bytes)); + const destination = new Uint16Array(wasm.memory.buffer, ptr, byteLength); + for (var i; i < string.length; i++) { + destination[i] = string.charCodeAt(i); + } + + return new DiplomatBuf(ptr, string.length, () => wasm.diplomat_free(ptr, byteLength, 2)); } - constructor(wasm, size, align, encodeCallback) { - const ptr = wasm.diplomat_alloc(size, align); - encodeCallback(new Uint8Array(wasm.memory.buffer, ptr, size)); + static slice = (wasm, list, rustType) => { + const elementSize = rustType == "u8" || rustType == "i8" || rustType == "bool" ? 1 : + rustType == "u16" || rustType == "i16" ? 2 : + rustType == "u64" || rustType == "i64" || rustType == "f64" ? 8 : + 4; - this.ptr = ptr; - this.size = size; - this.free = () => { - const successfully_unregistered = DiplomatBuf_finalizer.unregister(this); - if (successfully_unregistered) { - wasm.diplomat_free(this.ptr, this.size, align); - } else { - console.error(`Failed to unregister DiplomatBuf at ${ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`); - } - } + const byteLength = list.length * elementSize; + const ptr = wasm.diplomat_alloc(byteLength, elementSize); + + // Create an array view of the buffer. This gives us the `set` method which correctly handles untyped values + const destination = + rustType == "u8" || rustType == "bool" ? new Uint8Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i8" ? new Int8Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u16" ? new Uint16Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i16" ? new Int16Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u32" || rustType == "usize" || rustType == "char" ? new Uint32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : + Float64Array(wasm.memory.buffer, ptr, byteLength); + destination.set(list); - DiplomatBuf_finalizer.register(this, { wasm, ptr, size, align }, this); + return new DiplomatBuf(ptr, list.length, () => wasm.diplomat_free(ptr, byteLength, elementSize)); } - leak = () => { - const successfully_unregistered = DiplomatBuf_finalizer.unregister(this); - if (successfully_unregistered) { - // leak - } else { - console.error(`Failed to unregister DiplomatBuf at ${this.ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`); - } + constructor(ptr, size, free) { + this.ptr = ptr; + this.size = size; + this.free = free; + this.leak = () => { }; + this.garbageCollect = () => DiplomatBufferFinalizer(this, this.free); } } -const DiplomatBuf_finalizer = new FinalizationRegistry(({ wasm, ptr, size, align }) => { - wasm.diplomat_free(ptr, size, align); -}); +const DiplomatBufferFinalizer = new FinalizationRegistry(free => free()); diff --git a/feature_tests/js/docs/source/slices_ffi.rst b/feature_tests/js/docs/source/slices_ffi.rst index 76ea741ca..c9c0dc565 100644 --- a/feature_tests/js/docs/source/slices_ffi.rst +++ b/feature_tests/js/docs/source/slices_ffi.rst @@ -4,16 +4,10 @@ .. js:class:: Float64Vec .. js:function:: new(v) - - Note: ``v`` should be an ArrayBuffer or TypedArray corresponding to the slice type expected by Rust. - .. js:method:: fill_slice(v) - - Note: ``v`` should be an ArrayBuffer or TypedArray corresponding to the slice type expected by Rust. - .. js:method:: set_value(new_slice) - - Note: ``new_slice`` should be an ArrayBuffer or TypedArray corresponding to the slice type expected by Rust. - .. js:class:: MyString diff --git a/tool/src/js/conversions.rs b/tool/src/js/conversions.rs index 8f69aba9f..8fcfeda05 100644 --- a/tool/src/js/conversions.rs +++ b/tool/src/js/conversions.rs @@ -121,20 +121,20 @@ pub fn gen_value_js_to_rust<'env>( // clean this up. if let ast::TypeName::PrimitiveSlice(.., prim) = typ { pre_logic.push(format!( - "const {param_name_buf} = diplomatRuntime.DiplomatBuf.slice(wasm, {param_name}, {align});", - align = layout::primitive_size_alignment(*prim).align() + "const {param_name_buf} = diplomatRuntime.DiplomatBuf.slice(wasm, {param_name}, {rust_type:?});", + rust_type = prim.to_string(), )); - } else if matches!( - typ, - ast::TypeName::StrReference(_, ast::StringEncoding::UnvalidatedUtf16) - ) { + } else if let ast::TypeName::StrReference(_, encoding) = typ { pre_logic.push(format!( - "const {param_name_buf} = diplomatRuntime.DiplomatBuf.str16(wasm, {param_name}, 2);", + "const {param_name_buf} = diplomatRuntime.DiplomatBuf.{}(wasm, {param_name});", + match encoding { + ast::StringEncoding::UnvalidatedUtf8 => "str8", + ast::StringEncoding::UnvalidatedUtf16 => "str16", + _ => unreachable!("unknown AST/HIR variant"), + } )); } else { - pre_logic.push(format!( - "const {param_name_buf} = diplomatRuntime.DiplomatBuf.str8(wasm, {param_name});" - )); + unreachable!("unknown AST/HIR variant"); } invocation_params.push(format!("{param_name_buf}.ptr")); @@ -144,6 +144,7 @@ pub fn gen_value_js_to_rust<'env>( .as_named() .and_then(|current| borrowed_current_to_root.get(current)) { + post_logic.push(format!("{param_name_buf}.garbageCollect();")); entries.entry(named).or_default().push(param_name_buf); } else if lifetime == &ast::Lifetime::Static { post_logic.push(format!("{param_name_buf}.leak();")); diff --git a/tool/src/js/docs.rs b/tool/src/js/docs.rs index f96cd91bf..850bf95c9 100644 --- a/tool/src/js/docs.rs +++ b/tool/src/js/docs.rs @@ -142,19 +142,6 @@ pub fn gen_method_docs( writeln!(method_indented)?; } - for p in method - .params - .iter() - .filter(|p| matches!(p.ty, ast::TypeName::PrimitiveSlice(..))) - { - writeln!( - method_indented, - "- Note: ``{}`` should be an ArrayBuffer or TypedArray corresponding to the slice type expected by Rust.", - p.name - )?; - writeln!(method_indented)?; - } - let static_borrows = method.borrowed_params(); let static_borrows = static_borrows.static_names().collect::>(); if !static_borrows.is_empty() { diff --git a/tool/src/js/runtime.mjs b/tool/src/js/runtime.mjs index afb69ef0a..a63b24995 100644 --- a/tool/src/js/runtime.mjs +++ b/tool/src/js/runtime.mjs @@ -67,68 +67,73 @@ export function enumDiscriminant(wasm, ptr) { // or we can manually free the WASM memory if they don't. export class DiplomatBuf { static str8 = (wasm, string) => { - var utf8_len = 0; - for (const codepoint_string of string) { - let codepoint = codepoint_string.codePointAt(0); + var utf8Length = 0; + for (const codepointString of string) { + let codepoint = codepointString.codePointAt(0); if (codepoint < 0x80) { - utf8_len += 1 + utf8Length += 1 } else if (codepoint < 0x800) { - utf8_len += 2 + utf8Length += 2 } else if (codepoint < 0x10000) { - utf8_len += 3 + utf8Length += 3 } else { - utf8_len += 4 + utf8Length += 4 } } - return new DiplomatBuf(wasm, utf8_len, 1, buf => { - const result = (new TextEncoder()).encodeInto(string, buf); - console.assert(string.length == result.read && utf8_len == result.written, "UTF-8 write error"); - }) + + const ptr = wasm.diplomat_alloc(utf8Length, 1); + + const result = (new TextEncoder()).encodeInto(string, new Uint8Array(wasm.memory.buffer, ptr, utf8Length)); + console.assert(string.length == result.read && utf8Length == result.written, "UTF-8 write error"); + + return new DiplomatBuf(ptr, utf8Length, () => wasm.diplomat_free(ptr, utf8Length, 1)); } static str16 = (wasm, string) => { - return new DiplomatBuf(wasm, string.length, 2, buf => { - for (var i; i < string.length; i++) { - buf[i] = string.charCodeAt(i); - } - }) - } + const byteLength = string.length * 2; + const ptr = wasm.diplomat_alloc(byteLength, 2); - static slice = (wasm, slice, align) => { - // If the slice is not a Uint8Array, we have to convert to one, as that's the only - // thing we can write into the wasm buffer. - const bytes = slice.constructor.name == "Uint8Array" ? slice : new Uint8Array(slice); - return new DiplomatBuf(wasm, bytes.length, align, buf => buf.set(bytes)); + const destination = new Uint16Array(wasm.memory.buffer, ptr, byteLength); + for (var i; i < string.length; i++) { + destination[i] = string.charCodeAt(i); + } + + return new DiplomatBuf(ptr, string.length, () => wasm.diplomat_free(ptr, byteLength, 2)); } - constructor(wasm, size, align, encodeCallback) { - const ptr = wasm.diplomat_alloc(size, align); - encodeCallback(new Uint8Array(wasm.memory.buffer, ptr, size)); + static slice = (wasm, list, rustType) => { + const elementSize = rustType == "u8" || rustType == "i8" || rustType == "bool" ? 1 : + rustType == "u16" || rustType == "i16" ? 2 : + rustType == "u64" || rustType == "i64" || rustType == "f64" ? 8 : + 4; - this.ptr = ptr; - this.size = size; - this.free = () => { - const successfully_unregistered = DiplomatBuf_finalizer.unregister(this); - if (successfully_unregistered) { - wasm.diplomat_free(this.ptr, this.size, align); - } else { - console.error(`Failed to unregister DiplomatBuf at ${ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`); - } - } + const byteLength = list.length * elementSize; + const ptr = wasm.diplomat_alloc(byteLength, elementSize); + + // Create an array view of the buffer. This gives us the `set` method which correctly handles untyped values + const destination = + rustType == "u8" || rustType == "bool" ? new Uint8Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i8" ? new Int8Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u16" ? new Uint16Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i16" ? new Int16Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u32" || rustType == "usize" || rustType == "char" ? new Uint32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : + Float64Array(wasm.memory.buffer, ptr, byteLength); + destination.set(list); - DiplomatBuf_finalizer.register(this, { wasm, ptr, size, align }, this); + return new DiplomatBuf(ptr, list.length, () => wasm.diplomat_free(ptr, byteLength, elementSize)); } - leak = () => { - const successfully_unregistered = DiplomatBuf_finalizer.unregister(this); - if (successfully_unregistered) { - // leak - } else { - console.error(`Failed to unregister DiplomatBuf at ${this.ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`); - } + constructor(ptr, size, free) { + this.ptr = ptr; + this.size = size; + this.free = free; + this.leak = () => { }; + this.garbageCollect = () => DiplomatBufferFinalizer(this, this.free); } } -const DiplomatBuf_finalizer = new FinalizationRegistry(({ wasm, ptr, size, align }) => { - wasm.diplomat_free(ptr, size, align); -}); +const DiplomatBufferFinalizer = new FinalizationRegistry(free => free()); diff --git a/tool/src/js/snapshots/diplomat_tool__js__conversions__tests__str_borrowing@MyStruct.js.snap b/tool/src/js/snapshots/diplomat_tool__js__conversions__tests__str_borrowing@MyStruct.js.snap index 6840f1481..a364e7bd0 100644 --- a/tool/src/js/snapshots/diplomat_tool__js__conversions__tests__str_borrowing@MyStruct.js.snap +++ b/tool/src/js/snapshots/diplomat_tool__js__conversions__tests__str_borrowing@MyStruct.js.snap @@ -15,25 +15,29 @@ export class MyStruct { static new(arg_s) { const buf_arg_s = diplomatRuntime.DiplomatBuf.str8(wasm, arg_s); - return (() => { + const diplomat_out = (() => { const diplomat_receive_buffer = wasm.diplomat_alloc(8, 4); wasm.MyStruct_new(diplomat_receive_buffer, buf_arg_s.ptr, buf_arg_s.size); const out = new MyStruct(diplomat_receive_buffer, [buf_arg_s]); wasm.diplomat_free(diplomat_receive_buffer, 8, 4); return out; })(); + buf_arg_s.garbageCollect(); + return diplomat_out; } get() { const field_s_this = this["s"]; const buf_field_s_this = diplomatRuntime.DiplomatBuf.str8(wasm, field_s_this); - return (() => { + const diplomat_out = (() => { const diplomat_receive_buffer = wasm.diplomat_alloc(8, 4); wasm.MyStruct_get(diplomat_receive_buffer, buf_field_s_this.ptr, buf_field_s_this.size); const [ptr, size] = new Uint32Array(wasm.memory.buffer, diplomat_receive_buffer, 2); wasm.diplomat_free(diplomat_receive_buffer, 8, 4); return diplomatRuntime.readString8(wasm, ptr, size); })(); + buf_field_s_this.garbageCollect(); + return diplomat_out; } } From ea0dcc4c84f9650c5b9c70f00c52ed98d5ec5310 Mon Sep 17 00:00:00 2001 From: Robert Bastian <4706271+robertbastian@users.noreply.github.com> Date: Thu, 23 Nov 2023 16:43:35 +0100 Subject: [PATCH 2/4] fix --- example/js/lib/api/diplomat-runtime.js | 12 ++++++------ feature_tests/js/api/diplomat-runtime.js | 12 ++++++------ tool/src/js/conversions.rs | 2 +- tool/src/js/runtime.mjs | 12 ++++++------ 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/example/js/lib/api/diplomat-runtime.js b/example/js/lib/api/diplomat-runtime.js index a63b24995..f956561c8 100644 --- a/example/js/lib/api/diplomat-runtime.js +++ b/example/js/lib/api/diplomat-runtime.js @@ -116,12 +116,12 @@ export class DiplomatBuf { rustType == "i8" ? new Int8Array(wasm.memory.buffer, ptr, byteLength) : rustType == "u16" ? new Uint16Array(wasm.memory.buffer, ptr, byteLength) : rustType == "i16" ? new Int16Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "u32" || rustType == "usize" || rustType == "char" ? new Uint32Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : - Float64Array(wasm.memory.buffer, ptr, byteLength); + rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f64" ? new Float64Array(wasm.memory.buffer, ptr, byteLength) : + new Uint32Array(wasm.memory.buffer, ptr, byteLength); destination.set(list); return new DiplomatBuf(ptr, list.length, () => wasm.diplomat_free(ptr, byteLength, elementSize)); diff --git a/feature_tests/js/api/diplomat-runtime.js b/feature_tests/js/api/diplomat-runtime.js index a63b24995..f956561c8 100644 --- a/feature_tests/js/api/diplomat-runtime.js +++ b/feature_tests/js/api/diplomat-runtime.js @@ -116,12 +116,12 @@ export class DiplomatBuf { rustType == "i8" ? new Int8Array(wasm.memory.buffer, ptr, byteLength) : rustType == "u16" ? new Uint16Array(wasm.memory.buffer, ptr, byteLength) : rustType == "i16" ? new Int16Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "u32" || rustType == "usize" || rustType == "char" ? new Uint32Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : - Float64Array(wasm.memory.buffer, ptr, byteLength); + rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f64" ? new Float64Array(wasm.memory.buffer, ptr, byteLength) : + new Uint32Array(wasm.memory.buffer, ptr, byteLength); destination.set(list); return new DiplomatBuf(ptr, list.length, () => wasm.diplomat_free(ptr, byteLength, elementSize)); diff --git a/tool/src/js/conversions.rs b/tool/src/js/conversions.rs index 8fcfeda05..283e6f460 100644 --- a/tool/src/js/conversions.rs +++ b/tool/src/js/conversions.rs @@ -130,7 +130,7 @@ pub fn gen_value_js_to_rust<'env>( match encoding { ast::StringEncoding::UnvalidatedUtf8 => "str8", ast::StringEncoding::UnvalidatedUtf16 => "str16", - _ => unreachable!("unknown AST/HIR variant"), + _ => unreachable!("unknown AST/HIR variant"), } )); } else { diff --git a/tool/src/js/runtime.mjs b/tool/src/js/runtime.mjs index a63b24995..f956561c8 100644 --- a/tool/src/js/runtime.mjs +++ b/tool/src/js/runtime.mjs @@ -116,12 +116,12 @@ export class DiplomatBuf { rustType == "i8" ? new Int8Array(wasm.memory.buffer, ptr, byteLength) : rustType == "u16" ? new Uint16Array(wasm.memory.buffer, ptr, byteLength) : rustType == "i16" ? new Int16Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "u32" || rustType == "usize" || rustType == "char" ? new Uint32Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : - rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : - Float64Array(wasm.memory.buffer, ptr, byteLength); + rustType == "i32" || rustType == "isize" ? new Int32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "u64" ? new BigUint64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "i64" ? new BigInt64Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f32" ? new Float32Array(wasm.memory.buffer, ptr, byteLength) : + rustType == "f64" ? new Float64Array(wasm.memory.buffer, ptr, byteLength) : + new Uint32Array(wasm.memory.buffer, ptr, byteLength); destination.set(list); return new DiplomatBuf(ptr, list.length, () => wasm.diplomat_free(ptr, byteLength, elementSize)); From 58af00c8e5b2bf62a357ea8622ce9962c87c8f54 Mon Sep 17 00:00:00 2001 From: Robert Bastian <4706271+robertbastian@users.noreply.github.com> Date: Mon, 27 Nov 2023 08:33:31 +0100 Subject: [PATCH 3/4] comment --- tool/src/js/runtime.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tool/src/js/runtime.mjs b/tool/src/js/runtime.mjs index f956561c8..715920731 100644 --- a/tool/src/js/runtime.mjs +++ b/tool/src/js/runtime.mjs @@ -130,6 +130,9 @@ export class DiplomatBuf { constructor(ptr, size, free) { this.ptr = ptr; this.size = size; + // Generated code calls one of methods these for each allocation, to either + // free directly after the FFI call, to leak (to create a &'static), or to + // register the buffer with the garbage collector (to create a &'a). this.free = free; this.leak = () => { }; this.garbageCollect = () => DiplomatBufferFinalizer(this, this.free); From 29de879477563e25b153a89f7528fec29e0076d0 Mon Sep 17 00:00:00 2001 From: Robert Bastian <4706271+robertbastian@users.noreply.github.com> Date: Mon, 27 Nov 2023 08:56:24 +0100 Subject: [PATCH 4/4] gen --- example/js/lib/api/diplomat-runtime.js | 3 +++ feature_tests/js/api/diplomat-runtime.js | 3 +++ 2 files changed, 6 insertions(+) diff --git a/example/js/lib/api/diplomat-runtime.js b/example/js/lib/api/diplomat-runtime.js index f956561c8..715920731 100644 --- a/example/js/lib/api/diplomat-runtime.js +++ b/example/js/lib/api/diplomat-runtime.js @@ -130,6 +130,9 @@ export class DiplomatBuf { constructor(ptr, size, free) { this.ptr = ptr; this.size = size; + // Generated code calls one of methods these for each allocation, to either + // free directly after the FFI call, to leak (to create a &'static), or to + // register the buffer with the garbage collector (to create a &'a). this.free = free; this.leak = () => { }; this.garbageCollect = () => DiplomatBufferFinalizer(this, this.free); diff --git a/feature_tests/js/api/diplomat-runtime.js b/feature_tests/js/api/diplomat-runtime.js index f956561c8..715920731 100644 --- a/feature_tests/js/api/diplomat-runtime.js +++ b/feature_tests/js/api/diplomat-runtime.js @@ -130,6 +130,9 @@ export class DiplomatBuf { constructor(ptr, size, free) { this.ptr = ptr; this.size = size; + // Generated code calls one of methods these for each allocation, to either + // free directly after the FFI call, to leak (to create a &'static), or to + // register the buffer with the garbage collector (to create a &'a). this.free = free; this.leak = () => { }; this.garbageCollect = () => DiplomatBufferFinalizer(this, this.free);