working on documentation and more API functions

This commit is contained in:
udo
2026-04-29 12:09:37 +00:00
parent cd445f3c9b
commit 9f7625c7fd
94 changed files with 1896 additions and 458 deletions
+338 -111
View File
@@ -13,6 +13,8 @@ const char* UCE_SETUP_SYMBOL = "__uce_set_current_request";
const char* UCE_RENDER_SYMBOL = "__uce_render";
const char* UCE_COMPONENT_SYMBOL = "__uce_component";
const char* UCE_WEBSOCKET_SYMBOL = "__uce_websocket";
const char* UCE_ONCE_SYMBOL = "__uce_once";
const char* UCE_INIT_SYMBOL = "__uce_init";
const u64 UCE_UNIT_ABI_VERSION = 1;
struct SharedUnitFilesystemState
@@ -48,6 +50,10 @@ struct SharedUnitCompileCheck
bool needs_compile = false;
};
void compiler_unload_failed_shared_unit(SharedUnit* su);
bool compiler_run_unit_init(Request* context, SharedUnit* su, String* error_out = 0);
bool compiler_run_unit_once_if_needed(Request* context, SharedUnit* su, String* error_out = 0);
bool compiler_config_truthy(String raw, bool default_value)
{
raw = to_lower(trim(raw));
@@ -67,19 +73,6 @@ bool compiler_jit_compile_on_request_enabled(Request* context)
return(compiler_config_truthy(context->server->config["JIT_COMPILE_ON_REQUEST"], true));
}
u64 compiler_failure_retry_seconds(Request* context)
{
if(!context || !context->server)
return(10);
auto raw = trim(context->server->config["COMPILE_FAILURE_RETRY_SECONDS"]);
if(raw == "")
return(10);
auto value = int_val(raw);
if(value < 0)
return(0);
return((u64)value);
}
bool compiler_is_u64_string(String value)
{
value = trim(value);
@@ -167,16 +160,16 @@ bool compiler_failure_retry_deferred(Request* context, SharedUnit* su, const Sha
{
if(!context || !su)
return(false);
auto retry_seconds = compiler_failure_retry_seconds(context);
if(retry_seconds == 0)
return(false);
if(!state.compile_output_exists || state.compile_output_time == 0)
return(false);
if(state.source_time == 0)
return(false);
if(state.source_time > state.compile_output_time)
return(false);
return(time() < (u64)state.compile_output_time + retry_seconds);
auto required_failure_inputs_time = std::max({
state.source_time,
state.setup_template_time,
state.compiler_abi_time
});
return(state.compile_output_time >= required_failure_inputs_time);
}
String compiler_failure_output_for_state(SharedUnit* su, const SharedUnitFilesystemState& state)
@@ -653,6 +646,8 @@ void load_shared_unit(Request* context, SharedUnit* su)
su->on_render = 0;
su->on_component = 0;
su->on_websocket = 0;
su->on_once = 0;
su->on_init = 0;
su->on_setup = 0;
su->so_handle = 0;
su->compiler_messages = "";
@@ -695,17 +690,31 @@ void load_shared_unit(Request* context, SharedUnit* su)
dlerror();
su->on_websocket = (request_ref_handler)dlsym(su->so_handle, UCE_WEBSOCKET_SYMBOL);
dlerror();
su->on_once = (request_ref_handler)dlsym(su->so_handle, UCE_ONCE_SYMBOL);
dlerror();
su->on_init = (request_ref_handler)dlsym(su->so_handle, UCE_INIT_SYMBOL);
dlerror();
su->api_declarations = split(file_get_contents(su->api_file_name), "\n");
String init_error = "";
if(!compiler_run_unit_init(context, su, &init_error))
{
if(init_error != "")
su->compiler_messages = init_error;
compiler_unload_failed_shared_unit(su);
}
//else
// printf("(i) loaded unit %s\n", su->file_name.c_str());
}
else
{
const char* dl_error = dlerror();
su->compiler_messages = "could not open " + su->so_name;
if(dl_error && String(dl_error) != "")
su->compiler_messages += ": " + String(dl_error);
su->compile_status = "load_error";
su->compile_error_status = su->compiler_messages;
su->last_error = time();
printf("Error loading unit %s, could not open %s\n", su->file_name.c_str(), su->so_name.c_str());
printf("Error loading unit %s, %s\n", su->file_name.c_str(), su->compiler_messages.c_str());
}
}
@@ -1176,6 +1185,183 @@ struct UnitInvocationScope
}
};
enum class UnitCallMacroKind
{
none,
render,
component,
once,
init
};
struct UnitCallMacroTarget
{
UnitCallMacroKind kind = UnitCallMacroKind::none;
String handler_name;
};
struct RequestPropsScope
{
Request* context = 0;
DTree previous_props;
RequestPropsScope(Request* context, const DTree& props)
{
this->context = context;
if(this->context)
{
previous_props = this->context->props;
this->context->props = props;
}
}
~RequestPropsScope()
{
if(context)
context->props = previous_props;
}
};
void compiler_unload_failed_shared_unit(SharedUnit* su)
{
if(!su)
return;
if(su->so_handle)
dlclose(su->so_handle);
su->so_handle = 0;
su->api_functions.clear();
su->on_setup = 0;
su->on_render = 0;
su->on_component = 0;
su->on_websocket = 0;
su->on_once = 0;
su->on_init = 0;
}
bool compiler_run_unit_init(Request* context, SharedUnit* su, String* error_out)
{
if(!su || !su->on_init)
return(true);
if(!context)
{
if(error_out)
*error_out = "internal error: INIT() requires a Request context";
return(false);
}
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + su->file_name;
return(false);
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
try
{
su->on_init(*context);
return(true);
}
catch(...)
{
su->runtime_error_status = "uncaught exception during INIT";
su->compile_status = "load_error";
su->compile_error_status = su->runtime_error_status;
su->last_error = time();
if(error_out)
*error_out = su->runtime_error_status;
return(false);
}
}
bool compiler_run_unit_once_if_needed(Request* context, SharedUnit* su, String* error_out)
{
if(!su || !su->on_once)
return(true);
if(!context)
{
if(error_out)
*error_out = "internal error: ONCE() requires a Request context";
return(false);
}
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + su->file_name;
return(false);
}
if(context->once_units.find(su->file_name) != context->once_units.end())
return(true);
context->once_units.insert(su->file_name);
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
try
{
su->on_once(*context);
return(true);
}
catch(...)
{
context->once_units.erase(su->file_name);
su->runtime_error_status = "uncaught exception during ONCE";
su->last_error = time();
if(error_out)
*error_out = su->runtime_error_status;
throw;
}
}
String unit_call_macro_trim(String function_name)
{
function_name = trim(function_name);
if(function_name.length() >= 2 && function_name.substr(function_name.length() - 2) == "()")
function_name = trim(function_name.substr(0, function_name.length() - 2));
return(function_name);
}
UnitCallMacroTarget unit_call_macro_target(String function_name)
{
UnitCallMacroTarget target;
function_name = unit_call_macro_trim(function_name);
if(function_name == "RENDER")
{
target.kind = UnitCallMacroKind::render;
return(target);
}
if(function_name.rfind("RENDER:", 0) == 0)
{
target.kind = UnitCallMacroKind::render;
target.handler_name = trim(function_name.substr(7));
return(target);
}
if(function_name == "COMPONENT")
{
target.kind = UnitCallMacroKind::component;
return(target);
}
if(function_name.rfind("COMPONENT:", 0) == 0)
{
target.kind = UnitCallMacroKind::component;
target.handler_name = trim(function_name.substr(10));
return(target);
}
if(function_name == "ONCE")
{
target.kind = UnitCallMacroKind::once;
return(target);
}
if(function_name == "INIT")
{
target.kind = UnitCallMacroKind::init;
return(target);
}
return(target);
}
}
String component_normalize_path(String name)
@@ -1262,6 +1448,70 @@ String component_handler_symbol(String render_name)
return(String(UCE_COMPONENT_SYMBOL) + "_" + safe_name(render_name));
}
String compiler_missing_request_handler_message(UnitCallMacroKind kind, String handler_name)
{
handler_name = trim(handler_name);
if(kind == UnitCallMacroKind::render)
{
if(handler_name == "" || handler_name == "render")
return("no RENDER() entry point");
return("no RENDER:" + handler_name + "() entry point");
}
if(kind == UnitCallMacroKind::component)
{
if(handler_name == "")
return("no COMPONENT() entry point");
return("no COMPONENT:" + handler_name + "() entry point");
}
if(kind == UnitCallMacroKind::once)
return("no ONCE() entry point");
if(kind == UnitCallMacroKind::init)
return("no INIT() entry point");
return("request handler not found");
}
bool compiler_prepare_request_handler(Request* context, SharedUnit* su, String* error_out = 0, bool run_once = false)
{
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + su->file_name;
return(false);
}
if(run_once && !compiler_run_unit_once_if_needed(context, su, error_out))
return(false);
return(true);
}
void compiler_execute_request_handler(
Request* context,
SharedUnit* su,
request_ref_handler handler,
bool count_request,
String runtime_error_status
)
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
compiler_begin_render_result(su, count_request);
try
{
handler(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
runtime_error_status
);
throw;
}
}
request_ref_handler get_page_render_handler(SharedUnit* su, String render_name)
{
String symbol = page_render_handler_symbol(render_name);
@@ -1300,46 +1550,24 @@ bool compiler_invoke_render(Request* context, String file_name, String render_na
if(!su)
return(false);
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + file_name;
if(!compiler_prepare_request_handler(context, su, error_out, true))
return(false);
}
auto handler = get_page_render_handler(su, render_name);
if(!handler)
{
if(error_out)
{
if(trim(render_name) == "" || trim(render_name) == "render")
*error_out = "no RENDER() entry point";
else
*error_out = "no RENDER:" + render_name + "() entry point";
}
*error_out = compiler_missing_request_handler_message(UnitCallMacroKind::render, render_name);
return(false);
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
bool count_request = compiler_is_request_entry_unit(context, su);
compiler_begin_render_result(su, count_request);
try
{
handler(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
"uncaught exception during render"
);
throw;
}
compiler_execute_request_handler(
context,
su,
handler,
compiler_is_request_entry_unit(context, su),
"uncaught exception during render"
);
return(true);
}
@@ -1349,45 +1577,24 @@ bool compiler_invoke_component(Request* context, String file_name, String render
if(!su)
return(false);
if(!su->on_setup)
{
if(error_out)
*error_out = "internal error: " + String(UCE_SETUP_SYMBOL) + "() not defined in " + file_name;
if(!compiler_prepare_request_handler(context, su, error_out, true))
return(false);
}
auto handler = get_component_handler(su, render_name);
if(!handler)
{
if(error_out)
{
if(trim(render_name) == "")
*error_out = "no COMPONENT() entry point";
else
*error_out = "no COMPONENT:" + render_name + "() entry point";
}
*error_out = compiler_missing_request_handler_message(UnitCallMacroKind::component, render_name);
return(false);
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
compiler_begin_render_result(su, false);
try
{
handler(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
"uncaught exception during component render"
);
throw;
}
compiler_execute_request_handler(
context,
su,
handler,
false,
"uncaught exception during component render"
);
return(true);
}
@@ -1421,26 +1628,13 @@ void compiler_invoke_websocket(Request* context, String file_name)
return;
}
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
f64 render_start = time_precise();
bool count_request = compiler_is_request_entry_unit(context, su);
compiler_begin_render_result(su, count_request);
try
{
su->on_websocket(*context);
compiler_record_render_result(su, time_precise() - render_start, true);
}
catch(...)
{
compiler_record_render_result(
su,
time_precise() - render_start,
false,
"uncaught exception during websocket handler"
);
throw;
}
compiler_execute_request_handler(
context,
su,
su->on_websocket,
compiler_is_request_entry_unit(context, su),
"uncaught exception during websocket handler"
);
}
void unit_render(String file_name)
@@ -1499,14 +1693,11 @@ void component_render(String name, DTree props, Request& context)
return;
}
DTree previous_props = context.props;
context.props = props;
RequestPropsScope props_scope(&context, props);
String error_message = "";
if(!compiler_invoke_component(&context, resolved_name, render_name, &error_message) && error_message != "")
print(component_error_banner(error_message));
context.props = previous_props;
}
String component(String name)
@@ -1558,16 +1749,52 @@ DTree* unit_call(String file_name, String function_name, DTree* call_param)
}
else
{
auto f = (dtree_call_handler)dlsym(su->so_handle, function_name.c_str());
if(!f)
auto macro_target = unit_call_macro_target(function_name);
if(macro_target.kind != UnitCallMacroKind::none)
{
print("Error: unit_call() function '", function_name, "' not found");
RequestPropsScope props_scope(context, (call_param ? *call_param : DTree()));
String error_message = "";
if(macro_target.kind == UnitCallMacroKind::render)
{
if(!compiler_invoke_render(context, su->file_name, macro_target.handler_name, &error_message) && error_message != "")
print("Error: unit_call() ", error_message);
}
else if(macro_target.kind == UnitCallMacroKind::component)
{
if(!compiler_invoke_component(context, su->file_name, macro_target.handler_name, &error_message) && error_message != "")
print("Error: unit_call() ", error_message);
}
else
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
request_ref_handler handler = 0;
if(macro_target.kind == UnitCallMacroKind::once)
handler = su->on_once;
else if(macro_target.kind == UnitCallMacroKind::init)
handler = su->on_init;
if(!handler)
print("Error: unit_call() ", compiler_missing_request_handler_message(macro_target.kind, macro_target.handler_name));
else
handler(*context);
}
}
else
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
result = f(call_param);
auto f = (dtree_call_handler)dlsym(su->so_handle, function_name.c_str());
if(!f)
{
print("Error: unit_call() function '", function_name, "' not found");
}
else
{
UnitInvocationScope invoke_scope(context, su);
su->on_setup(context);
result = f(call_param);
}
}
}
}