use alef::backends::ffi::type_map::result_presence_companion_exists;
use alef::backends::java::JavaBackend;
use alef::core::backend::Backend;
use alef::core::config::{NewAlefConfig, ResolvedCrateConfig};
use alef::core::ir::{ApiSurface, FunctionDef, MethodDef, PrimitiveType, ReceiverKind, TypeDef, TypeRef};
const COMPANION_MARKER: &str = "_HAS_RESULT";
fn config() -> ResolvedCrateConfig {
let cfg: NewAlefConfig = toml::from_str(
r#"
[workspace]
languages = ["java", "ffi"]
[[crates]]
name = "test_lib"
sources = ["src/lib.rs"]
[crates.ffi]
prefix = "test"
[crates.java]
package = "com.example"
"#,
)
.unwrap();
cfg.resolve().unwrap().remove(0)
}
fn optional(inner: TypeRef) -> TypeRef {
TypeRef::Optional(Box::new(inner))
}
fn i64_option() -> TypeRef {
optional(TypeRef::Primitive(PrimitiveType::I64))
}
fn surface(types: Vec<TypeDef>, functions: Vec<FunctionDef>) -> ApiSurface {
ApiSurface {
crate_name: "test_lib".to_string(),
version: "0.1.0".to_string(),
types,
functions,
..Default::default()
}
}
fn render(api: &ApiSurface) -> String {
JavaBackend
.generate_bindings(api, &config())
.expect("java bindings")
.iter()
.map(|f| f.content.as_str())
.collect::<Vec<_>>()
.join("\n")
}
fn free_function(name: &str, return_type: TypeRef) -> FunctionDef {
FunctionDef {
name: name.to_string(),
return_type,
..Default::default()
}
}
fn opaque_type(name: &str, methods: Vec<MethodDef>) -> TypeDef {
TypeDef {
name: name.to_string(),
rust_path: format!("test_lib::{name}"),
is_opaque: true,
methods,
..Default::default()
}
}
fn method_def(name: &str, return_type: TypeRef, receiver: Option<ReceiverKind>) -> MethodDef {
MethodDef {
name: name.to_string(),
return_type,
receiver,
..Default::default()
}
}
#[test]
fn should_declare_a_presence_handle_when_a_free_function_returns_an_optional_scalar() {
let generated = render(&surface(vec![], vec![free_function("port", i64_option())]));
assert!(
generated.contains(r#"LIB.find("test_port_has_result")"#),
"the companion must be bound to the FFI crate's exported symbol; got:\n{generated}"
);
assert!(
generated.contains("static final MethodHandle TEST_PORT_HAS_RESULT = LINKER.downcallHandle("),
"the companion needs its own downcall handle; got:\n{generated}"
);
assert!(
generated.contains("FunctionDescriptor.of(ValueLayout.JAVA_INT)"),
"the companion always returns i32 regardless of the primary's shape; got:\n{generated}"
);
}
#[test]
fn should_report_absent_as_an_empty_optional_and_keep_a_zero_valued_result_present() {
let generated = render(&surface(vec![], vec![free_function("port", i64_option())]));
assert!(
generated.contains("var presenceResult = (int) NativeLib.TEST_PORT_HAS_RESULT.invoke();"),
"the presence answer must be captured; got:\n{generated}"
);
assert!(
generated.contains("return presenceResult == 1 ? Optional.of(primitiveResult) : Optional.empty();"),
"absent must be an empty Optional and a present zero must stay present; got:\n{generated}"
);
assert!(
!generated.contains("return Optional.of(primitiveResult);"),
"the unconditional wrap is the defect being fixed; got:\n{generated}"
);
}
#[test]
fn should_invoke_the_companion_before_the_primary_downcall() {
let generated = render(&surface(vec![], vec![free_function("port", i64_option())]));
let companion_at = generated
.find("TEST_PORT_HAS_RESULT.invoke(")
.expect("companion invocation");
let primary_at = generated
.find("var primitiveResult = (long) NativeLib.TEST_PORT.invoke(")
.expect("primary invocation");
assert!(
companion_at < primary_at,
"the presence companion must run before the primary downcall; got:\n{generated}"
);
}
#[test]
fn should_not_reference_a_companion_for_a_pointer_shaped_optional_return() {
let generated = render(&surface(
vec![],
vec![free_function("label", optional(TypeRef::String))],
));
assert!(
!generated.contains(COMPANION_MARKER),
"`Option<String>` already carries a real null pointer; got:\n{generated}"
);
}
#[test]
fn should_not_reference_a_companion_for_an_owned_receiver_because_ffi_exports_none() {
let consumed = method_def("into_timeout", i64_option(), Some(ReceiverKind::Owned));
assert!(
!result_presence_companion_exists(&consumed.return_type, consumed.receiver.as_ref()),
"the FFI backend must not export a companion for an owned receiver"
);
let generated = render(&surface(vec![opaque_type("Settings", vec![consumed])], vec![]));
assert!(
!generated.contains(COMPANION_MARKER),
"an owned receiver's first call consumes the handle, so no companion exists; got:\n{generated}"
);
}
#[test]
fn java_references_a_companion_exactly_when_the_ffi_backend_exports_one() {
let shapes: Vec<TypeRef> = vec![
i64_option(),
optional(TypeRef::Primitive(PrimitiveType::U64)),
optional(TypeRef::Primitive(PrimitiveType::Bool)),
optional(TypeRef::Primitive(PrimitiveType::F64)),
optional(TypeRef::Duration),
optional(TypeRef::String),
optional(TypeRef::Path),
optional(TypeRef::Json),
optional(TypeRef::Named("Settings".to_string())),
optional(TypeRef::Vec(Box::new(TypeRef::String))),
TypeRef::Primitive(PrimitiveType::I64),
TypeRef::String,
TypeRef::Unit,
];
let receivers = [
None,
Some(ReceiverKind::Ref),
Some(ReceiverKind::RefMut),
Some(ReceiverKind::Owned),
];
for shape in &shapes {
let generated = render(&surface(vec![], vec![free_function("probe", shape.clone())]));
assert_eq!(
generated.contains(COMPANION_MARKER),
result_presence_companion_exists(shape, None),
"free function returning {shape:?} disagreed with the FFI backend; got:\n{generated}"
);
for receiver in &receivers {
let method = method_def("probe", shape.clone(), receiver.clone());
let generated = render(&surface(vec![opaque_type("Settings", vec![method])], vec![]));
assert_eq!(
generated.contains(COMPANION_MARKER),
result_presence_companion_exists(shape, receiver.as_ref()),
"method returning {shape:?} with receiver {receiver:?} disagreed with the FFI \
backend; got:\n{generated}"
);
}
}
}