Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,9 @@ csharp = ['dep:wit-bindgen-csharp']
csharp-mono = ['csharp']
moonbit = ['dep:wit-bindgen-moonbit']
async = []

[dev-dependencies]
wit-bindgen-core = { workspace = true }
wit-bindgen-rust = { workspace = true }
wit-bindgen-go = { workspace = true }
wit-parser = { workspace = true }
3 changes: 2 additions & 1 deletion crates/go/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2589,7 +2589,8 @@ func {camel}FromOwnHandle(handleValue int32) *{camel} {{
}}

func {camel}FromBorrowHandle(handleValue int32) *{camel} {{
return {camel}FromOwnHandle(handleValue)
handle := wit_runtime.MakeHandle(handleValue)
return &{camel}{{handle}}
}}
"#
);
Expand Down
65 changes: 65 additions & 0 deletions tests/go_borrow_handle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
fn go_codegen(wit: &str) -> String {
let mut resolve = wit_parser::Resolve::default();
let pkg = resolve.push_str("test.wit", wit).unwrap();
let world = resolve.select_world(&[pkg], None).unwrap();

let opts = wit_bindgen_go::Opts::default();
let mut generator = opts.build();
let mut files = wit_bindgen_core::Files::default();
generator.generate(&mut resolve, world, &mut files).unwrap();

let mut all_code = String::new();
for (_name, contents) in files.iter() {
all_code.push_str(&String::from_utf8_lossy(contents));
}
all_code
}

/// Verify that FromBorrowHandle for imported resources does NOT delegate
/// to FromOwnHandle (which registers a GC finalizer via AddCleanup).
/// Borrowed handles must not have GC finalizers because the caller retains
/// ownership; cleaning them up on GC would corrupt the resource table.
#[test]
fn go_borrow_handle_no_gc_finalizer() {
let wit = r#"
package test:borrow-handle;

interface resources {
resource blob;
peek: func(b: borrow<blob>) -> u32;
}

world test {
import resources;
}
"#;

let code = go_codegen(wit);

// Find the FromBorrowHandle function body
let borrow_fn_start = code
.find("FromBorrowHandle")
.expect("FromBorrowHandle function should exist in generated Go code");
let borrow_fn_body = &code[borrow_fn_start..];
// Get the function body up to the closing brace
let borrow_fn_end = borrow_fn_body
.find("\n}")
.expect("closing brace for FromBorrowHandle");
let borrow_fn_text = &borrow_fn_body[..borrow_fn_end];

// The FromBorrowHandle function should NOT call FromOwnHandle
assert!(
!borrow_fn_text.contains("FromOwnHandle"),
"FromBorrowHandle should not delegate to FromOwnHandle. \
Borrowed handles must not register GC finalizers.\n\
Function body:\n{borrow_fn_text}"
);

// The FromBorrowHandle function should NOT contain AddCleanup
assert!(
!borrow_fn_text.contains("AddCleanup"),
"FromBorrowHandle should not register GC cleanup. \
Borrowed handles are not owned by the callee.\n\
Function body:\n{borrow_fn_text}"
);
}
Loading