Skip to content
Merged
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
4 changes: 2 additions & 2 deletions baml_language/crates/bex_engine/tests/host_value_callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,12 @@ fn complete_with_test_error(call_id: u32, class_name: &str, message: &str) {
);
fields.insert("traceback".to_string(), BexExternalValue::Null);
// The class's `_handle $rust_type` slot is required by the engine's
// structural check. Use a synthetic `HostValue(kind=Error)` handle
// structural check. Use a synthetic `HostValue(kind=Opaque)` handle
// so the BAML→host decoder has *something* to round-trip — the
// test doesn't rehydrate, so the key value is arbitrary.
fields.insert(
"_handle".to_string(),
BexExternalValue::HostValue(HostValueArc::new(next_host_key(), HostValueKind::Error)),
BexExternalValue::HostValue(HostValueArc::new(next_host_key(), HostValueKind::Opaque)),
);
sys_native::host_dispatch::complete_with_throw(
call_id,
Expand Down
26 changes: 14 additions & 12 deletions baml_language/crates/bex_resource_types/src/host_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ use once_cell::sync::Lazy;
pub enum HostValueKind {
/// A host-language callable (function/closure/method).
Callable,
/// An opaque host-language error value (a native exception that has no
/// BAML representation). Holds a reference back to the host error object
/// so the originating host can recover the exact native exception on
/// round-trip. Surfaced in BAML as `baml.errors.HostCallable`.
Error,
/// An arbitrary host-language value with no BAML representation, referenced
/// by key and round-tripped by identity. Surfaces in BAML as an opaque
/// `$rust_type` value (`Ty::RustType`). A native host exception is one
/// consumer: the bridge registers it as an opaque value and wraps the
/// handle in `baml.errors.HostCallable`.
Opaque,
}

/// Drop-on-last-clone notification fired to the host language.
Expand Down Expand Up @@ -562,18 +563,19 @@ mod tests {
);
}

