///|
fn v2_abi_kind(raw : String, canonical : String) -> String {
let named = normalized_type(raw)
if named == "size_t" {
return "size"
}
if named == "ptrdiff_t" {
return "ptrdiff"
}
match normalized_type(canonical) {
"short" | "short int" | "signed short" | "signed short int" => "short"
"unsigned short" | "unsigned short int" => "ushort"
"long" | "long int" | "signed long" | "signed long int" => "long"
"unsigned long" | "unsigned long int" => "ulong"
"_Bool" | "bool" => "bool"
_ => ""
}
}
///|
fn v2_abi_moon_type(kind : String) -> String {
match kind {
"short" => "Int"
"ushort" => "UInt"
"long" | "ptrdiff" => "Int64"
"ulong" | "size" => "UInt64"
"bool" => "Bool"
_ => ""
}
}
///|
fn v2_abi_c_type(kind : String) -> String {
match kind {
"short" | "bool" => "int32_t"
"ushort" => "uint32_t"
"long" | "ptrdiff" => "int64_t"
"ulong" | "size" => "uint64_t"
_ => ""
}
}
///|
fn v2_abi_range_check(kind : String, value : String) -> String {
match kind {
"short" => value + " < SHRT_MIN || " + value + " > SHRT_MAX"
"ushort" => value + " > USHRT_MAX"
"long" => value + " < LONG_MIN || " + value + " > LONG_MAX"
"ulong" => value + " > ULONG_MAX"
"size" => value + " > SIZE_MAX"
"ptrdiff" => value + " < PTRDIFF_MIN || " + value + " > PTRDIFF_MAX"
_ => ""
}
}
///|
fn needs_v2_abi_bridge(function : CFunction, config : GenerateConfig) -> Bool {
if config.schema_version != 2 {
return false
}
if v2_abi_kind(function.return_type, function.canonical_return_type) != "" {
return true
}
for parameter in function.parameters {
if v2_abi_kind(parameter.c_type, parameter.canonical_type) != "" {
return true
}
}
false
}
///|
fn render_v2_abi_bridge(
function : CFunction,
aliases : Map[String, String],
opaque_types : Map[String, Bool],
enums : Map[String, Bool],
config : GenerateConfig,
) -> (String?, String?, String) {
if function.c_signature.contains("...") {
return (None, None, "variadic function")
}
let return_kind = v2_abi_kind(
function.return_type,
function.canonical_return_type,
)
let moon_return = if return_kind != "" {
v2_abi_moon_type(return_kind)
} else {
match
resolve_configured_type(
function.return_type,
function.canonical_return_type,
aliases,
opaque_types,
enums,
config,
utf8_input=false,
result=true,
) {
Some(value) => value.moon_type
None =>
return (None, None, "unsupported return type: " + function.return_type)
}
}
let c_return = if return_kind != "" {
v2_abi_c_type(return_kind)
} else {
function.return_type
}
let moon_params : Array[String] = []
let ffi_params : Array[String] = []
let ffi_args : Array[String] = []
let c_params : Array[String] = []
let c_args : Array[String] = []
let borrowed : Array[String] = []
let guards : Array[String] = []
for index, parameter in function.parameters {
let kind = v2_abi_kind(parameter.c_type, parameter.canonical_type)
let moon_type = if kind != "" {
v2_abi_moon_type(kind)
} else {
match
resolve_configured_type(
parameter.c_type,
parameter.canonical_type,
aliases,
opaque_types,
enums,
config,
utf8_input=config.is_utf8_input(function.name, parameter.name),
result=false,
) {
Some(value) => value.moon_type
None =>
return (None, None, "unsupported parameter type: " + parameter.c_type)
}
}
let name = "arg" + index.to_string()
moon_params.push(name + " : " + moon_type)
ffi_params.push(name + " : " + moon_type)
ffi_args.push(name)
let c_name = "p" + index.to_string()
if kind != "" {
c_params.push(v2_abi_c_type(kind) + " " + c_name)
c_args.push("(" + parameter.c_type + ")" + c_name)
let check = v2_abi_range_check(kind, c_name)
if check != "" {
guards.push(check)
}
} else if moon_type == "Bytes" {
borrowed.push(name)
c_params.push("moonbit_bytes_t " + c_name)
c_args.push("(" + parameter.c_type + ")" + c_name)
} else {
c_params.push(parameter.c_type + " " + c_name)
c_args.push(c_name)
}
}
let bridge_name = "mbg_bridge_" + function.moon_name
ffi_params.push("mbg_error : FixedArray[Int]")
ffi_args.push("mbg_error")
c_params.push("int32_t *mbg_error")
borrowed.push("mbg_error")
let moon = "///|\n#borrow(" +
borrowed.join(", ") +
")\n" +
v2_extern(bridge_name, ffi_params, moon_return) +
"\n///|\n" +
v2_public_head(function.moon_name, moon_params) +
" -> " +
moon_return +
" raise {\n" +
" let mbg_error = FixedArray::make(1, 0)\n" +
" let value = " +
bridge_name +
"(" +
ffi_args.join(", ") +
")\n" +
" if mbg_error[0] != 0 {\n fail(\"C scalar argument is out of range\")\n }\n" +
" value\n}\n"
let failure_return = match moon_return {
"Unit" => "return;"
"Float" | "Double" => "return 0.0;"
_ => "return 0;"
}
let call = function.name + "(" + c_args.join(", ") + ")"
let call_line = if moon_return == "Unit" {
call + ";"
} else {
"return (" + c_return + ")" + call + ";"
}
let c = "MOONBIT_FFI_EXPORT " +
c_return +
" " +
bridge_name +
"(" +
c_params.join(", ") +
") {\n" +
(if guards.length() == 0 {
""
} else {
" if (" +
guards.join(" || ") +
") { mbg_error[0] = 1; " +
failure_return +
" }\n"
}) +
" mbg_error[0] = 0;\n " +
call_line +
"\n}\n"
(Some(moon), Some(c), "")
}