///|
pub struct GeneratedArtifacts {
wrapper : String
package_file : String
header : String
embed : String
native : String
declarations : String
cmake : String
}
///|
priv struct Accessor {
name : String
params : Array[FieldDef]
result : TypeExpr
expression : String
}
///|
fn accessor_definitions(
plan : GenerationPlan,
) -> Array[Accessor] raise ConfigError {
let result : Array[Accessor] = []
for type_ in plan.types {
let prefix = "type_" + type_.index.to_string()
match type_.kind {
List(element) => {
let value : FieldDef = { name: "v", kind: type_.kind, }
result.push({
name: prefix + "_new",
params: [],
result: type_.kind,
expression: "[]",
})
result.push({
name: prefix + "_push",
params: [value, { name: "x", kind: element, }],
result: Scalar(Void),
expression: "v.push(x)",
})
result.push({
name: prefix + "_length",
params: [value],
result: Scalar(Int32),
expression: "v.length()",
})
result.push({
name: prefix + "_get",
params: [value, { name: "i", kind: Scalar(Int32), }],
result: element,
expression: "v[i]",
})
}
Record(_) =>
for accessor in record_accessors(plan.project, type_) {
result.push(accessor)
}
_ => ()
}
}
result
}
///|
fn cpp_reader(type_ : TypePlan, project : Project) -> String raise ConfigError {
let index = type_.index.to_string()
let mut source = "static bool Read_" +
index +
"(napi_env env, napi_value value, BridgeValue &out, Budget &budget, Ancestors &ancestors, size_t depth, const std::string &path) {\n"
match type_.kind {
Scalar(kind) => {
let (wanted, reader, slot, width) = match kind {
Int32 => ("napi_number", "ReadInteger", "integer", "sizeof(int32_t)")
Float64 => ("napi_number", "ReadFinite", "number", "sizeof(double)")
Boolean => ("napi_boolean", "ReadBool", "integer", "sizeof(int32_t)")
Text =>
(
"napi_string", "ReadText", "text", "out.text.size() * sizeof(char16_t)",
)
Binary => ("napi_object", "ReadBytes", "bytes", "out.bytes.size()")
Void => ("napi_undefined", "", "", "0")
}
if kind != Void {
source += " if (!LeafType(env, value, " +
wanted +
", path)) return false;\n"
if kind == Text || kind == Binary {
source += " if (!" +
(if kind == Text { "PrepareText" } else { "PrepareBytes" }) +
"(env, value, budget, depth, path)) return false;\n"
}
source += " if (!" +
reader +
"(env, value, out." +
slot +
(if kind == Int32 || kind == Float64 { ", path" } else { "" }) +
")) return false;\n"
if kind != Text && kind != Binary {
source += " budget.add(0, " + width + ", depth, path);\n"
}
}
}
List(_) => {
let child = type_.dependencies[0].to_string()
source += " if (!Ordinary(env, value, true, path) || !ancestors.enter(value, path)) return false;\n AncestorScope scope(ancestors);\n uint32_t length;\n if (!Check(env, napi_get_array_length(env, value, &length))) return false;\n budget.add(length, 0, depth + 1, path);\n MOONOHOS_BUFFER_ALLOCATE();\n out.children.resize(length);\n for (uint32_t i = 0; i < length; ++i) {\n auto child_path = path + \"[\" + std::to_string(i) + \"]\";\n napi_value child;\n if (!Own(env, value, std::to_string(i), child, child_path)) return false;\n if (!Read_" +
child +
"(env, child, out.children[i], budget, ancestors, depth + 1, child_path)) return false;\n }\n"
}
Record(_) => source += record_reader(type_, project)
Handle(_) | External(_, _) => ()
}
source + " return true;\n}\n"
}
///|
fn cpp_make(type_ : TypePlan, types : Array[TypePlan]) -> String {
let index = type_.index.to_string()
let mut source = "static " +
type_.kind.c_name() +
" Make_" +
index +
"(const BridgeValue &value) {\n"
match type_.kind {
Scalar(kind) =>
source += match kind {
Int32 | Boolean => " return value.integer;\n"
Float64 => " return value.number;\n"
Text => " return MakeText(value.text);\n"
Binary => " return MakeBytes(value.bytes);\n"
Void => ""
}
List(element) => {
let child = type_.dependencies[0].to_string()
source += " MoonOwner result{" +
type_.symbol +
"_new()};\n for (const auto &item : value.children) {\n "
if element.is_reference() {
source += "MoonOwner<" +
types[type_.dependencies[0]].kind.c_name() +
"> child{Make_" +
child +
"(item)};\n " +
type_.symbol +
"_push(result.ptr, child.ptr);\n"
} else {
source += type_.symbol +
"_push(result.ptr, Make_" +
child +
"(item));\n"
}
source += " }\n auto pointer = result.ptr; result.ptr = nullptr; return pointer;\n"
}
Record(_) => source += record_maker(type_, types)
Handle(_) | External(_, _) => ()
}
source + "}\n"
}
///|
fn cpp_copy(
type_ : TypePlan,
types : Array[TypePlan],
project : Project,
) -> String raise ConfigError {
let index = type_.index.to_string()
let mut source = "static BridgeValue Copy_" +
index +
"(" +
type_.kind.c_name() +
" value, Budget &budget, size_t depth, const std::string &path) {\n BridgeValue out;\n"
match type_.kind {
Scalar(kind) =>
source += match kind {
Int32 | Boolean =>
" out.integer = value; budget.add(0, sizeof(int32_t), depth, path);\n"
Float64 =>
" if (!std::isfinite(value)) throw RangeFault(path + \": non-finite result\");\n out.number = value; budget.add(0, sizeof(double), depth, path);\n"
Text =>
" budget.add(0, static_cast(Moonbit_array_length(value)) * sizeof(char16_t), depth, path);\n out.text = CopyText(value);\n"
Binary =>
" budget.add(0, static_cast(Moonbit_array_length(value)), depth, path);\n out.bytes = CopyBytes(value);\n"
Void => ""
}
List(element) => {
let child = type_.dependencies[0].to_string()
source += " int32_t length = " +
type_.symbol +
"_length(value);\n if (length < 0) throw RangeFault(path + \": invalid array length\");\n budget.add(static_cast(length), 0, depth + 1, path);\n MOONOHOS_BUFFER_ALLOCATE();\n out.children.reserve(length);\n for (int32_t i = 0; i < length; ++i) {\n "
if element.is_reference() {
source += "MoonOwner<" +
types[type_.dependencies[0]].kind.c_name() +
"> child{" +
type_.symbol +
"_get(value, i)};\n out.children.push_back(Copy_" +
child +
"(child.ptr, budget, depth + 1, path + \"[\" + std::to_string(i) + \"]\"));\n"
} else {
source += "out.children.push_back(Copy_" +
child +
"(" +
type_.symbol +
"_get(value, i), budget, depth + 1, path + \"[\" + std::to_string(i) + \"]\"));\n"
}
source += " }\n"
}
Record(_) => source += record_copier(type_, project, types)
Handle(_) | External(_, _) => ()
}
source + " return out;\n}\n"
}
///|
fn cpp_create(type_ : TypePlan, project : Project) -> String raise ConfigError {
let index = type_.index.to_string()
let mut source = "static bool Create_" +
index +
"(napi_env env, const BridgeValue &value, napi_value &result) {\n"
match type_.kind {
Scalar(kind) =>
source += " return Check(env, " +
(match kind {
Int32 => "napi_create_int32(env, value.integer, &result)"
Float64 => "napi_create_double(env, value.number, &result)"
Boolean => "napi_get_boolean(env, value.integer != 0, &result)"
Text =>
"napi_create_string_utf16(env, value.text.data(), value.text.size(), &result)"
Binary => "CreateBytes(env, value.bytes, &result)"
Void => "napi_get_undefined(env, &result)"
}) +
");\n"
List(_) =>
source += " if (!Check(env, napi_create_array_with_length(env, value.children.size(), &result))) return false;\n for (size_t i = 0; i < value.children.size(); ++i) {\n napi_value child;\n if (!Create_" +
type_.dependencies[0].to_string() +
"(env, value.children[i], child)) return false;\n if (!Check(env, napi_set_element(env, result, static_cast(i), child))) return false;\n }\n return true;\n"
Record(_) => source += record_creator(type_, project)
Handle(_) | External(_, _) => ()
}
source + "}\n"
}
///|
pub fn Project::generate(
self : Project,
) -> GeneratedArtifacts raise ConfigError {
let plan = make_generation_plan(self)
let accessors = accessor_definitions(plan)
let mut wrapper = self.business_wrapper()
let exports = self.functions.map(f => "\"moonohos_raw_" + f.name + "\"")
let mut header = self
.business_header()
.replace_all(old="#ifdef __cplusplus\n}\n#endif\n", new="")
let mut embed = "#include \"moonohos.h\"\n#define main moonohos_unused_main\n#include \"core.c\"\n#undef main\nvoid moonohos_initialize(void) { static char name[] = \"moonohos\"; static char *args[] = {name, 0}; moonbit_runtime_init(1, args); moonbit_init(); }\n" +
self.business_adapters()
for accessor in accessors {
wrapper += moon_export(
accessor.name,
accessor.params,
accessor.result,
accessor.expression,
)
exports.push("\"moonohos_raw_" + accessor.name + "\"")
header += accessor.result.c_name() +
" moonohos_" +
accessor.name +
"(" +
c_parameters(accessor.params) +
");\n"
embed += raw_adapter(accessor.name, accessor.params, accessor.result)
}
header += "#ifdef __cplusplus\n}\n#endif\n"
let imports = ["\"" + self.package_name + "\" @core"]
for adapter in self.adapters {
let line = "\"" + adapter.pkg + "\" @" + adapter.pkg_alias
if !imports.contains(line) {
imports.push(line)
}
}
let package_file = "import { " +
imports.join(", ") +
", }\npkgtype(kind: \"executable\")\noptions(link: { \"native\": { \"exports\": [" +
exports.join(", ") +
"] } })\n"
let mut native = native_prelude() +
lifecycle_core() +
container_runtime() +
lifecycle_napi()
for type_ in plan.types {
if type_.kind.is_opaque() {
continue
}
if type_.kind != Scalar(Void) {
native += cpp_reader(type_, self) +
cpp_make(type_, plan.types) +
cpp_copy(type_, plan.types, self)
}
native += cpp_create(type_, self)
}
native += bridge_callbacks(plan) + init_source(plan)
let declarations = bridge_declarations(self)
let shell : Config = {
module_dir: self.module_dir,
package_name: self.package_name,
native_module: self.native_module,
functions: [],
}
{
wrapper,
package_file,
header,
embed,
native,
declarations,
cmake: cmake_source(shell),
}
}
///|
pub fn Project::generate_arrays(
self : Project,
) -> GeneratedArtifacts raise ConfigError {
self.generate()
}