/// `HostValueKind::Error` (added for opaque host-callable error round-trip)
/// must follow the same intern/refcount/release contract as `Callable`.
/// `HostValueKind::Opaque` (used for opaque host-value round-trip, e.g. the
/// host-callable error wrapper) must follow the same intern/refcount/release
/// contract as `Callable`.
#[test]
fn error_kind_release_fires_on_last_drop() {
fn opaque_kind_release_fires_on_last_drop() {
let _guard = lock_and_reset();
let key = 200;
let arc = HostValueArc::new(key, HostValueKind::Error);
let arc = HostValueArc::new(key, HostValueKind::Opaque);
drop(arc);
assert_eq!(
FIRED.lock().unwrap().as_slice(),
&[key],
"Error-kind release must fire just like Callable"
"Opaque-kind release must fire just like Callable"
);
}

Expand All @@ -584,11 +586,11 @@ mod tests {
/// the assert to a runtime check) is an intentional change.
#[cfg(not(debug_assertions))]
#[test]
fn intern_collision_callable_vs_error_release_build_keeps_existing() {
fn intern_collision_callable_vs_opaque_release_build_keeps_existing() {
let _guard = lock_and_reset();
let key = 201;
let callable = HostValueArc::intern(key, HostValueKind::Callable);
let coerced = HostValueArc::intern(key, HostValueKind::Error);
let coerced = HostValueArc::intern(key, HostValueKind::Opaque);
assert!(
Arc::ptr_eq(&callable, &coerced),
"release-build intern collision must alias to the existing Arc"
Expand Down
14 changes: 7 additions & 7 deletions baml_language/crates/bridge_ctypes/src/value_decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ pub fn inbound_to_external(
let host_value_kind =
if handle.handle_type == BamlHandleType::HostValueCallable as i32 {
Some(bex_project::HostValueKind::Callable)
} else if handle.handle_type == BamlHandleType::HostValueError as i32 {
Some(bex_project::HostValueKind::Error)
} else if handle.handle_type == BamlHandleType::HostValueOpaque as i32 {
Some(bex_project::HostValueKind::Opaque)
} else {
None
};
Expand Down Expand Up @@ -226,11 +226,11 @@ mod tests {
}

#[test]
fn decode_inbound_host_value_error() {
fn decode_inbound_host_value_opaque() {
let table = CffiHandleTable::new();
let handle = BamlHandle {
key: 777,
handle_type: BamlHandleType::HostValueError as i32,
handle_type: BamlHandleType::HostValueOpaque as i32,
};
let inbound = InboundValue {
value: Some(InboundValueVariant::Handle(handle)),
Expand All @@ -239,14 +239,14 @@ mod tests {
match result {
BexExternalValue::HostValue(arc) => {
assert_eq!(arc.key, 777);
assert_eq!(arc.kind, bex_project::HostValueKind::Error);
assert_eq!(arc.kind, bex_project::HostValueKind::Opaque);
}
other => panic!("unexpected variant: {other:?}"),
}
// Like callables, opaque error handles bypass the HANDLE_TABLE.
// Like callables, opaque handles bypass the HANDLE_TABLE.
assert!(
table.resolve(777).is_none(),
"HOST_VALUE_ERROR must not touch HANDLE_TABLE"
"HOST_VALUE_OPAQUE must not touch HANDLE_TABLE"
);
}

Expand Down
12 changes: 6 additions & 6 deletions baml_language/crates/bridge_ctypes/src/value_encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ pub fn external_to_outbound(
use crate::baml_core::cffi::BamlHandleType;
let ht = match arc.kind {
bex_project::HostValueKind::Callable => BamlHandleType::HostValueCallable as i32,
bex_project::HostValueKind::Error => BamlHandleType::HostValueError as i32,
bex_project::HostValueKind::Opaque => BamlHandleType::HostValueOpaque as i32,
};
Some(BamlValueVariant::HandleValue(BamlOutboundHandle {
key: arc.key,
Expand Down Expand Up @@ -499,8 +499,8 @@ mod tests {
}

#[test]
fn encode_outbound_host_value_error() {
let arc = HostValueArc::new(42, HostValueKind::Error);
fn encode_outbound_host_value_opaque() {
let arc = HostValueArc::new(42, HostValueKind::Opaque);
let value = BexExternalValue::HostValue(arc);
let options = CffiHandleTableOptions::for_in_process();
let encoded = external_to_outbound(&value, &options).expect("encode succeeds");
Expand All @@ -509,11 +509,11 @@ mod tests {
other => panic!("unexpected: {other:?}"),
};
assert_eq!(handle.key, 42);
assert_eq!(handle.handle_type, BamlHandleType::HostValueError as i32);
// Like callables, opaque error handles bypass the HANDLE_TABLE.
assert_eq!(handle.handle_type, BamlHandleType::HostValueOpaque as i32);
// Like callables, opaque handles bypass the HANDLE_TABLE.
assert!(
options.table.resolve(42).is_none(),
"HOST_VALUE_ERROR must not be inserted into HANDLE_TABLE"
"HOST_VALUE_OPAQUE must not be inserted into HANDLE_TABLE"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,14 @@ enum BamlHandleType {
// Lifetime: Rust drop notifies host via HostReleaseFn (not via
// HANDLE_TABLE). See bex_external_types::host_value.
HOST_VALUE_CALLABLE = 15;
// Host-owned opaque error value (a native exception with no BAML
// representation), referenced via the same per-bridge registry and
// HostReleaseFn lifetime as HOST_VALUE_CALLABLE. Surfaced in BAML as
// baml.errors.HostCallable. See bex_external_types::host_value.
HOST_VALUE_ERROR = 16;
// Host-owned opaque value: an arbitrary host object with no BAML
// representation, referenced via the per-bridge HostValueRegistry and
// released via HostReleaseFn (not via HANDLE_TABLE), same as
// HOST_VALUE_CALLABLE. A host-thrown native exception is one consumer:
// the bridge registers it as an opaque value and wraps the handle in a
// baml.errors.HostCallable instance for throw semantics. See
// bex_external_types::host_value.
HOST_VALUE_OPAQUE = 16;
}

message BamlHandle {
Expand Down
8 changes: 4 additions & 4 deletions baml_language/crates/bridge_wasm/src/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ fn type_name(ht: BamlHandleType) -> &'static str {
// Host-owned callables are tracked per-bridge, not in HANDLE_TABLE.
// The key here is the bridge-side identity passed in from the host.
BamlHandleType::HostValueCallable => "host_value_callable",
// Host-owned opaque error values: same per-bridge tracking as callables.
BamlHandleType::HostValueError => "host_value_error",
// Host-owned opaque values: same per-bridge tracking as callables.
BamlHandleType::HostValueOpaque => "host_value_opaque",
}
}

Expand Down Expand Up @@ -109,7 +109,7 @@ impl BamlHandle {

impl Drop for BamlHandle {
fn drop(&mut self) {
// Host-owned handles (`HostValueCallable`, `HostValueError`) are
// Host-owned handles (`HostValueCallable`, `HostValueOpaque`) are
// *not* tracked in HANDLE_TABLE — their lifetime is managed
// per-bridge via the `HostReleaseFn` dispatch. Releasing them here
// would call into the global HANDLE_TABLE with a key that may
Expand All @@ -118,7 +118,7 @@ impl Drop for BamlHandle {
// it. Skip the table release for those variants; their owners drop
// them through their own release path.
match self.handle_type {
BamlHandleType::HostValueCallable | BamlHandleType::HostValueError => {}
BamlHandleType::HostValueCallable | BamlHandleType::HostValueOpaque => {}
_ => {
let _ = HANDLE_TABLE.release(self.key);
}
Expand Down
2 changes: 1 addition & 1 deletion baml_language/crates/bridge_wasm/tests/host_callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ fn make_dispatch_error() -> js_sys::Function {
value: Some(InboundValue {
value: Some(InboundVariant::Handle(BamlHandle {
key: UNRESOLVED_HOST_ERROR_KEY,
handle_type: BamlHandleType::HostValueError as i32,
handle_type: BamlHandleType::HostValueOpaque as i32,
})),
}),
};
Expand Down
7 changes: 4 additions & 3 deletions baml_language/crates/sys_native/src/host_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ impl io::IoNamespaceHost for NativeSysOps {
// Extract the HostValueArc from the incoming handle and confirm
// it's a callable. Only `HostValueKind::Callable` is dispatchable —
// the bridge's dispatcher invokes a host *function* through this
// path. An `HostValueKind::Error` arc represents an opaque
// host-thrown exception and has no callable identity; dispatching
// path. An `HostValueKind::Opaque` arc represents an opaque
// host value (e.g. a host-thrown exception) and has no callable
// identity; dispatching
// it would either find no entry in the bridge's callable registry
// (returning a confusing "no callable for key" error) or, worse,
// collide with a callable that happens to share its key. Reject
Expand Down Expand Up @@ -358,7 +359,7 @@ mod tests {
language: Some("python".to_string()),
handle: bex_resource_types::HostValueArc::new(
42,
bex_resource_types::HostValueKind::Error,
bex_resource_types::HostValueKind::Opaque,
),
},
),
Expand Down
2 changes: 1 addition & 1 deletion baml_language/crates/sys_types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,7 @@ mod tests {
language: None,
handle: bex_external_types::HostValueArc::new(
1,
bex_external_types::HostValueKind::Error,
bex_external_types::HostValueKind::Opaque,
),
},
];
Expand Down

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

This file was deleted.

This file was deleted.

Loading
Loading