Expose dynamic compile failures

This commit is contained in:
root
2026-07-19 23:15:39 +00:00
parent cd993b34e9
commit 93cbc83b82
10 changed files with 286 additions and 41 deletions
+5 -1
View File
@@ -189,6 +189,8 @@ CLI_SOCKET_MODE=0600
SITE_DIRECTORY=/var/www/html
HTTP_DOCUMENT_ROOT=/var/www/html
JIT_COMPILE_ON_REQUEST=1
SHOW_DYNAMIC_COMPILE_ERRORS=1
SERVE_LAST_KNOWN_GOOD=0
PROACTIVE_COMPILE_ENABLED=1
PROACTIVE_COMPILE_JOBS=2
PROACTIVE_COMPILE_CHECK_INTERVAL=60
@@ -226,7 +228,9 @@ Important settings:
- `HTTP_PORT` is the built-in HTTP/WebSocket listener used for WebSocket upgrade traffic and direct local probes. Bind/firewall it for local access only; nginx/Apache should be the public entry point.
- `WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` controls how long a forwarded WS message can remain queued in the broker before being dropped (default `30`). Set to `0` to disable the timeout.
- `WASM_COMPILE_SCRIPT` must point to `scripts/compile_wasm_unit` unless you provide an equivalent compiler. Relative paths are resolved from the runtime root/`COMPILER_SYS_PATH`. That script calls `scripts/check_unit_wasm.py` after linking each unit and uses the pinned WASI SDK on every deployment host.
- `PROACTIVE_COMPILE_JOBS` selects 116 low-priority full-site scanner processes (default `2`). Each canonical unit path has one scanner owner. The separate higher-priority demand compiler remains reserved for stale units requested over HTTP, so total background compile concurrency can reach this value plus one.
- `SHOW_DYNAMIC_COMPILE_ERRORS=1` makes a failed dynamic `component()`, `unit_render()`, or `unit_call()` show the bounded compiler diagnostic instead of only a generic missing-handler message. Set it to `0` on deployments where source paths and compiler output must not reach HTTP responses.
- `SERVE_LAST_KNOWN_GOOD=1` lets HTTP GET/HEAD/OPTIONS requests keep using a compatible complete unit artifact while the proactive compiler builds changed source. It defaults to `0`; CLI and mutation requests always use current code or fail closed. The option requires an enabled proactive compiler with a positive check interval. Failed background builds preserve the prior Wasm, source map, and serialized module until a successful atomic publication replaces them. Missing source or an incompatible compiler/core ABI is never served as last-known-good.
- `PROACTIVE_COMPILE_JOBS` selects 116 low-priority full-site scanner processes (default `2`). Each canonical unit path has one scanner owner. When last-known-good serving is enabled, the separate higher-priority demand compiler remains reserved for stale units requested over HTTP, so total background compile concurrency can reach this value plus one.
- `WASM_CORE_PATH` must point at the built `core.wasm` file.
- `WASM_EPOCH_DEADLINE_TICKS` and `WASM_EPOCH_PERIOD_MS` bound one
uninterrupted guest CPU segment. `WASM_INVOCATION_TIMEOUT_MS` is the
+13 -11
View File
@@ -292,17 +292,19 @@ All compiler processes use the same per-unit lock, so concurrent demand and scan
discovery cannot publish duplicate artifacts. The priority worker is idle when
there is no demand and never scans the site on its own. Unit
compilation writes and validates a process-unique temporary wasm file, then
publishes it with an atomic rename. When proactive compilation is enabled,
read-only HTTP requests keep using the last complete artifact while requesting
that stale unit at the head of the compiler queue. Non-read requests never run
a stale entry artifact: they return `503 Service Unavailable` with
`Retry-After: 1`, allowing the client to retry after the priority rebuild.
CLI and explicit compile paths remain synchronous. A failed rebuild removes
availability and surfaces the compiler error rather than serving the old unit
indefinitely. This freshness contract includes components resolved lazily from
an otherwise-current CLI entry unit: if the proactive compiler already owns a
stale child's lock, the CLI request joins that compile and waits for the fresh
artifact. Read-only HTTP may use the child's last complete artifact instead.
publishes it with an atomic rename. By default, requests do not execute stale
code: a missing or changed unit is rebuilt synchronously and a failed dynamic
build surfaces its bounded compiler diagnostic. `SERVE_LAST_KNOWN_GOOD=1`
opts ordinary read-only HTTP requests into using the last complete,
ABI-compatible artifact while requesting that stale unit at the head of the
compiler queue. The proactive compiler must be enabled for this mode. Failed
background builds preserve that artifact and its source map and serialized
module until a later successful atomic publication replaces them. Missing
source, incompatible ABI generations, CLI, WebSocket dispatch, and mutation
requests never use last-known-good code. A stale mutation returns `503 Service
Unavailable` with `Retry-After: 1`; CLI and explicit compile paths remain
synchronous. If a proactive compiler already owns a stale child's lock, a CLI
request joins that compile and waits for the current artifact.
Before preprocessing, the compiler verifies that the worker can actually read
the source. An unreadable path is reported as a source-read failure with a
+10
View File
@@ -37,6 +37,16 @@ SITE_DIRECTORY=site
# ENABLE JIT COMPILATION WHEN A PAGE REQUEST HITS A STALE OR MISSING UNIT
JIT_COMPILE_ON_REQUEST=1
# SHOW THE BOUNDED COMPILER DIAGNOSTIC WHEN DYNAMIC DISPATCH TARGETS A UNIT
# THAT FAILED TO COMPILE. WHEN DISABLED, THE CALL REPORTS THE GENERIC MISSING
# COMPONENT OR UNIT ERROR.
SHOW_DYNAMIC_COMPILE_ERRORS=1
# KEEP SERVING A COMPATIBLE LAST-KNOWN-GOOD ARTIFACT TO HTTP READ REQUESTS
# WHILE THE PROACTIVE COMPILER BUILDS A CHANGED UNIT. MUTATIONS AND CLI NEVER
# SERVE STALE CODE. REQUIRES THE PROACTIVE COMPILER AND DEFAULTS FAIL-CLOSED.
SERVE_LAST_KNOWN_GOOD=0
# WASM SIDE-MODULE COMPILER USED FOR .uce UNITS
WASM_COMPILE_SCRIPT=scripts/compile_wasm_unit
+1
View File
@@ -86,6 +86,7 @@ if [[ "$action" == "run" ]]; then
scripts/test_unit_export_surface.sh
timeout --signal=TERM --kill-after=5s 120s scripts/test_wasm_metadata_buffer.sh
timeout --signal=TERM --kill-after=5s 150s scripts/test_wasm_metadata_deadline.sh
timeout --signal=TERM --kill-after=5s 240s scripts/test_dynamic_compile_failures.sh
scripts/test_wasm_source_locations.sh
scripts/test_server_arguments.sh
scripts/test_socket_activation.sh
+167
View File
@@ -0,0 +1,167 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
if [[ "${1:-}" != "--inside" ]]; then
exec timeout --signal=TERM --kill-after=5s 240s unshare --mount --net --fork --kill-child=TERM "$0" --inside
fi
ip link set lo up
root="/tmp/uce-dynamic-compile-failures-$$"
site="$root/site"
work="$root/work"
settings="$root/settings.cfg"
log="$root/service.log"
port=18080
server_pid=""
cleanup() {
status=$?
if [[ -n "$server_pid" ]] && kill -0 "$server_pid" 2>/dev/null; then
kill -TERM "$server_pid" 2>/dev/null || true
wait "$server_pid" 2>/dev/null || true
fi
if (( status != 0 )) && [[ -r "$log" ]]; then cat "$log" >&2; fi
while umount /etc/uce/settings.cfg 2>/dev/null; do :; done
rm -rf "$root"
return "$status"
}
trap cleanup EXIT
mkdir -p "$site" "$work" "$root/run" "$root/session" "$root/upload"
cat >"$site/driver.uce" <<'UCE'
RENDER(Request& context) {
print("driver:");
if(context.get["mode"] == "render") unit_render("broken-render", context);
else if(context.get["mode"] == "call") unit_call("broken-call", "COMPONENT");
else print(component("child", context));
}
UCE
printf '%s\n' 'COMPONENT(Request& context) { print("good-v1"); }' >"$site/child.uce"
printf '%s\n' 'RENDER(Request& context) { print("render-ok"); }' >"$site/broken-render.uce"
printf '%s\n' 'COMPONENT(Request& context) { print("call-ok"); }' >"$site/broken-call.uce"
write_settings() {
local show_errors="$1" serve_good="$2" proactive="$3"
sed -E '/^[[:space:]]*(BIN_DIRECTORY|PRECOMPILE_FILES_IN|SITE_DIRECTORY|FCGI_SOCKET_PATH|FCGI_PORT|CLI_SOCKET_PATH|WS_BROKER_SOCKET_PATH|HTTP_PORT|HTTP_DOCUMENT_ROOT|SESSION_PATH|TMP_UPLOAD_PATH|WASM_CORE_PATH|PROACTIVE_COMPILE_ENABLED|PROACTIVE_COMPILE_CHECK_INTERVAL|PROACTIVE_COMPILE_JOBS|WORKER_COUNT|SHOW_DYNAMIC_COMPILE_ERRORS|SERVE_LAST_KNOWN_GOOD)[[:space:]]*=/d' \
/etc/uce/settings.cfg >"$settings"
cat >>"$settings" <<CFG
BIN_DIRECTORY=$work
PRECOMPILE_FILES_IN=$site
SITE_DIRECTORY=$site
FCGI_SOCKET_PATH=$root/run/fastcgi.sock
FCGI_PORT=
CLI_SOCKET_PATH=$root/run/cli.sock
WS_BROKER_SOCKET_PATH=$root/run/ws.sock
HTTP_PORT=$port
HTTP_DOCUMENT_ROOT=$site
SESSION_PATH=$root/session
TMP_UPLOAD_PATH=$root/upload
WASM_CORE_PATH=$(pwd)/bin/wasm/core.wasm
PROACTIVE_COMPILE_ENABLED=$proactive
PROACTIVE_COMPILE_CHECK_INTERVAL=1
PROACTIVE_COMPILE_JOBS=1
WORKER_COUNT=1
SHOW_DYNAMIC_COMPILE_ERRORS=$show_errors
SERVE_LAST_KNOWN_GOOD=$serve_good
CFG
mount --bind "$settings" /etc/uce/settings.cfg
}
start_server() {
: >"$log"
bin/uce_fastcgi.linux.bin >"$log" 2>&1 &
server_pid=$!
for _ in $(seq 1 500); do
curl -sS --max-time 1 "http://127.0.0.1:$port/driver.uce" >/dev/null 2>&1 && return
kill -0 "$server_pid" 2>/dev/null || break
sleep 0.02
done
cat "$log" >&2
return 1
}
stop_server() {
kill -TERM "$server_pid" 2>/dev/null || true
wait "$server_pid" 2>/dev/null || true
server_pid=""
}
request() {
local method="${1:-GET}" query="${2:-}"
if [[ "$method" == POST ]]; then
curl -sS --max-time 30 -w $'\n%{http_code}' -X POST --data '' "http://127.0.0.1:$port/driver.uce?$query"
else
curl -sS --max-time 30 -w $'\n%{http_code}' "http://127.0.0.1:$port/driver.uce?$query"
fi
}
body_of() { printf '%s' "${1%$'\n'*}"; }
status_of() { printf '%s' "${1##*$'\n'}"; }
write_settings 1 0 0
start_server
printf '%s\n' 'COMPONENT(Request& context) { UCE_DYNAMIC_COMPILE_MARKER }' >"$site/child.uce"
response=$(request GET)
body=$(body_of "$response")
[[ "$(status_of "$response")" == 500 ]]
[[ "$body" == *UCE_DYNAMIC_COMPILE_MARKER* ]]
[[ "$body" != *"component not found"* ]]
printf '%s\n' 'RENDER(Request& context) { UCE_RENDER_COMPILE_MARKER }' >"$site/broken-render.uce"
response=$(request GET mode=render)
[[ "$(status_of "$response")" == 500 && "$(body_of "$response")" == *UCE_RENDER_COMPILE_MARKER* ]]
printf '%s\n' 'COMPONENT(Request& context) { UCE_CALL_COMPILE_MARKER }' >"$site/broken-call.uce"
response=$(request GET mode=call)
[[ "$(body_of "$response")" == *UCE_CALL_COMPILE_MARKER* ]]
[[ "$(body_of "$response")" != *"function 'COMPONENT' not found"* ]]
printf '%s\n' 'COMPONENT(Request& context) { print("recovered"); }' >"$site/child.uce"
response=$(request GET)
[[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:recovered" ]]
stop_server
write_settings 0 0 0
start_server
printf '%s\n' 'COMPONENT(Request& context) { UCE_HIDDEN_COMPILE_MARKER }' >"$site/child.uce"
response=$(request GET)
body=$(body_of "$response")
[[ "$(status_of "$response")" == 500 ]]
[[ "$body" == *"component not found: child"* ]]
[[ "$body" != *UCE_HIDDEN_COMPILE_MARKER* ]]
stop_server
printf '%s\n' 'COMPONENT(Request& context) { print("good-v1"); }' >"$site/child.uce"
write_settings 1 1 1
start_server
for _ in $(seq 1 100); do
response=$(request GET)
[[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:good-v1" ]] && break
sleep 0.1
done
[[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:good-v1" ]]
cache="$(scripts/unit_cache_directory "$work")$(realpath "$site")"
for artifact in "$cache/child.uce.wasm" "$cache/child.uce.cwasm" "$cache/child.uce.wasm.source-map"; do [[ -s "$artifact" ]]; done
before_hashes=$(sha256sum "$cache/child.uce.wasm" "$cache/child.uce.cwasm" "$cache/child.uce.wasm.source-map")
printf '%s\n' 'COMPONENT(Request& context) { UCE_STALE_COMPILE_MARKER }' >"$site/child.uce"
for _ in $(seq 1 30); do
response=$(request GET)
[[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:good-v1" ]]
sleep 0.1
done
[[ -s "$cache/child.uce.compile.txt" ]]
[[ "$(sha256sum "$cache/child.uce.wasm" "$cache/child.uce.cwasm" "$cache/child.uce.wasm.source-map")" == "$before_hashes" ]]
stop_server
start_server
response=$(request GET)
[[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:good-v1" ]] || { echo "last-known-good artifact did not survive worker restart: $response" >&2; exit 1; }
printf '%s\n' 'COMPONENT(Request& context) { print("permission-repair"); }' >"$site/child.uce"
chmod 000 "$site/child.uce"
for _ in $(seq 1 100); do
grep -q "source file is not readable" "$cache/child.uce.compile.txt" 2>/dev/null && break
sleep 0.05
done
[[ "$(sha256sum "$cache/child.uce.wasm" "$cache/child.uce.cwasm" "$cache/child.uce.wasm.source-map")" == "$before_hashes" ]]
response=$(request GET)
[[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:good-v1" ]] || { echo "unreadable source removed last-known-good service: $response" >&2; exit 1; }
chmod 644 "$site/child.uce"
printf '%s\n' 'COMPONENT(Request& context) { print("good-v2"); }' >"$site/child.uce"
for _ in $(seq 1 200); do
response=$(request GET)
if [[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:good-v2" ]]; then break; fi
sleep 0.1
done
[[ "$(status_of "$response")" == 200 && "$(body_of "$response")" == "driver:good-v2" ]] || { echo "repaired unit was not published: $response" >&2; exit 1; }
echo "dynamic compile failure handling passed"
+31 -14
View File
@@ -1236,6 +1236,8 @@ String compiler_format_source_read_failure(Request* context, SharedUnit* su, Str
void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadline* deadline)
{
f64 comp_start = time_precise();
bool preserve_last_known_good = compiler_preserve_last_known_good(context, su->file_name);
bool stage_artifacts = deadline || preserve_last_known_good;
if(!file_exists(su->file_name))
{
@@ -1261,7 +1263,8 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
file_put_contents(su->compile_output_file_name, su->compiler_messages + "\n");
file_put_contents(su->wasm_check_file_name, su->compiler_messages + "\n");
file_put_contents(su->meta_file_name, compiler_unit_metadata_text(context, su));
compiler_unlink_unit_wasm_artifacts(su);
if(!preserve_last_known_good)
compiler_unlink_unit_wasm_artifacts(su);
compiler_record_compile_result(su, time_precise() - comp_start, false, "compile_error", su->compiler_messages);
printf("%s \n", compiler_format_source_read_failure(context, su, su->compiler_messages).c_str());
if(deadline)
@@ -1281,7 +1284,7 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
String staged_api_name;
String staged_meta_name;
bool publication_failed = false;
if(deadline)
if(stage_artifacts)
{
u64 stage_id = compiler_invocation_stage_counter.fetch_add(1, std::memory_order_relaxed) + 1;
String stage_suffix = ".invocation-" + std::to_string((u64)getpid()) + "-" + std::to_string(stage_id);
@@ -1316,8 +1319,8 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
}
if(deadline && (deadline->timed_out || deadline->operational_failure))
break;
if(!file_put_contents(deadline ? staged_pre_name : su->pre_path + "/" + su->pre_file_name, generated_source) ||
!file_put_contents(deadline ? staged_api_name : su->api_file_name, join(su->api_declarations, "\n")))
if(!file_put_contents(stage_artifacts ? staged_pre_name : su->pre_path + "/" + su->pre_file_name, generated_source) ||
!file_put_contents(stage_artifacts ? staged_api_name : su->api_file_name, join(su->api_declarations, "\n")))
{
su->compiler_messages = "could not write generated bounded compile inputs";
break;
@@ -1327,8 +1330,8 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
shell_escape(su->src_path)+" "+
shell_escape(su->bin_path)+" "+
shell_escape(su->file_name)+" "+
shell_escape(deadline ? staged_pre_file_name : su->pre_file_name)+" "+
shell_escape(deadline ? staged_wasm_file_name : su->wasm_file_name)+" "+
shell_escape(stage_artifacts ? staged_pre_file_name : su->pre_file_name)+" "+
shell_escape(stage_artifacts ? staged_wasm_file_name : su->wasm_file_name)+" "+
shell_escape(compiler_unit_bin_directory(context));
if(deadline)
{
@@ -1353,7 +1356,7 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
else
su->compiler_messages = trim(shell_exec(compile_command));
String compiled_wasm_name = deadline ? staged_wasm_name : su->wasm_name;
String compiled_wasm_name = stage_artifacts ? staged_wasm_name : su->wasm_name;
if(su->compiler_messages.length() == 0 && !file_exists(compiled_wasm_name))
su->compiler_messages = "wasm compile script completed without creating " + compiled_wasm_name;
if(su->compiler_messages.length() > 0)
@@ -1361,9 +1364,9 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
String current_input_signature = compiler_unit_input_signature(context, su);
if(current_input_signature == compiled_input_signature)
{
if(deadline)
if(stage_artifacts)
{
if(deadline->expire_if_needed())
if(deadline && deadline->expire_if_needed())
break;
String source_map = file_get_contents(staged_map_name);
if(source_map == "")
@@ -1383,7 +1386,7 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
}
break;
}
if(deadline)
if(stage_artifacts)
{
file_unlink(staged_wasm_name);
file_unlink(staged_map_name);
@@ -1426,11 +1429,14 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
file_unlink(staged_api_name);
file_unlink(staged_meta_name);
compiler_record_compile_result(su, time_precise() - comp_start, false, "publish_error", su->compiler_messages);
deadline->operational_failure = true;
deadline->operational_error = su->compiler_messages;
if(deadline)
{
deadline->operational_failure = true;
deadline->operational_error = su->compiler_messages;
}
return;
}
if(deadline)
if(stage_artifacts)
{
file_unlink(staged_wasm_name);
file_unlink(staged_map_name);
@@ -1445,7 +1451,7 @@ void compile_shared_unit_bounded(Request* context, SharedUnit* su, CompilerDeadl
file_put_contents(su->compile_output_file_name, raw_messages + "\n");
file_put_contents(su->wasm_check_file_name, raw_messages + "\n");
file_put_contents(su->meta_file_name, compiler_unit_metadata_text(context, su, compiled_input_signature));
if(!publication_failed)
if(!publication_failed && !preserve_last_known_good)
compiler_unlink_unit_wasm_artifacts(su);
compiler_record_compile_result(su, time_precise() - comp_start, false, "compile_error", raw_messages);
printf("%s \n", compiler_format_compile_failure(context, su, raw_messages).c_str());
@@ -1691,10 +1697,21 @@ bool compiler_unit_compile_in_progress(Request* context, String file_name)
bool compiler_request_can_serve_stale_artifact(Request* context)
{
return(context && context->server && !context->resources.is_cli &&
context->params["UCE_WS"] != "1" &&
to_bool(context->server->config["SERVE_LAST_KNOWN_GOOD"], false) &&
to_bool(context->server->config["PROACTIVE_COMPILE_ENABLED"], true) &&
float_val(context->server->config["PROACTIVE_COMPILE_CHECK_INTERVAL"]) > 0);
}
bool compiler_preserve_last_known_good(Request* context, String file_name)
{
return(
context && context->server &&
to_bool(context->server->config["SERVE_LAST_KNOWN_GOOD"], false) &&
compiler_unit_can_serve_stale_artifact(context, file_name)
);
}
bool compiler_unit_can_serve_stale_artifact(Request* context, String file_name)
{
if(!compiler_request_can_serve_stale_artifact(context))
+1
View File
@@ -34,6 +34,7 @@ String compiler_error_page_unit(Request* context, String config_key);
bool compiler_unit_compile_pending(Request* context, String file_name);
bool compiler_unit_compile_in_progress(Request* context, String file_name);
bool compiler_request_can_serve_stale_artifact(Request* context);
bool compiler_preserve_last_known_good(Request* context, String file_name);
bool compiler_unit_can_serve_stale_artifact(Request* context, String file_name);
void compiler_prioritize_unit(Request* context, String file_name);
StringList compiler_take_priority_units(Request* context);
+2
View File
@@ -1925,6 +1925,8 @@ StringMap make_server_settings()
cfg["PRECOMPILE_JOBS"] = "2";
cfg["SITE_DIRECTORY"] = "site";
cfg["JIT_COMPILE_ON_REQUEST"] = "1";
cfg["SHOW_DYNAMIC_COMPILE_ERRORS"] = "1";
cfg["SERVE_LAST_KNOWN_GOOD"] = "0";
cfg["PROACTIVE_COMPILE_ENABLED"] = "1";
cfg["PROACTIVE_COMPILE_JOBS"] = "2";
cfg["COMPILE_FAILURE_RETRY_SECONDS"] = std::to_string(10);
+32 -11
View File
@@ -479,6 +479,7 @@ extern "C" int32_t uce_host_component_resolve(
// but a single workspace can render the same component many times)
static std::map<String, s32> wasm_component_slots;
static std::map<String, String> wasm_component_paths;
static std::map<String, String> wasm_component_errors;
// These mirror small page-runtime pieces that cannot include compiler.cpp in
// the wasm core (compiler.cpp owns parser/clang/cache bookkeeping for the host
@@ -548,7 +549,7 @@ struct RequestPropsScope
// handler is just a string: "render", "component:CARD", "render:VARIANT",
// "once", "cli", "websocket", "serve_http:named" — or "exists" (an existence
// probe that loads nothing). No per-mode kinds.
static s32 wasm_resolve_target(String unit_target, String handler, String* resolved_out = 0)
static s32 wasm_resolve_target(String unit_target, String handler, String* resolved_out = 0, String* error_out = 0)
{
String current = context ? context->resources.current_unit_file : "";
String cache_key = current + "\t" + handler + "\t" + unit_target;
@@ -558,21 +559,30 @@ static s32 wasm_resolve_target(String unit_target, String handler, String* resol
{
if(resolved_out)
*resolved_out = wasm_component_paths[cache_key];
if(error_out)
*error_out = wasm_component_errors[cache_key];
return(cached->second);
}
char resolved[512];
char resolved[4096];
s32 once_slot = 0;
s32 slot = uce_host_component_resolve(
unit_target.data(), unit_target.size(), handler.data(), handler.size(),
current.data(), current.size(),
resolved, sizeof(resolved), &once_slot);
String resolved_path = slot ? String(resolved, strnlen(resolved, sizeof(resolved))) : String("");
String response(resolved, strnlen(resolved, sizeof(resolved)));
String resolve_error = slot < 0 ? response : String("");
if(slot < 0)
slot = 0;
String resolved_path = slot ? response : String("");
if(resolved_out && slot)
*resolved_out = resolved_path;
if(error_out)
*error_out = resolve_error;
if(!is_exists)
{
wasm_component_slots[cache_key] = slot;
wasm_component_paths[cache_key] = resolved_path;
wasm_component_errors[cache_key] = resolve_error;
bool runs_once = handler == "render" || handler.rfind("render:", 0) == 0 ||
handler == "component" || handler.rfind("component:", 0) == 0;
if(slot && runs_once)
@@ -636,10 +646,14 @@ DValue* unit_call(String file_name, String function_name, DValue* call_param)
if(handler != "")
{
String resolved;
s32 slot = wasm_resolve_target(file_name, handler, &resolved);
String resolve_error;
s32 slot = wasm_resolve_target(file_name, handler, &resolved, &resolve_error);
if(!slot)
{
print("Error: unit_call() function '", function_name, "' not found");
if(resolve_error != "")
print("Error: ", resolve_error);
else
print("Error: unit_call() function '", function_name, "' not found");
return(0);
}
DValue props = call_param ? *call_param : DValue();
@@ -656,10 +670,14 @@ DValue* unit_call(String file_name, String function_name, DValue* call_param)
}
String resolved;
s32 slot = wasm_resolve_target(file_name, "export:" + function_name, &resolved);
String resolve_error;
s32 slot = wasm_resolve_target(file_name, "export:" + function_name, &resolved, &resolve_error);
if(!slot)
{
print("Error: unit_call() function '", function_name, "' not found");
if(resolve_error != "")
print("Error: ", resolve_error);
else
print("Error: unit_call() function '", function_name, "' not found");
return(0);
}
@@ -683,11 +701,12 @@ static void component_render_with_props(String name, DValue& props, Request& req
component_parse_target(trim(name), file_name, render_name);
String handler = render_name == "" ? String("component") : "component:" + render_name;
String resolved;
s32 slot = wasm_resolve_target(file_name, handler, &resolved);
String resolve_error;
s32 slot = wasm_resolve_target(file_name, handler, &resolved, &resolve_error);
if(!slot)
{
request.set_status(500, "Internal Server Error");
print(component_error_banner("component not found: " + trim(name)));
print(component_error_banner(resolve_error != "" ? resolve_error : "component not found: " + trim(name)));
return;
}
wasm_run_once(resolved, request);
@@ -724,11 +743,12 @@ void unit_render(String file_name, Request& request)
component_parse_target(trim(file_name), unit_name, render_name);
String handler = render_name == "" ? String("render") : "render:" + render_name;
String resolved;
s32 slot = wasm_resolve_target(unit_name, handler, &resolved);
String resolve_error;
s32 slot = wasm_resolve_target(unit_name, handler, &resolved, &resolve_error);
if(!slot)
{
request.set_status(500, "Internal Server Error");
print(component_error_banner("unit not found: " + trim(file_name)));
print(component_error_banner(resolve_error != "" ? resolve_error : "unit not found: " + trim(file_name)));
return;
}
wasm_run_once(resolved, request);
@@ -857,6 +877,7 @@ void uce_wasm_core_reset_request()
wasm_request.resources.current_unit_file = "";
wasm_component_slots.clear();
wasm_component_paths.clear();
wasm_component_errors.clear();
}
// Host pushes the worker-cached immutable configuration followed by the
+24 -4
View File
@@ -2995,12 +2995,15 @@ private:
// `handler` names the export ("render", "component:CARD", "cli",
// "serve_http:named", "once") or is "exists" (probe only, loads nothing).
int32_t component_resolve(const String& target, const String& handler, const String& current_unit,
String& resolved_out, int32_t* once_slot_out = 0, bool* compile_timed_out = 0)
String& resolved_out, int32_t* once_slot_out = 0, bool* compile_timed_out = 0,
String* compile_error_out = 0)
{
if(once_slot_out)
*once_slot_out = 0;
if(compile_timed_out)
*compile_timed_out = false;
if(compile_error_out)
compile_error_out->clear();
auto probe_start = std::chrono::steady_clock::now();
auto record_probe = [&]() {
component_resolve_count += 1;
@@ -3130,10 +3133,22 @@ private:
{
u64 remaining_ms = invocation_remaining_ms();
bool timed_out = false;
SharedUnit* compiled_unit = 0;
if(remaining_ms == 0)
timed_out = true;
else
get_shared_unit_bounded(context, resolved, remaining_ms, &timed_out);
compiled_unit = get_shared_unit_bounded(context, resolved, remaining_ms, &timed_out);
if(compiled_unit && compile_error_out &&
to_bool(context->server->config["SHOW_DYNAMIC_COMPILE_ERRORS"], true))
{
String detail = trim(first(compiled_unit->compiler_messages, compiled_unit->compile_error_status));
if(detail != "")
{
*compile_error_out = "compile failed for " + resolved + ":\n" + detail;
if(compile_error_out->size() > 4095)
*compile_error_out = compile_error_out->substr(0, 4064) + "\n[compile error truncated]";
}
}
if(timed_out)
{
if(compile_timed_out)
@@ -4521,15 +4536,20 @@ private:
}));
if(mod == "env" && name == "uce_host_component_resolve")
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
String target, handler, current, resolved;
String target, handler, current, resolved, compile_error;
self->hostcall_read(args[0].i32(), args[1].i32(), target);
self->hostcall_read(args[2].i32(), args[3].i32(), handler);
self->hostcall_read(args[4].i32(), args[5].i32(), current);
int32_t once_slot = 0;
bool compile_timed_out = false;
int32_t slot = self->component_resolve(target, handler, current, resolved, &once_slot, &compile_timed_out);
int32_t slot = self->component_resolve(target, handler, current, resolved, &once_slot, &compile_timed_out, &compile_error);
if(compile_timed_out)
return(Trap(self->invocation_timeout_error()));
if(slot == 0 && compile_error != "")
{
resolved = compile_error;
slot = -1;
}
u32 cap = (u32)args[7].i32();
if(cap > 0)
{