///|
pub fn wrapper_source(config : Config) -> String {
let mut text = "///|\nfn main { () }\n\n"
for fn_ in config.functions {
// The typed function binding rejects raises, generics and incompatible ABI.
let params = fn_.params
.map(p => p.name + " : " + p.kind.moon_name())
.join(", ")
let args = fn_.params.map(p => p.name).join(", ")
text += "///|\npub fn moonohos_raw_" +
fn_.name +
"(" +
params +
") -> " +
fn_.result.moon_name() +
" {\n @core." +
fn_.name +
"(" +
args +
")\n}\n\n"
}
text
}
///|
pub fn wrapper_package(config : Config) -> String {
let exports = config.functions
.map(f => "\"moonohos_raw_" + f.name + "\"")
.join(", ")
"import { \"" +
config.package_name +
"\" @core, }\npkgtype(kind: \"executable\")\noptions(link: { \"native\": { \"exports\": [" +
exports +
"] } })\n"
}
///|
pub fn c_header(config : Config) -> String {
let mut text = "#pragma once\n#include \n#include \n// Reference arguments are borrowed; reference results carry one owned reference.\n// Internal ABI for the pinned MoonBit compiler only.\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nvoid moonohos_initialize(void);\n"
for fn_ in config.functions {
let args = if fn_.params.is_empty() {
"void"
} else {
fn_.params.map(p => p.kind.c_name()).join(", ")
}
text += fn_.result.c_name() + " moonohos_" + fn_.name + "(" + args + ");\n"
}
text + "#ifdef __cplusplus\n}\n#endif\n"
}
///|
pub fn declarations(config : Config) -> String {
let mut text = "// Generated by MoonOHOS. Synchronous calls may throw TypeError/RangeError.\n"
for fn_ in config.functions {
let params = fn_.params
.map(p => p.name + ": " + p.kind.ark_name())
.join(", ")
text += "export const " +
fn_.name +
": (" +
params +
") => " +
fn_.result.ark_name() +
";\n"
}
text
}
///|
pub fn embed_source(config : Config) -> String {
let mut text = (
#|#include "moonohos.h"
#|#define main moonohos_unused_main
#|#include "core.c"
#|#undef main
#|void moonohos_initialize(void) {
#| static char name[] = "moonohos";
#| static char *args[] = {name, 0};
#| moonbit_runtime_init(1, args);
#| moonbit_init();
#|}
#|
) +
"\n"
// This compiler lowers Unit exports to int32_t; normalize them to C void.
for fn_ in config.functions {
let params = if fn_.params.is_empty() {
"void"
} else {
fn_.params
.mapi((i, p) => p.kind.c_name() + " arg" + i.to_string())
.join(", ")
}
let args = fn_.params.mapi((i, _) => "arg" + i.to_string()).join(", ")
text += fn_.result.c_name() +
" moonohos_" +
fn_.name +
"(" +
params +
") { " +
(if fn_.result == Void { "(void)" } else { "return " }) +
"moonohos_raw_" +
fn_.name +
"(" +
args +
"); }\n"
}
text
}
///|
pub fn native_source(config : Config) -> String {
let mut text = (
#|#include
#|#include
#|#include
#|#include
#|#include
#|#include
#|#include
#|#include
#|#include "moonohos.h"
#|static std::mutex gate;
#|static std::once_flag initialized;
#|static bool Check(napi_env env, napi_status status) {
#| if (status == napi_ok) return true;
#| if (status != napi_pending_exception) {
#| const napi_status thrown = napi_throw_error(env, nullptr, "Node-API operation failed");
#| if (thrown != napi_ok && thrown != napi_pending_exception) return false;
#| }
#| return false;
#|}
#|static bool ReadNumber(napi_env env, napi_value value, double &out) {
#| napi_valuetype type;
#| if (!Check(env, napi_typeof(env, value, &type))) return false;
#| if (type != napi_number) { Check(env, napi_throw_type_error(env, nullptr, "number required")); return false; }
#| if (!Check(env, napi_get_value_double(env, value, &out))) return false;
#| if (!std::isfinite(out)) { Check(env, napi_throw_range_error(env, nullptr, "finite number required")); return false; }
#| return true;
#|}
#|static bool ReadInt(napi_env env, napi_value value, int32_t &out) {
#| double number;
#| if (!ReadNumber(env, value, number)) return false;
#| if (std::trunc(number) != number || number < INT32_MIN || number > INT32_MAX) {
#| Check(env, napi_throw_range_error(env, nullptr, "signed 32-bit integer required")); return false;
#| }
#| out = static_cast(number); return true;
#|}
#|static bool ReadBool(napi_env env, napi_value value, int32_t &out) {
#| napi_valuetype type;
#| if (!Check(env, napi_typeof(env, value, &type))) return false;
#| if (type != napi_boolean) { Check(env, napi_throw_type_error(env, nullptr, "boolean required")); return false; }
#| bool boolean;
#| if (!Check(env, napi_get_value_bool(env, value, &boolean))) return false;
#| out = boolean ? 1 : 0; return true;
#|}
#|
) +
"\n" +
reference_helpers()
for fn_ in config.functions {
text += "static napi_value Call_" +
fn_.name +
"(napi_env env, napi_callback_info info) {\n try {\n"
if !fn_.params.is_empty() {
let count = fn_.params.length().to_string()
text += " size_t argc = " +
count +
";\n napi_value argv[" +
count +
"] = {};\n" +
" if (!Check(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr))) return nullptr;\n" +
" if (argc < " +
count +
") { Check(env, napi_throw_type_error(env, nullptr, \"missing arguments\")); return nullptr; }\n"
}
for index, p in fn_.params {
let argument = "arg" + index.to_string()
let reader = match p.kind {
Int32 => "ReadInt"
Float64 => "ReadNumber"
Boolean => "ReadBool"
Text => "ReadText"
Binary => "ReadBytes"
Void => ""
}
text += " " +
(match p.kind {
Text => "std::u16string"
Binary => "std::vector"
_ => p.kind.c_name()
}) +
" " +
argument +
";\n if (!" +
reader +
"(env, argv[" +
index.to_string() +
"], " +
argument +
")) return nullptr;\n"
}
let args = fn_.params
.mapi((i, p) => {
(if p.kind == Text || p.kind == Binary { "owned" } else { "arg" }) +
i.to_string() +
(if p.kind == Text || p.kind == Binary { ".ptr" } else { "" })
})
.join(", ")
let call = "moonohos_" +
fn_.name +
"(" +
args +
(if fn_.result == Text || fn_.result == Binary {
")};\n"
} else {
");\n"
})
if fn_.result != Void {
text += " " +
(match fn_.result {
Text => "std::u16string"
Binary => "std::vector"
_ => fn_.result.c_name()
}) +
" value;\n"
}
text += " {\n std::lock_guard lock(gate);\n std::call_once(initialized, moonohos_initialize);\n"
for index, p in fn_.params {
if p.kind == Text || p.kind == Binary {
text += " MoonOwner<" +
p.kind.c_name() +
"> owned" +
index.to_string() +
"{" +
(if p.kind == Text { "MakeText" } else { "MakeBytes" }) +
"(arg" +
index.to_string() +
")};\n"
}
}
text += " " +
(if fn_.result == Void {
""
} else if fn_.result == Text || fn_.result == Binary {
"MoonOwner<" + fn_.result.c_name() + "> returned{"
} else {
"value = "
}) +
call +
(if fn_.result == Text || fn_.result == Binary {
" value = " +
(if fn_.result == Text { "CopyText" } else { "CopyBytes" }) +
"(returned.ptr);\n"
} else {
""
}) +
" }\n napi_value result;\n"
let creation = match fn_.result {
Int32 => "napi_create_int32(env, value, &result)"
Float64 => "napi_create_double(env, value, &result)"
Boolean => "napi_get_boolean(env, value != 0, &result)"
Void => "napi_get_undefined(env, &result)"
Text =>
"napi_create_string_utf16(env, value.data(), value.size(), &result)"
Binary => "CreateBytes(env, value, &result)"
}
if fn_.result == Float64 {
text += " if (!std::isfinite(value)) { Check(env, napi_throw_range_error(env, nullptr, \"non-finite result\")); return nullptr; }\n"
}
text += " if (!Check(env, " +
creation +
")) return nullptr;\n return result;\n } catch (const std::length_error &) { Check(env, napi_throw_range_error(env, nullptr, \"buffer capacity exceeded\")); return nullptr; }\n catch (const std::exception &) { Check(env, napi_throw_error(env, nullptr, \"native allocation or runtime operation failed\")); return nullptr; }\n}\n\n"
}
text += "static napi_value Init(napi_env env, napi_value exports) {\n napi_property_descriptor properties[] = {\n"
for fn_ in config.functions {
text += " {\"" +
fn_.name +
"\", nullptr, Call_" +
fn_.name +
", nullptr, nullptr, nullptr, napi_default, nullptr},\n"
}
text += " };\n if (!Check(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(properties[0]), properties))) return nullptr;\n return exports;\n}\nNAPI_MODULE(" +
config.native_module +
", Init)\n"
text
}
///|
pub fn cmake_source(config : Config) -> String {
let target = config.native_module
(
#|cmake_minimum_required(VERSION 3.16)
#|project(MoonOHOS LANGUAGES C CXX)
#|add_library(__TARGET__ SHARED embed.c napi_init.cpp
#| "${MOON_HOME}/lib/runtime/runtime.c" "${MOON_HOME}/lib/runtime/env.c"
#| "${MOON_HOME}/lib/runtime/backtrace.c" "${MOON_HOME}/lib/runtime/sync_io.c"
#| "${MOON_HOME}/lib/runtime/utf.c")
#|target_include_directories(__TARGET__ PRIVATE "${MOON_HOME}/include")
#|target_compile_features(__TARGET__ PRIVATE c_std_11 cxx_std_17)
#|target_compile_definitions(__TARGET__ PRIVATE _GNU_SOURCE MOONBIT_ALLOCATOR=MOONBIT_ALLOCATOR_SYSTEM MOONBIT_NATIVE_EXIT_ON_PANIC)
#|target_compile_options(__TARGET__ PRIVATE -fwrapv -fno-strict-aliasing -fvisibility=hidden -Werror=implicit-function-declaration)
#|target_link_options(__TARGET__ PRIVATE -Wl,--no-undefined)
#|target_link_libraries(__TARGET__ PRIVATE libace_napi.z.so m)
#|
).replace_all(old="__TARGET__", new=target)
}