]
path and looks up the export's funcref slot; `exists` lets callers probe a unit
without instantiating it.
-`wasm_backend_should_handle(request, entry_unit)` gates whether the wasm path
-takes the request (vs. the legacy native compiler path, still selectable while
-the native pipeline is retired). With `WASM_BACKEND_ENABLED=1` this is the
-default for all unit execution.
+`wasm_backend_should_handle(request, entry_unit)` checks whether the wasm
+backend is initialized and the requested artifact/handler is currently
+available. If an artifact is cold or stale, dispatch compiles it on demand via
+`get_shared_unit()` and rechecks. There is no native unit-execution fallback.
---
@@ -171,9 +173,9 @@ The `UCE_*` params are set by whichever broker forwarded the request:
`request.resources.websocket_*` and `request.connection` from them before
invoking `__uce_websocket`.
-Each branch has a native-compiler fallback (`compiler_invoke*`) used only when
-`wasm_backend_should_handle` declines — the holdout path during native
-retirement.
+If a unit still cannot be served after the on-demand wasm compile, the worker
+returns a clean 500 with the wasm/compile error; it does not execute native unit
+code.
---
@@ -294,7 +296,6 @@ header free-functions are `inline`. The wasm backend exposes only declarations
| Key | Default | Meaning |
|---|---|---|
-| `WASM_BACKEND_ENABLED` | `1` | Route unit execution through the wasm path. |
| `WASM_BACKEND_VERBOSE` | `0` | Emit `X-UCE-Wasm-*` workspace timing headers (benchmark only). |
| `FCGI_SOCKET_PATH` | `/run/uce.sock` | Worker pool FastCGI socket (brokers forward here). |
| `CLI_SOCKET_PATH` | `/run/uce/cli.sock` | Worker CLI socket. |
@@ -308,7 +309,7 @@ header free-functions are `inline`. The wasm backend exposes only declarations
- **Regression gate**: `scripts/run_cli_tests.sh --include-wasm-kill` runs the
in-runtime CLI suite (`site/tests/cli_runner.uce`) plus the site test pages.
- Current baseline: **86 passed, 0 failed**.
+ Current baseline: **87 passed, 0 failed**.
- **WebSocket end-to-end**: a headless client performs a raw WS handshake to
`:HTTP_PORT` with path `/site/tests/websockets.ws.uce` (self-resolving
`SCRIPT_FILENAME`) and asserts the `hello-ack` frame — exercising the full
diff --git a/etc/uce/settings.cfg b/etc/uce/settings.cfg
index 1b94315..84205db 100644
--- a/etc/uce/settings.cfg
+++ b/etc/uce/settings.cfg
@@ -21,13 +21,10 @@ SITE_DIRECTORY=site
# ENABLE JIT COMPILATION WHEN A PAGE REQUEST HITS A STALE OR MISSING UNIT
JIT_COMPILE_ON_REQUEST=1
-# OPTIONAL W2 WEBASSEMBLY SIDE-MODULE COMPILATION BESIDE NATIVE .so UNITS
-COMPILE_WASM_UNITS=0
+# WASM SIDE-MODULE COMPILER USED FOR .uce UNITS
WASM_COMPILE_SCRIPT=scripts/compile_wasm_unit
-# DEFAULT PAGE RENDER BACKEND. W5 defaults to the WASM backend with explicit
-# native fallbacks for host-owned surfaces that are not membrane APIs yet.
-WASM_BACKEND_ENABLED=1
+# WASM RUNTIME SETTINGS. Unit execution is always routed through wasm.
WASM_BACKEND_VERBOSE=0
WASM_CORE_PATH=/Code/uce.openfu.com/uce/bin/wasm/core.wasm
WASM_MEMORY_LIMIT_BYTES=536870912
diff --git a/scripts/build_linux.sh b/scripts/build_linux.sh
index b707daf..4269e9f 100755
--- a/scripts/build_linux.sh
+++ b/scripts/build_linux.sh
@@ -9,11 +9,11 @@ GF="uce_fastcgi"
mkdir bin > /dev/null 2>&1
mkdir bin/tmp > /dev/null 2>&1
mkdir bin/assets > /dev/null 2>&1
+mkdir bin/wasm > /dev/null 2>&1
mkdir work > /dev/null 2>&1
COMPILER="clang++"
-# -rdynamic is a link-time flag (exports the binary's symbols so dlopen'd .uce
-# units resolve the runtime against it); the -c compiles below do not need it.
+# -rdynamic is a link-time flag; the -c compiles below do not need it.
FLAGS="-g -w -Wall -$OPT_FLAG -std=c++20 -fpermissive -ffast-math"
# Wasmtime C++ API — needed only by the wasm backend object (src/wasm).
@@ -30,8 +30,8 @@ SRCFLAGS="-D EXEC_NAME=\"$GF\" -D PLATFORM_NAME=\"linux\""
# bin/sqlite3.o vendored SQLite amalgamation (depends only on its own source)
# bin/wasm.o wasm backend + worker + wasmtime.hh (src/wasm)
# bin/main.o linux_fastcgi.cpp + the uce_lib core amalgamation
-# All link into the single -rdynamic binary, so the dlopen unit model is
-# unchanged. Delete bin/*.o to force a clean rebuild.
+# All link into the single -rdynamic binary. Delete bin/*.o to force a clean
+# rebuild.
# Rebuild $1 if it is missing or anything under the remaining find-args is newer.
needs_rebuild() {
@@ -41,6 +41,14 @@ needs_rebuild() {
return 1
}
+# core.wasm: guest runtime loaded by the native wasm backend.
+if needs_rebuild bin/wasm/core.wasm src/wasm/core.cpp src/lib src/wasm/core_hostcalls.syms src/wasm/core_libc_exports.syms scripts/build_core_wasm.sh; then
+ echo "Compiling wasm core..."
+ bash scripts/build_core_wasm.sh || exit 1
+else
+ echo "Reusing bin/wasm/core.wasm"
+fi
+
# SQLite: vendored C, depends only on its own source (not our headers).
if needs_rebuild bin/sqlite3.o src/3rdparty/sqlite/sqlite3.c src/3rdparty/sqlite/sqlite3.h; then
echo "Compiling SQLite..."
diff --git a/scripts/compile_wasm_unit b/scripts/compile_wasm_unit
index 078b20a..007bd00 100755
--- a/scripts/compile_wasm_unit
+++ b/scripts/compile_wasm_unit
@@ -50,7 +50,7 @@ build_pch_if_needed() {
return 0
fi
mkdir -p "$PCH_DIR"
- if [ -s "$PCH_FN" ]; then
+ if [ -s "$PCH_FN" ] && [ -z "$(find src/lib -maxdepth 1 -name '*.h' -type f -newer "$PCH_FN" -print -quit)" ]; then
return 0
fi
"$SDK/bin/clang++" "${COMMON_FLAGS[@]}" \
diff --git a/scripts/wasm/run_w5.sh b/scripts/wasm/run_w5.sh
index 81d1c5a..bc75453 100755
--- a/scripts/wasm/run_w5.sh
+++ b/scripts/wasm/run_w5.sh
@@ -1,94 +1,19 @@
#!/bin/bash
-# W5 parity/performance gate for the config-selectable WASM backend.
-# Runs on k-uce. Requires root because the live service reads /etc/uce/settings.cfg.
+# Retired W5 parity/performance gate.
+#
+# W7e removed the config-selectable native backend and all native unit
+# execution. There is no longer a valid WASM_BACKEND_ENABLED=0 baseline to
+# switch to here; doing so would only create a misleading service state.
set -euo pipefail
-cd "$(dirname "$0")/../.."
+cat >&2 <<'EOF'
+run_w5.sh is retired: UCE unit execution is wasm-only after W7e.
+Use the supported regression gate instead:
-if [ "${EUID:-$(id -u)}" -ne 0 ]; then
- echo "run_w5.sh must run as root so it can switch /etc/uce/settings.cfg and restart uce.service" >&2
- exit 1
-fi
+ bash scripts/build_linux.sh && systemctl restart uce.service && sleep 3 \
+ && bash scripts/run_cli_tests.sh --include-wasm-kill
-OUT=${UCE_W5_OUT:-/tmp/uce/wasm-w5}
-CONFIG=${UCE_CONFIG:-/etc/uce/settings.cfg}
-mkdir -p "$OUT"
-BACKUP="$OUT/settings.cfg.before-w5"
-cp "$CONFIG" "$BACKUP"
-
-restore_on_error() {
- if [ "${UCE_W5_KEEP_BACKEND:-0}" != "1" ]; then
- cp "$BACKUP" "$CONFIG"
- systemctl restart uce.service >/dev/null 2>&1 || true
- fi
-}
-trap restore_on_error EXIT
-
-set_backend() {
- local enabled="$1"
- local tmp
- tmp=$(mktemp)
- awk -F= -v enabled="$enabled" '
- BEGIN { found = 0 }
- /^WASM_BACKEND_ENABLED=/ { print "WASM_BACKEND_ENABLED=" enabled; found = 1; next }
- { print }
- END { if(!found) print "WASM_BACKEND_ENABLED=" enabled }
- ' "$CONFIG" > "$tmp"
- for kv in \
- 'WASM_BACKEND_VERBOSE=0' \
- 'WASM_CORE_PATH=/Code/uce.openfu.com/uce/bin/wasm/core.wasm' \
- 'WASM_MEMORY_LIMIT_BYTES=536870912' \
- 'WASM_EPOCH_DEADLINE_TICKS=200' \
- 'WASM_EPOCH_PERIOD_MS=50'
- do
- key=${kv%%=*}
- if ! grep -q "^${key}=" "$tmp"; then
- printf '%s\n' "$kv" >> "$tmp"
- fi
- done
- cat "$tmp" > "$CONFIG"
- rm -f "$tmp"
- systemctl restart uce.service >/dev/null
- sleep 1
-}
-
-summary_total() {
- awk '/^Summary:/ { print $2; found=1 } END { if(!found) print 0 }' "$1"
-}
-
-# Native reference baseline.
-set_backend 0
-scripts/run_cli_tests.sh > "$OUT/native-warmup.txt" || true
-scripts/run_cli_tests.sh > "$OUT/native-network.txt"
-python3 tests/wasm_benchmark.py --out-dir "$OUT/native-benchmark" --samples "${UCE_W5_BENCH_SAMPLES:-20}" --timeout 30 >/dev/null
-
-# WASM default backend with W5 native fallbacks for host-owned surfaces.
-set_backend 1
-scripts/run_cli_tests.sh --include-wasm-kill > "$OUT/wasm-warmup.txt" || true
-scripts/run_cli_tests.sh --include-wasm-kill > "$OUT/wasm-network.txt"
-python3 tests/wasm_benchmark.py \
- --out-dir "$OUT/benchmark" \
- --backend-label wasm \
- --compare-native-json "$OUT/native-benchmark/benchmark.json" \
- --samples "${UCE_W5_BENCH_SAMPLES:-20}" \
- --timeout 30
-python3 tests/wasm_site_audit.py --out-dir "$OUT" >/dev/null
-
-network_cases=$(summary_total "$OUT/wasm-network.txt")
-if [ "$network_cases" -lt 80 ]; then
- echo "W5 HARNESS: FAIL"
- echo "wasm network ran $network_cases cases, expected at least 80"
- exit 1
-fi
-
-echo 'W5 HARNESS: PASS'
-echo "network_cases=$network_cases benchmark_rows=see benchmark.json"
-echo "reports=$OUT"
-
-if [ "${UCE_W5_KEEP_BACKEND:-0}" = "1" ]; then
- trap - EXIT
- printf 'WASM backend left enabled in %s\n' "$CONFIG"
-else
- cp "$BACKUP" "$CONFIG"
- systemctl restart uce.service >/dev/null
- trap - EXIT
-fi
+For performance comparisons, use tests/wasm_benchmark.py against the current
+wasm backend; native-baseline artifacts under docs/wasm-baselines are historical
+only.
+EOF
+exit 2
diff --git a/src/lib/compiler-parser.cpp b/src/lib/compiler-parser.cpp
index 4460c14..e80650f 100644
--- a/src/lib/compiler-parser.cpp
+++ b/src/lib/compiler-parser.cpp
@@ -564,7 +564,10 @@ String compiler_preprocess_shared_unit_char_wise(Request* context, SharedUnit* s
parsed_content.resize(parsed_content.length() - current_line.length());
nibble(current_line, "\"");
String unit_name = nibble(current_line, "\"");
- SharedUnit* sub_su = compiler_load_shared_unit(context, unit_name, su->src_path, true);
+ String resolved_unit = unit_name;
+ if(resolved_unit != "" && resolved_unit[0] != '/')
+ resolved_unit = expand_path(resolved_unit, su->src_path);
+ SharedUnit* sub_su = (resolved_unit == "" ? 0 : get_shared_unit(context, resolved_unit, true));
if(sub_su)
parsed_content.append("#include \"" + sub_su->bin_path + "/" + sub_su->pre_file_name + "\"\n");
}
diff --git a/src/lib/compiler.cpp b/src/lib/compiler.cpp
index 0009946..426dd29 100644
--- a/src/lib/compiler.cpp
+++ b/src/lib/compiler.cpp
@@ -10,14 +10,6 @@
namespace {
-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_CLI_SYMBOL = "__uce_cli";
-const char* UCE_SERVE_HTTP_SYMBOL = "__uce_serve_http";
-const char* UCE_ONCE_SYMBOL = "__uce_once";
-const char* UCE_INIT_SYMBOL = "__uce_init";
const u64 UCE_UNIT_ABI_VERSION = 6;
struct SharedUnitFilesystemState
@@ -33,9 +25,6 @@ struct SharedUnitFilesystemState
time_t metadata_time = 0;
time_t compile_output_time = 0;
time_t setup_template_time = 0;
- time_t compiler_header_time = 0;
- time_t compiler_source_time = 0;
- time_t runtime_binary_time = 0;
time_t compiler_abi_time = 0;
time_t required_time = 0;
u64 metadata_abi_version = 0;
@@ -53,10 +42,6 @@ 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_jit_compile_on_request_enabled(Request* context)
{
if(!context || !context->server)
@@ -132,13 +117,6 @@ String compiler_unit_metadata_text(Request* context, SharedUnit* su)
);
}
-bool compiler_wasm_unit_compile_enabled(Request* context)
-{
- if(!context || !context->server)
- return(false);
- return(config_bool("COMPILE_WASM_UNITS", false));
-}
-
String compiler_wasm_compile_script(Request* context)
{
String script = "scripts/compile_wasm_unit";
@@ -207,17 +185,11 @@ void compiler_restore_persisted_failure(SharedUnit* su, const SharedUnitFilesyst
time_t compiler_runtime_abi_time(Request* context)
{
- if(!context || !context->server)
- return(0);
-
- String root = context->server->config["COMPILER_SYS_PATH"];
- return(std::max({
- file_mtime(root + "/src/lib/compiler.h"),
- file_mtime(root + "/src/lib/compiler.cpp"),
- file_mtime(root + "/src/lib/compiler-parser.h"),
- file_mtime(root + "/src/lib/compiler-parser.cpp"),
- file_mtime(root + "/bin/uce_fastcgi.linux.bin")
- }));
+ (void)context;
+ // Do not force every unit stale on every server/core rebuild. The stable unit
+ // ABI is tracked explicitly in compiler_unit_input_signature() via
+ // UCE_UNIT_ABI_VERSION; bump that when generated-unit/runtime ABI changes.
+ return(0);
}
String compiler_registry_file_name(Request* context)
@@ -335,9 +307,6 @@ SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, Share
context->server->config["COMPILER_SYS_PATH"] + "/" +
context->server->config["SETUP_TEMPLATE"]
);
- state.compiler_header_time = file_mtime(context->server->config["COMPILER_SYS_PATH"] + "/src/lib/compiler.h");
- state.compiler_source_time = file_mtime(context->server->config["COMPILER_SYS_PATH"] + "/src/lib/compiler.cpp");
- state.runtime_binary_time = file_mtime(context->server->config["COMPILER_SYS_PATH"] + "/bin/uce_fastcgi.linux.bin");
state.compiler_abi_time = compiler_runtime_abi_time(context);
state.metadata_time = file_mtime(su->meta_file_name);
state.compile_output_time = file_mtime(su->compile_output_file_name);
@@ -371,17 +340,9 @@ SharedUnitFilesystemState inspect_shared_unit_filesystem(Request* context, Share
state.metadata_input_signature == state.current_input_signature
);
state.required_time = std::max({state.source_time, state.setup_template_time, state.compiler_abi_time});
- state.compiled_time = file_mtime(su->so_name);
- if(compiler_wasm_unit_compile_enabled(context))
- {
- time_t wasm_time = file_mtime(su->wasm_name);
- // The wasm backend serves the .wasm, so a missing/stale .wasm must
- // trigger a rebuild even when the .so is current. Treat the unit as
- // compiled only as of the OLDER artifact; a missing .wasm (mtime 0)
- // forces recompile via the existing needs_compile == (compiled_time==0).
- if(wasm_time == 0 || wasm_time < state.compiled_time)
- state.compiled_time = wasm_time;
- }
+ // Native .so execution has been removed; compile freshness is the wasm
+ // side-module freshness.
+ state.compiled_time = file_mtime(su->wasm_name);
return(state);
}
@@ -572,8 +533,6 @@ String compiler_status_from_filesystem(const SharedUnitFilesystemState& state, S
return("not_compiled");
if(compile_check.needs_compile)
return("stale");
- if(su && su->so_handle)
- return("loaded");
return("compiled");
}
@@ -686,85 +645,6 @@ void setup_unit_paths(Request* context, SharedUnit* su, String file_name)
//su->setup_file_name = su->bin_path + "/" + su->src_file_name + ".setup.h";
}
-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 = "";
-
- if(!file_exists(su->so_name))
- {
- if(su->opt_so_optional)
- {
- su->compile_status = "not_compiled";
- return;
- }
- auto fs_state = inspect_shared_unit_filesystem(context, su);
- if(compiler_failure_retry_deferred(context, su, fs_state))
- {
- compiler_restore_persisted_failure(su, fs_state);
- return;
- }
- //printf("(i) unit file not found: %s\n", su->so_name.c_str());
- su->compiler_messages = "unit file not found";
- su->compile_status = "not_compiled";
- su->compile_error_status = su->compiler_messages;
- su->last_error = time();
- return;
- }
-
- su->so_handle = dlopen(su->so_name.c_str(), RTLD_NOW);
- if(su->so_handle)
- {
- su->last_compiled = file_mtime(su->so_name);
- su->last_loaded = time();
- su->compile_status = "loaded";
- su->compile_error_status = "";
- char *error;
- su->on_setup = (request_handler)dlsym(su->so_handle, UCE_SETUP_SYMBOL);
- if ((error = dlerror()) != NULL)
- printf("Error - %s in %s\n", error, su->file_name.c_str());
- su->on_render = (request_ref_handler)dlsym(su->so_handle, UCE_RENDER_SYMBOL);
- dlerror();
- su->on_component = (request_ref_handler)dlsym(su->so_handle, UCE_COMPONENT_SYMBOL);
- dlerror();
- su->on_websocket = (request_ref_handler)dlsym(su->so_handle, UCE_WEBSOCKET_SYMBOL);
- dlerror();
- su->on_cli = (request_ref_handler)dlsym(su->so_handle, UCE_CLI_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, %s\n", su->file_name.c_str(), su->compiler_messages.c_str());
- }
-}
-
/*String compile_setup_file(Request* context, SharedUnit* su)
{
String result =
@@ -860,63 +740,40 @@ void compile_shared_unit(Request* context, SharedUnit* su)
file_put_contents(su->pre_path + "/" + su->pre_file_name, preprocess_shared_unit(context, su));
file_put_contents(su->api_file_name, join(su->api_declarations, "\n"));
- if(!su->opt_so_optional)
- su->compiler_messages = trim(shell_exec(shell_escape(context->server->config["COMPILE_SCRIPT"])+" "+
- shell_escape(su->src_path)+" "+
- shell_escape(su->bin_path)+" "+
- shell_escape(su->file_name)+" "+
- shell_escape(su->pre_file_name)+" "+
- shell_escape(su->bin_file_name)
- ));
+ su->compiler_messages = trim(shell_exec(shell_escape(compiler_wasm_compile_script(context))+" "+
+ shell_escape(su->src_path)+" "+
+ shell_escape(su->bin_path)+" "+
+ shell_escape(su->file_name)+" "+
+ shell_escape(su->pre_file_name)+" "+
+ shell_escape(su->wasm_file_name)
+ ));
- if(su->compiler_messages.length() == 0 && !su->opt_so_optional && compiler_wasm_unit_compile_enabled(context))
- {
- // The wasm side-module build is best-effort: a unit that cannot compile
- // to wasm (e.g. try/catch) still works via the native .so and falls back
- // to native at request time. Record the failure, never fail the unit on
- // it. On failure, remove any stale .wasm so the backend won't serve it.
- String wasm_messages = trim(shell_exec(shell_escape(compiler_wasm_compile_script(context))+" "+
- shell_escape(su->src_path)+" "+
- shell_escape(su->bin_path)+" "+
- shell_escape(su->file_name)+" "+
- shell_escape(su->pre_file_name)+" "+
- shell_escape(su->wasm_file_name)
- ));
- if(wasm_messages.length() > 0)
- {
- file_put_contents(su->wasm_check_file_name, wasm_messages + "\n");
- file_unlink(su->wasm_name);
- printf("(i) wasm side-module unavailable for %s (native .so serves it)\n", su->file_name.c_str());
- }
- }
+ if(su->compiler_messages.length() == 0 && !file_exists(su->wasm_name))
+ su->compiler_messages = "wasm compile script completed without creating " + su->wasm_name;
if(su->compiler_messages.length() > 0)
{
String raw_messages = su->compiler_messages;
file_put_contents(su->compile_output_file_name, raw_messages + "\n");
+ file_put_contents(su->wasm_check_file_name, raw_messages + "\n");
+ file_unlink(su->wasm_name);
compiler_record_compile_result(su, time_precise() - comp_start, false, "compile_error", raw_messages);
printf("%s \n", compiler_format_compile_failure(su, raw_messages).c_str());
}
else
{
- load_shared_unit(context, su);
- if(su->so_handle)
- {
- file_put_contents(su->meta_file_name, compiler_unit_metadata_text(context, su));
- file_unlink(su->compile_output_file_name);
- }
- else if(trim(su->compiler_messages) != "")
- {
- file_put_contents(su->compile_output_file_name, su->compiler_messages + "\n");
- }
+ su->last_compiled = file_mtime(su->wasm_name);
+ file_put_contents(su->meta_file_name, compiler_unit_metadata_text(context, su));
+ file_unlink(su->compile_output_file_name);
+ file_unlink(su->wasm_check_file_name);
compiler_record_compile_result(
su,
time_precise() - comp_start,
- (su->so_handle != 0),
- (su->so_handle ? "loaded" : "load_error"),
+ file_exists(su->wasm_name),
+ (file_exists(su->wasm_name) ? "compiled" : "compile_error"),
compiler_error_status(su)
);
- printf("(i) compiled unit %s in %f s\n",
+ printf("(i) compiled wasm unit %s in %f s\n",
(su->pre_path + "/" + su->pre_file_name).c_str(),
time_precise() - comp_start);
}
@@ -980,23 +837,10 @@ SharedUnit* compiler_get_shared_unit_internal(Request* context, String file_name
}
else
{
- load_shared_unit(context, su);
- if(!su->so_handle)
- {
- if(!force_recompile && retry_deferred)
- compiler_restore_persisted_failure(su, state);
- else if(!force_recompile && !jit_enabled)
- {
- compiler_restore_persisted_failure(su, state, "jit_compile_disabled");
- if(trim(su->compiler_messages) == "")
- {
- su->compiler_messages = "JIT compilation on request is disabled";
- su->compile_error_status = su->compiler_messages;
- }
- }
- else
- compile_shared_unit(context, su);
- }
+ su->api_declarations = split(file_get_contents(su->api_file_name), "\n");
+ su->last_compiled = state.compiled_time;
+ su->compile_status = compiler_status_from_filesystem(state, su);
+ su->compile_error_status = "";
}
auto observed_state = inspect_shared_unit_filesystem(context, su);
@@ -1013,60 +857,129 @@ SharedUnit* get_shared_unit(Request* context, String file_name, bool opt_so_opti
return(compiler_get_shared_unit_internal(context, file_name, opt_so_optional, false));
}
-SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String current_path, bool opt_so_optional)
+String compiler_error_page_unit(Request* context, String config_key)
{
+ if(!context || !context->server)
+ return("");
+ String configured = trim(first(
+ context->server->config[config_key],
+ context->server->config[to_upper(config_key)]
+ ));
+ if(configured == "")
+ return("");
+ // Resolve relative to where the server was started, not the volatile
+ // per-unit working directory (which is also stale right after a fault).
+ String resolved = compiler_resolve_unit_path(context, configured, process_start_directory());
+ if(resolved == "" || !file_exists(resolved))
+ return("");
+ return(resolved);
+}
- context->stats.invoke_count++;
+bool compiler_unit_compile_pending(Request* context, String file_name)
+{
+ String normalized = compiler_normalize_unit_path(context, file_name);
+ if(normalized == "" || !file_exists(normalized))
+ return(false);
+ if(compiler_reusable_cached_unit(context, normalized, false, false))
+ return(false);
- file_name = compiler_resolve_unit_path(context, file_name, current_path);
- if(file_name == "")
- return(0);
- if(file_exists(file_name))
- compiler_track_known_unit(context, file_name);
- else
- compiler_untrack_known_unit(context, file_name);
+ SharedUnit probe;
+ setup_unit_paths(context, &probe, normalized);
+ auto state = inspect_shared_unit_filesystem(context, &probe);
+ if(!shared_unit_compile_check(state).needs_compile)
+ return(false);
+ // A persisted, still-current failure is a compiler error, not a build in
+ // progress: let the load path surface it.
+ if(compiler_failure_retry_deferred(context, &probe, state))
+ return(false);
+ return(true);
+}
- //printf("(i) load '%s'\n", file_name.c_str());
+void unit_render(String file_name)
+{
+ unit_render(file_name, *context);
+}
- //switch_to_system_alloc();
- auto su = get_shared_unit(context, file_name, opt_so_optional);
- //switch_to_arena(context->mem);
- if(!su)
- {
- printf("Error loading unit %s\n", file_name.c_str());
- if(compiler_can_write_response(context))
- print("Error loading unit: " + file_name);
- return(0);
- }
- else if(su->compiler_messages.length() > 0)
- {
- String display_messages = su->compiler_messages;
- if(su->compile_status == "compile_error")
- display_messages = compiler_format_compile_failure(su, su->compiler_messages);
- printf("%s\n", display_messages.c_str());
- if(compiler_can_write_response(context) && context->stats.invoke_count == 1)
- {
- DValue error_info;
- error_info["type"] = "compiler_error";
- error_info["status"] = su->compile_status;
- error_info["source"] = su->file_name;
- error_info["compiler_output"] = su->compiler_messages;
- error_info["generated_cpp"] = compiler_generated_cpp_path(su);
- error_info["details"] = display_messages;
- if(compiler_render_error_page(context, "page_compiler_error", 500, "UCE Unit Compile Error", error_info))
- return(0);
- context->set_status(500, "UCE Unit Compile Error");
- context->header["Content-Type"] = "text/plain; charset=utf-8";
- }
- if(compiler_can_write_response(context))
- print(display_messages);
- return(0);
- }
- else
- {
- return(su);
- }
+void unit_render(String file_name, Request& request)
+{
+ request.set_status(500, "Native Unit Render Removed");
+ print("native unit_render() is unavailable; units must render through the wasm backend: " + file_name);
+}
+String component_resolve(String name)
+{
+ (void)name;
+ return("");
+}
+
+bool component_exists(String name)
+{
+ return(component_resolve(name) != "");
+}
+
+String component_error_banner(String message)
+{
+ return("" + html_escape(message) + "
");
+}
+
+void component_render(String name)
+{
+ DValue props;
+ component_render(name, props, *context);
+}
+
+void component_render(String name, Request& context)
+{
+ DValue props;
+ component_render(name, props, context);
+}
+
+void component_render(String name, DValue props)
+{
+ component_render(name, props, *context);
+}
+
+void component_render(String name, DValue props, Request& context)
+{
+ (void)props; (void)context;
+ print(component_error_banner("native component_render() is unavailable; components must render through the wasm backend: " + trim(name)));
+}
+
+String component(String name)
+{
+ DValue props;
+ return(component(name, props, *context));
+}
+
+String component(String name, Request& context)
+{
+ DValue props;
+ return(component(name, props, context));
+}
+
+String component(String name, DValue props)
+{
+ return(component(name, props, *context));
+}
+
+String component(String name, DValue props, Request& context)
+{
+ ob_start();
+ component_render(name, props, context);
+ return(ob_get_close());
+}
+
+SharedUnit* unit_load(String file_name)
+{
+ String resolved = compiler_resolve_unit_path(context, file_name);
+ return(resolved == "" ? 0 : get_shared_unit(context, resolved, true));
+}
+
+DValue* unit_call(String file_name, String function_name, DValue* call_param)
+{
+ (void)call_param;
+ print("Error: native unit_call() is unavailable; units must call through the wasm workspace: ", file_name, "::", function_name);
+ return(0);
}
String compiler_site_directory(Request* context)
@@ -1094,17 +1007,12 @@ StringList compiler_scan_site_units(Request* context)
{
if(walk_error)
{
- printf("(!) proactive scan warning in %s: %s\n",
- site_directory.c_str(),
- walk_error.message().c_str());
+ printf("(!) proactive scan warning in %s: %s\n", site_directory.c_str(), walk_error.message().c_str());
walk_error.clear();
continue;
}
-
std::error_code entry_error;
- if(!it->is_regular_file(entry_error))
- continue;
- if(entry_error)
+ if(!it->is_regular_file(entry_error) || entry_error)
continue;
auto path = it->path().string();
if(path.length() >= 4 && path.substr(path.length() - 4) == ".uce")
@@ -1115,9 +1023,7 @@ StringList compiler_scan_site_units(Request* context)
StringList compiler_list_known_units(Request* context)
{
- return(compiler_with_registry_lock(context, [&]() {
- return(compiler_read_known_units_unlocked(context));
- }));
+ return(compiler_with_registry_lock(context, [&]() { return(compiler_read_known_units_unlocked(context)); }));
}
void compiler_set_known_units(Request* context, StringList files)
@@ -1134,7 +1040,6 @@ void compiler_track_known_unit(Request* context, String file_name)
file_name = compiler_normalize_unit_path(context, file_name);
if(file_name == "" || !compiler_is_known_unit_file(file_name))
return;
-
compiler_with_registry_lock(context, [&]() {
auto files = compiler_read_known_units_unlocked(context);
if(std::find(files.begin(), files.end(), file_name) != files.end())
@@ -1150,7 +1055,6 @@ void compiler_untrack_known_unit(Request* context, String file_name)
file_name = compiler_normalize_unit_path(context, file_name);
if(file_name == "")
return;
-
compiler_with_registry_lock(context, [&]() {
auto files = compiler_read_known_units_unlocked(context);
files.erase(std::remove(files.begin(), files.end(), file_name), files.end());
@@ -1180,7 +1084,6 @@ DValue unit_info(String path)
DValue info;
if(!context)
return(info);
-
String resolved_path = compiler_resolve_unit_path(context, path);
if(resolved_path == "")
return(info);
@@ -1188,9 +1091,7 @@ DValue unit_info(String path)
SharedUnit* su = 0;
auto it = context->server->units.find(resolved_path);
if(it != context->server->units.end())
- {
su = it->second;
- }
else
{
auto known_units = compiler_list_known_units(context);
@@ -1263,7 +1164,7 @@ DValue unit_info(String path)
info["metadata_build_token"] = fs_state.metadata_build_token;
compiler_tree_set_bool(info, "known", compiler_has_known_unit_cached(context, resolved_path));
compiler_tree_set_bool(info, "current_unit", resolved_path == compiler_current_unit_path(context));
- compiler_tree_set_bool(info, "loaded", su->so_handle != 0);
+ compiler_tree_set_bool(info, "loaded", file_exists(su->wasm_name));
compiler_tree_set_bool(info, "source_exists", fs_state.source_exists);
compiler_tree_set_bool(info, "compiled_exists", fs_state.compiled_time != 0);
compiler_tree_set_bool(info, "metadata_exists", fs_state.metadata_exists);
@@ -1273,15 +1174,13 @@ DValue unit_info(String path)
compiler_tree_set_bool(info, "stale", shared_unit_compile_check(fs_state).needs_compile && !compiler_failure_retry_deferred(context, su, fs_state));
compiler_tree_set_bool(info, "retry_deferred", compiler_failure_retry_deferred(context, su, fs_state));
compiler_tree_set_bool(info, "cache_stale", shared_unit_cache_is_stale(context, su));
- compiler_tree_set_bool(info, "has_render", su->on_render != 0);
- compiler_tree_set_bool(info, "has_component", su->on_component != 0);
- compiler_tree_set_bool(info, "has_websocket", su->on_websocket != 0);
- compiler_tree_set_bool(info, "has_setup", su->on_setup != 0);
+ compiler_tree_set_bool(info, "has_render", false);
+ compiler_tree_set_bool(info, "has_component", false);
+ compiler_tree_set_bool(info, "has_websocket", false);
+ compiler_tree_set_bool(info, "has_setup", false);
compiler_tree_set_bool(info, "has_error", trim(compiler_error_status(su)) != "");
-
compiler_tree_push_strings(info["exports"], exports);
info["exports_text"] = exports_text;
-
return(info);
}
@@ -1289,7 +1188,6 @@ StringList units_list()
{
if(!context)
return(StringList());
-
auto known_units = compiler_list_known_units(context);
for(auto& it : context->server->units)
known_units.push_back(it.first);
@@ -1300,870 +1198,10 @@ bool unit_compile(String path)
{
if(!context)
return(false);
-
String resolved_path = compiler_resolve_unit_path(context, path);
if(resolved_path == "")
return(false);
-
compiler_track_known_unit(context, resolved_path);
auto su = compiler_get_shared_unit_internal(context, resolved_path, false, true);
- return(su && trim(su->compiler_messages) == "" && su->so_handle != 0);
-}
-
-namespace {
-
-struct UnitInvocationScope
-{
- Request* context = 0;
- String previous_working_directory;
- String previous_unit_file;
-
- UnitInvocationScope(Request* context, SharedUnit* su)
- {
- this->context = context;
- previous_working_directory = cwd_get();
- previous_unit_file = context->resources.current_unit_file;
- cwd_set(su->src_path);
- context->resources.current_unit_file = su->file_name;
- }
-
- ~UnitInvocationScope()
- {
- if(!context)
- return;
- context->resources.current_unit_file = previous_unit_file;
- cwd_set(previous_working_directory);
- }
-};
-
-enum class UnitCallMacroKind
-{
- none,
- render,
- component,
- once,
- init,
- cli,
- serve_http
-};
-
-struct UnitCallMacroTarget
-{
- UnitCallMacroKind kind = UnitCallMacroKind::none;
- String handler_name;
-};
-
-struct RequestPropsScope
-{
- Request* context = 0;
- DValue previous_props;
-
- RequestPropsScope(Request* context, const DValue& 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_cli = 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);
- }
- if(function_name == "CLI")
- {
- target.kind = UnitCallMacroKind::cli;
- return(target);
- }
- if(function_name == "SERVE_HTTP")
- {
- target.kind = UnitCallMacroKind::serve_http;
- return(target);
- }
- if(function_name.rfind("SERVE_HTTP:", 0) == 0)
- {
- target.kind = UnitCallMacroKind::serve_http;
- target.handler_name = trim(function_name.substr(11));
- return(target);
- }
-
- return(target);
-}
-
-}
-
-String component_normalize_path(String name)
-{
- name = trim(name);
- if(name.length() >= 4 && name.substr(name.length() - 4) == ".uce")
- return(name);
- return(name + ".uce");
-}
-
-void component_parse_target(String target, String& file_name, String& render_name)
-{
- target = trim(target);
- render_name = "";
- auto render_split_pos = target.find(":");
- if(render_split_pos != String::npos)
- {
- render_name = trim(target.substr(render_split_pos + 1));
- target = trim(target.substr(0, render_split_pos));
- }
- file_name = target;
-}
-
-String component_resolve_path(String name, Request* request_context = 0)
-{
- String target_name = trim(name);
- String file_name;
- String render_name;
- component_parse_target(target_name, file_name, render_name);
-
- if(file_name == "")
- {
- if(target_name.rfind(":", 0) == 0 && request_context && request_context->resources.current_unit_file != "")
- file_name = request_context->resources.current_unit_file;
- else
- return("");
- }
-
- StringList candidates;
- auto push_candidate = [&] (String candidate) {
- if(candidate == "")
- return;
- candidates.push_back(candidate);
- };
-
- push_candidate(file_name);
- push_candidate(component_normalize_path(file_name));
-
- if(file_name.rfind("components/", 0) != 0)
- {
- push_candidate("components/" + file_name);
- push_candidate(component_normalize_path("components/" + file_name));
- }
-
- std::map seen;
- for(auto& candidate : candidates)
- {
- if(seen[candidate])
- continue;
- seen[candidate] = true;
- String resolved = candidate;
- if(resolved[0] != '/')
- resolved = expand_path(resolved, cwd_get());
- if(file_exists(resolved))
- return(resolved);
- }
-
- return("");
-}
-
-String page_render_handler_symbol(String render_name)
-{
- render_name = trim(render_name);
- if(render_name == "" || render_name == "render")
- return(UCE_RENDER_SYMBOL);
- return(String(UCE_RENDER_SYMBOL) + "_" + safe_name(render_name));
-}
-
-String component_handler_symbol(String render_name)
-{
- render_name = trim(render_name);
- if(render_name == "")
- return(UCE_COMPONENT_SYMBOL);
- return(String(UCE_COMPONENT_SYMBOL) + "_" + safe_name(render_name));
-}
-
-String serve_http_handler_symbol(String handler_name)
-{
- handler_name = trim(handler_name);
- if(handler_name == "")
- return(UCE_SERVE_HTTP_SYMBOL);
- return(String(UCE_SERVE_HTTP_SYMBOL) + "_" + safe_name(handler_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::cli)
- return("no CLI() entry point");
- if(kind == UnitCallMacroKind::serve_http)
- {
- if(handler_name == "")
- return("no SERVE_HTTP() entry point");
- return("no SERVE_HTTP:" + handler_name + "() 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_request_handler_symbol(SharedUnit* su, String symbol, request_ref_handler default_handler = 0)
-{
- if(default_handler && symbol == "")
- return(default_handler);
-
- auto it = su->api_functions.find(symbol);
- if(it != su->api_functions.end())
- return((request_ref_handler)it->second);
-
- auto handler = (request_ref_handler)dlsym(su->so_handle, symbol.c_str());
- dlerror();
- su->api_functions[symbol] = (void*)handler;
- return(handler);
-}
-
-request_ref_handler get_page_render_handler(SharedUnit* su, String render_name)
-{
- String symbol = page_render_handler_symbol(render_name);
- if(symbol == UCE_RENDER_SYMBOL)
- return(su->on_render);
- return(get_request_handler_symbol(su, symbol));
-}
-
-request_ref_handler get_component_handler(SharedUnit* su, String render_name)
-{
- String symbol = component_handler_symbol(render_name);
- if(symbol == UCE_COMPONENT_SYMBOL)
- return(su->on_component);
- return(get_request_handler_symbol(su, symbol));
-}
-
-request_ref_handler get_serve_http_handler(SharedUnit* su, String handler_name)
-{
- return(get_request_handler_symbol(su, serve_http_handler_symbol(handler_name)));
-}
-
-bool compiler_invoke_loaded_request_handler(
- Request* context,
- SharedUnit* su,
- request_ref_handler handler,
- UnitCallMacroKind kind,
- String handler_name,
- bool count_request,
- String runtime_error_status,
- String* error_out = 0
-)
-{
- if(!compiler_prepare_request_handler(context, su, error_out, true))
- return(false);
-
- if(!handler)
- {
- if(error_out)
- *error_out = compiler_missing_request_handler_message(kind, handler_name);
- return(false);
- }
-
- compiler_execute_request_handler(
- context,
- su,
- handler,
- count_request,
- runtime_error_status
- );
- return(true);
-}
-
-bool compiler_invoke_render(Request* context, String file_name, String render_name, String* error_out = 0)
-{
- auto su = compiler_load_shared_unit(context, file_name, "", false);
- if(!su)
- return(false);
-
- return(compiler_invoke_loaded_request_handler(
- context,
- su,
- get_page_render_handler(su, render_name),
- UnitCallMacroKind::render,
- render_name,
- compiler_is_request_entry_unit(context, su),
- "uncaught exception during render",
- error_out
- ));
-}
-
-bool compiler_invoke_component(Request* context, String file_name, String render_name, String* error_out = 0)
-{
- auto su = compiler_load_shared_unit(context, file_name, "", false);
- if(!su)
- return(false);
-
- return(compiler_invoke_loaded_request_handler(
- context,
- su,
- get_component_handler(su, render_name),
- UnitCallMacroKind::component,
- render_name,
- false,
- "uncaught exception during component render",
- error_out
- ));
-}
-
-String compiler_error_page_unit(Request* context, String config_key)
-{
- if(!context || !context->server)
- return("");
- String configured = trim(first(
- context->server->config[config_key],
- context->server->config[to_upper(config_key)]
- ));
- if(configured == "")
- return("");
- // Resolve relative to where the server was started, not the volatile
- // per-unit working directory (which is also stale right after a fault).
- String resolved = compiler_resolve_unit_path(context, configured, process_start_directory());
- if(resolved == "" || !file_exists(resolved))
- return("");
- return(resolved);
-}
-
-bool compiler_render_error_page(Request* context, String config_key, s32 status_code, String status_reason, DValue error_info)
-{
- if(!compiler_can_write_response(context))
- return(false);
- if(!context->server || context->resources.error_page_active)
- return(false);
- String unit_file = compiler_error_page_unit(context, config_key);
- if(unit_file == "")
- return(false);
-
- String previous_response_code = context->response_code;
- s32 previous_status = context->flags.status;
- String previous_content_type = context->header["Content-Type"];
-
- context->resources.error_page_active = true;
- context->call["error"] = error_info;
- context->set_status(status_code, status_reason);
- context->header["Content-Type"] = first(context->server->config["CONTENT_TYPE"], "text/html; charset=utf-8");
-
- ob_start();
- String error_message = "";
- bool rendered = compiler_invoke_render(context, unit_file, "render", &error_message);
- String html = ob_get_close();
- context->resources.error_page_active = false;
-
- if(!rendered)
- {
- printf("(!) configured %s page %s failed to render: %s\n", config_key.c_str(), unit_file.c_str(), trim(error_message).c_str());
- context->response_code = previous_response_code;
- context->flags.status = previous_status;
- context->header["Content-Type"] = previous_content_type;
- context->call.remove("error");
- return(false);
- }
- print(html);
- return(true);
-}
-
-bool compiler_unit_compile_pending(Request* context, String file_name)
-{
- String normalized = compiler_normalize_unit_path(context, file_name);
- if(normalized == "" || !file_exists(normalized))
- return(false);
- if(compiler_reusable_cached_unit(context, normalized, false, false))
- return(false);
-
- SharedUnit probe;
- setup_unit_paths(context, &probe, normalized);
- auto state = inspect_shared_unit_filesystem(context, &probe);
- if(!shared_unit_compile_check(state).needs_compile)
- return(false);
- // A persisted, still-current failure is a compiler error, not a build in
- // progress: let the load path surface it.
- if(compiler_failure_retry_deferred(context, &probe, state))
- return(false);
- return(true);
-}
-
-void compiler_invoke(Request* context, String file_name)
-{
- printf("(i) compiler_invoke file %s\n", file_name.c_str());
-
- // Entry-unit hook for the configured "compiling" page: when the unit needs
- // a (re)build that the proactive compiler can deliver, answer immediately
- // instead of blocking the worker on a synchronous compile.
- if(context && context->stats.invoke_count == 0 &&
- !context->resources.is_cli && !context->resources.error_page_active &&
- config_bool("PROACTIVE_COMPILE_ENABLED", true) &&
- compiler_error_page_unit(context, "page_compiling") != "")
- {
- String resolved = compiler_resolve_unit_path(context, file_name, "");
- if(resolved != "" && compiler_unit_compile_pending(context, resolved))
- {
- compiler_track_known_unit(context, resolved);
- DValue error_info;
- error_info["type"] = "compiling";
- error_info["source"] = resolved;
- if(compiler_render_error_page(context, "page_compiling", 503, "UCE Unit Compiling", error_info))
- return;
- }
- }
-
- String error_message = "";
- if(!compiler_invoke_render(context, file_name, "render", &error_message) && error_message != "")
- {
- if(context->stats.invoke_count == 1)
- context->header["Content-Type"] = "text/plain";
- print(error_message);
- }
-}
-
-void compiler_invoke_cli(Request* context, String file_name)
-{
- auto su = compiler_load_shared_unit(context, file_name, "", false);
- if(!su)
- return;
-
- String error_message = "";
- if(!compiler_invoke_loaded_request_handler(
- context,
- su,
- su->on_cli,
- UnitCallMacroKind::cli,
- "",
- compiler_is_request_entry_unit(context, su),
- "uncaught exception during cli handler",
- &error_message
- ))
- {
- if(!su->on_cli)
- context->set_status(404, "CLI Entry Point Not Found");
- else
- context->set_status(500, "CLI Unit Error");
- if(error_message != "")
- print(error_message);
- }
-}
-
-void compiler_invoke_serve_http(Request* context, String file_name, String handler_name)
-{
- auto su = compiler_load_shared_unit(context, file_name, "", false);
- if(!su)
- return;
-
- String error_message = "";
- if(!compiler_invoke_loaded_request_handler(
- context,
- su,
- get_serve_http_handler(su, handler_name),
- UnitCallMacroKind::serve_http,
- handler_name,
- true,
- "uncaught exception during HTTP server handler",
- &error_message
- ))
- {
- context->set_status(500, "HTTP Server Handler Error");
- if(error_message != "")
- print(error_message);
- }
-}
-
-void compiler_invoke_websocket(Request* context, String file_name)
-{
- auto su = compiler_load_shared_unit(context, file_name, "", false);
- if(!su)
- return;
-
- if(!su->on_setup)
- {
- printf("internal error: %s() not defined in %s\n", UCE_SETUP_SYMBOL, file_name.c_str());
- return;
- }
-
- if(!su->on_websocket)
- {
- printf("no WS() entry point in %s\n", file_name.c_str());
- return;
- }
-
- 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)
-{
- //printf("(i) unit_render(%s)\n", file_name.c_str());
- compiler_invoke(context, file_name);
-}
-
-void unit_render(String file_name, Request& context)
-{
- compiler_invoke(&context, file_name);
-}
-
-String component_resolve(String name)
-{
- return(component_resolve_path(name, context));
-}
-
-bool component_exists(String name)
-{
- return(component_resolve(name) != "");
-}
-
-String component_error_banner(String message)
-{
- return("" + html_escape(message) + "
");
-}
-
-void component_render(String name)
-{
- DValue props;
- component_render(name, props, *context);
-}
-
-void component_render(String name, Request& context)
-{
- DValue props;
- component_render(name, props, context);
-}
-
-void component_render(String name, DValue props)
-{
- component_render(name, props, *context);
-}
-
-void component_render(String name, DValue props, Request& context)
-{
- String file_name;
- String render_name;
- component_parse_target(name, file_name, render_name);
-
- String resolved_name = component_resolve_path(name, &context);
- if(resolved_name == "")
- {
- print(component_error_banner("component not found: " + trim(name)));
- return;
- }
-
- 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));
-}
-
-String component(String name)
-{
- DValue props;
- return(component(name, props, *context));
-}
-
-String component(String name, Request& context)
-{
- DValue props;
- return(component(name, props, context));
-}
-
-String component(String name, DValue props)
-{
- return(component(name, props, *context));
-}
-
-String component(String name, DValue props, Request& context)
-{
- ob_start();
- component_render(name, props, context);
- return(ob_get_close());
-}
-
-SharedUnit* unit_load(String file_name)
-{
- auto su = compiler_load_shared_unit(context, file_name, "", false);
- if(su && su->so_handle)
- {
- return(su);
- }
- else
- {
- return(0);
- }
-}
-
-DValue* unit_call(String file_name, String function_name, DValue* call_param)
-{
- DValue* result = 0;
- auto su = compiler_load_shared_unit(context, file_name, "", false);
- if(su && su->so_handle)
- {
- if(!su->on_setup)
- {
- print("internal error: ", UCE_SETUP_SYMBOL, "() not defined in ", file_name, "\n");
- }
- else
- {
- auto macro_target = unit_call_macro_target(function_name);
- if(macro_target.kind != UnitCallMacroKind::none)
- {
- RequestPropsScope props_scope(context, (call_param ? *call_param : DValue()));
- 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
- {
- 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;
- else if(macro_target.kind == UnitCallMacroKind::cli)
- handler = su->on_cli;
-
- if(!handler)
- print("Error: unit_call() ", compiler_missing_request_handler_message(macro_target.kind, macro_target.handler_name));
- else if(macro_target.kind == UnitCallMacroKind::cli)
- {
- String prepare_error = "";
- if(!compiler_prepare_request_handler(context, su, &prepare_error, true))
- print("Error: unit_call() ", prepare_error);
- else
- compiler_execute_request_handler(context, su, handler, false, "uncaught exception during cli handler");
- }
- else
- {
- UnitInvocationScope invoke_scope(context, su);
- su->on_setup(context);
- handler(*context);
- }
- }
- }
- else
- {
- auto f = (dv_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);
- }
- }
- }
- }
- else
- {
- print("Error: unit_call() could not load unit file '", file_name, "'");
- }
- return(result);
+ return(su && trim(su->compiler_messages) == "" && file_exists(su->wasm_name));
}
diff --git a/src/lib/compiler.h b/src/lib/compiler.h
index cc4721d..b72dbcc 100644
--- a/src/lib/compiler.h
+++ b/src/lib/compiler.h
@@ -13,16 +13,9 @@ String preprocess_shared_unit(Request* context, SharedUnit* su);
String compiler_generated_cpp_path(Request* context, String source_file);
String compiler_generated_cpp_path(SharedUnit* su);
void setup_unit_paths(Request* context, SharedUnit* su, String file_name);
-void load_shared_unit(Request* context, SharedUnit* su);
void compile_shared_unit(Request* context, SharedUnit* su);
SharedUnit* get_shared_unit(Request* context, String file_name, bool opt_so_optional = false);
-void compiler_invoke(Request* context, String file_name);
-void compiler_invoke_websocket(Request* context, String file_name);
-void compiler_invoke_cli(Request* context, String file_name);
-void compiler_invoke_serve_http(Request* context, String file_name, String handler_name = "");
-SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String current_path = "", bool opt_so_optional = false);
String compiler_error_page_unit(Request* context, String config_key);
-bool compiler_render_error_page(Request* context, String config_key, s32 status_code, String status_reason, DValue error_info);
bool compiler_unit_compile_pending(Request* context, String file_name);
String compiler_site_directory(Request* context);
StringList compiler_scan_site_units(Request* context);
diff --git a/src/lib/sys.cpp b/src/lib/sys.cpp
index 60ea731..8ac6e3e 100644
--- a/src/lib/sys.cpp
+++ b/src/lib/sys.cpp
@@ -1314,10 +1314,7 @@ StringMap make_server_settings()
StringMap cfg;
cfg["BIN_DIRECTORY"] = "/tmp/uce/work";
- cfg["COMPILE_SCRIPT"] = "scripts/compile";
cfg["WASM_COMPILE_SCRIPT"] = "scripts/compile_wasm_unit";
- cfg["COMPILE_WASM_UNITS"] = "0";
- cfg["WASM_BACKEND_ENABLED"] = "1";
cfg["WASM_BACKEND_VERBOSE"] = "0";
cfg["WASM_CORE_PATH"] = "";
cfg["WASM_MEMORY_LIMIT_BYTES"] = std::to_string(512ull * 1024 * 1024);
diff --git a/src/lib/types.cpp b/src/lib/types.cpp
index a39a374..f820763 100644
--- a/src/lib/types.cpp
+++ b/src/lib/types.cpp
@@ -79,12 +79,6 @@ String http_status_reason(s32 code)
SharedUnit::~SharedUnit()
{
-#ifndef __UCE_WASM_CORE__
- if(so_handle)
- {
- dlclose(so_handle);
- }
-#endif
}
String nibble(String div, String& haystack)
diff --git a/src/lib/types.h b/src/lib/types.h
index 68d239c..6a03405 100644
--- a/src/lib/types.h
+++ b/src/lib/types.h
@@ -74,7 +74,6 @@ struct SharedUnit {
String compile_output_file_name;
String setup_file_name;
StringList api_declarations;
- std::map api_functions;
String src_path;
String bin_path;
@@ -84,16 +83,6 @@ struct SharedUnit {
String wasm_file_name;
String pre_file_name;
- void* so_handle = 0;
-
- request_handler on_setup = 0;
- request_ref_handler on_render = 0;
- request_ref_handler on_component = 0;
- request_ref_handler on_websocket = 0;
- request_ref_handler on_cli = 0;
- request_ref_handler on_once = 0;
- request_ref_handler on_init = 0;
-
String compiler_messages;
String compile_status = "unknown";
String compile_error_status = "";
@@ -153,8 +142,6 @@ String nibble(String div, String& haystack);
#include "dvalue.h"
-void compiler_invoke(Request* context, String file_name);
-
struct Request {
ServerState* server = 0;
diff --git a/src/linux_fastcgi.cpp b/src/linux_fastcgi.cpp
index cac1415..cf14321 100644
--- a/src/linux_fastcgi.cpp
+++ b/src/linux_fastcgi.cpp
@@ -65,6 +65,51 @@ void clear_request_output(Request& request)
request.ob_start();
}
+bool render_wasm_error_page(Request& request, String config_key, s32 status_code, String status_reason, DValue error_info)
+{
+ if(!(request.params["REQUEST_METHOD"] != "" && request.ob && request.ob_stack.size() > 0))
+ return(false);
+ if(!request.server || request.resources.error_page_active)
+ return(false);
+ String unit_file = compiler_error_page_unit(&request, config_key);
+ if(unit_file == "")
+ return(false);
+
+ String previous_response_code = request.response_code;
+ s32 previous_status = request.flags.status;
+ String previous_content_type = request.header["Content-Type"];
+
+ request.resources.error_page_active = true;
+ request.call["error"] = error_info;
+ request.set_status(status_code, status_reason);
+ request.header["Content-Type"] = first(request.server->config["CONTENT_TYPE"], "text/html; charset=utf-8");
+
+ String unit = compiler_normalize_unit_path(&request, unit_file);
+ if(!wasm_backend_should_handle(request, unit))
+ get_shared_unit(&request, unit, true);
+
+ ob_start();
+ String wasm_error = "";
+ if(wasm_backend_should_handle(request, unit))
+ wasm_error = wasm_backend_serve(request, unit, "render");
+ else
+ wasm_error = "error page wasm unit unavailable after compile: " + unit_file;
+ String html = ob_get_close();
+ request.resources.error_page_active = false;
+
+ if(wasm_error != "")
+ {
+ printf("(!) configured %s page %s failed to render: %s\n", config_key.c_str(), unit_file.c_str(), trim(wasm_error).c_str());
+ request.response_code = previous_response_code;
+ request.flags.status = previous_status;
+ request.header["Content-Type"] = previous_content_type;
+ request.call.remove("error");
+ return(false);
+ }
+ print(html);
+ return(true);
+}
+
void render_request_failure(Request& request, String title, String details, String trace, int status_code = 500)
{
request.response_code = request_status_line(request, status_code, "Internal Server Error");
@@ -92,7 +137,7 @@ void render_request_failure(Request& request, String title, String details, Stri
error_info["signal_name"] = signal_name((int)request_fault_signal);
}
error_info["trace"] = trace;
- if(compiler_render_error_page(&request, "page_runtime_error", status_code, "Internal Server Error", error_info))
+ if(render_wasm_error_page(request, "page_runtime_error", status_code, "Internal Server Error", error_info))
{
request.err = "UCE runtime error: " + title + (details != "" ? " (" + details + ")" : "");
restore_active_request(previous_context);
@@ -368,10 +413,9 @@ int handle_cli_complete(FastCGIRequest& request)
// The CLI socket path installs no native fault handler, so the
// wasm worker (whose traps are signal-based) can run directly.
String cli_unit = compiler_normalize_unit_path(&request, script_filename);
- // W7e: compile a cold/stale unit on demand instead of executing
- // native. force_recompile: the compile-needed check ignores the
- // .wasm (keys off .so mtime), so a missing/stale .wasm with a
- // current .so must be forced to rebuild — see wasm_ready note.
+ // W7e: compile a cold/stale unit on demand; native execution has
+ // been removed, so a unit that still cannot be served by wasm is a
+ // request failure instead of a fallback path.
if(!wasm_backend_should_handle(request, cli_unit))
get_shared_unit(&request, cli_unit, true);
if(wasm_backend_should_handle(request, cli_unit))
@@ -384,7 +428,10 @@ int handle_cli_complete(FastCGIRequest& request)
}
}
else
- compiler_invoke_cli(&request, script_filename);
+ {
+ request.set_status(500, "Internal Server Error");
+ print("UCE CLI wasm unit unavailable after compile: ", script_filename, "\n");
+ }
}
}
}
@@ -484,27 +531,21 @@ int handle_complete(FastCGIRequest& request) {
String entry_unit = compiler_normalize_unit_path(&request, request.params["SCRIPT_FILENAME"]);
// W7e: every unit runs on wasm. When the artifact is missing or stale
- // (cold worker, or source edited since the last compile),
- // wasm_backend_should_handle() returns false; rather than fall back to
- // native execution we compile the unit on demand — get_shared_unit()
- // builds the .wasm side-module — and recheck. Native execution remains
- // only as a safety net for a genuine wasm-compile failure (or the wasm
- // backend being disabled), both of which leave should_handle() false.
- // Only reached on the cold/stale path (should_handle false); warm
- // requests short-circuit before this, so there is no steady-state
- // recompile cost. force_recompile is needed because the compile-needed
- // check keys off the .so mtime only (shared_unit_compile_check /
- // compiled_time ignores the .wasm), so an uncached unit with a current
- // .so but missing .wasm would otherwise not rebuild. NOTE: this does not
- // cover a unit already cached in-process whose .wasm later vanished
- // (compiler_reusable_cached_unit short-circuits before the recompile) —
- // that synthetic case still uses native fallback and is closed properly
- // in W7e stage B (make the .wasm part of the compile-freshness check).
+ // (cold worker, or source edited since the last compile), compile the
+ // unit on demand — get_shared_unit() builds the .wasm side-module — and
+ // recheck. Native execution has been removed, so a unit that still cannot
+ // be served by wasm becomes a clean 500 request failure.
auto wasm_ready = [&](const String& unit) -> bool {
if(!wasm_backend_should_handle(request, unit))
get_shared_unit(&request, unit, true);
return(wasm_backend_should_handle(request, unit));
};
+ auto fail_wasm_unavailable = [&](const String& handler) {
+ failure_title = "wasm unit unavailable after compile";
+ failure_details = handler + " handler could not be served by wasm";
+ failure_trace = "source: " + request.params["SCRIPT_FILENAME"];
+ };
+
if(request.params["UCE_WS"] == "1")
{
// A WS message the broker forwarded here: rebuild the connection
@@ -529,14 +570,14 @@ int handle_complete(FastCGIRequest& request) {
if(wasm_ready(entry_unit))
serve_via_wasm(entry_unit, "websocket");
else
- compiler_invoke_websocket(&request, request.params["SCRIPT_FILENAME"]);
+ fail_wasm_unavailable("websocket");
}
else if(request.resources.is_cli)
{
if(wasm_ready(entry_unit))
serve_via_wasm(entry_unit, "cli");
else
- compiler_invoke_cli(&request, request.params["SCRIPT_FILENAME"]);
+ fail_wasm_unavailable("cli");
}
else if(request.params["UCE_SERVE_HTTP"] == "1")
{
@@ -549,12 +590,12 @@ int handle_complete(FastCGIRequest& request) {
serve_via_wasm(entry_unit, fn == "" ? String("serve_http") : "serve_http:" + fn);
}
else
- compiler_invoke_serve_http(&request, request.params["SCRIPT_FILENAME"], request.params["UCE_SERVE_HTTP_FUNCTION"]);
+ fail_wasm_unavailable("serve_http");
}
else if(wasm_ready(entry_unit))
serve_via_wasm(entry_unit, "render");
else
- compiler_invoke(&request, request.params["SCRIPT_FILENAME"]);
+ fail_wasm_unavailable("render");
}
catch(const std::exception& e)
{
@@ -1270,9 +1311,6 @@ void init_base_process()
server_state.config["COMPILER_SYS_PATH"] = cwd_get();
printf("Compiler base path: %s\n", server_state.config["COMPILER_SYS_PATH"].c_str());
- server_state.config["COMPILE_SCRIPT"] =
- server_state.config["COMPILER_SYS_PATH"] + "/" + server_state.config["COMPILE_SCRIPT"];
-
if(server_state.config["FCGI_PORT"] != "")
server.listen(int_val(server_state.config["FCGI_PORT"]));
diff --git a/src/wasm/backend.cpp b/src/wasm/backend.cpp
index 2d63df0..f9729a8 100644
--- a/src/wasm/backend.cpp
+++ b/src/wasm/backend.cpp
@@ -2,12 +2,8 @@
//
// Included into the native server TU (src/linux_fastcgi.cpp) after uce_lib.cpp,
// so it shares String/DValue/config and the UCEB1 codec. Provides a
-// config-selectable page-render backend: when WASM_BACKEND_ENABLED and a wasm
-// artifact exists for the entry unit, the request is served through a
-// per-request wasm workspace instead of the native dlopen path.
-//
-// The seam is narrow on purpose — only the page render branch in
-// handle_complete() changes. CLI, serve_http, and websocket stay native.
+// Always-on wasm backend: every unit request is served through a per-request
+// wasm workspace. The legacy native dlopen execution path has been removed.
#include "../lib/wasm_trace.h"
// The native server TU has the real connectors (sqlite/mysql) compiled in, so
@@ -31,9 +27,7 @@ static bool g_wasm_init_attempted = false;
bool wasm_backend_configured(Request* context)
{
- if(!context || !context->server)
- return(false);
- return(config_bool("WASM_BACKEND_ENABLED", false));
+ return(context && context->server);
}
// Lazily bring up the per-process worker on first use inside a forked child
@@ -90,22 +84,19 @@ static bool wasm_artifact_exists(Request* context, const String& entry_unit)
struct stat wasm_st;
if(stat(wasm_path.c_str(), &wasm_st) != 0 || !S_ISREG(wasm_st.st_mode))
return(false);
- // The wasm path bypasses the native JIT recompile, so a source edit would
- // otherwise be served from a stale .wasm. Require the artifact to be newer
- // than the source; if it is stale, fall back to native — which JIT-rebuilds
- // both .so and .wasm — so the next request gets a fresh artifact.
+ // Require the artifact to be newer than the source. If it is stale,
+ // dispatch compiles the unit on demand and then rechecks this predicate.
struct stat src_st;
- if(stat(entry_unit.c_str(), &src_st) == 0 && wasm_st.st_mtime < src_st.st_mtime)
+ if(stat(entry_unit.c_str(), &src_st) != 0 || !S_ISREG(src_st.st_mode))
+ return(false);
+ if(wasm_st.st_mtime < src_st.st_mtime)
return(false);
return(true);
}
-// True if this request should be served by the wasm backend. Every unit now
-// runs on wasm (W7d retired the last native-only surfaces), so this only gates
-// on config, the presence of a current wasm artifact, and a healthy worker —
-// the remaining native fallback exists solely to cover a cold/stale artifact
-// (a unit not yet compiled, or whose source is newer than its .wasm). W7e
-// removes that last fallback once cold/stale triggers an on-demand wasm compile.
+// True if this request can be served by the wasm backend now. Every unit runs
+// on wasm; cold/stale artifacts return false so dispatch can compile on demand
+// and then recheck.
bool wasm_backend_should_handle(Request& request, const String& entry_unit)
{
if(!wasm_backend_configured(&request))
@@ -181,9 +172,8 @@ String wasm_backend_serve(Request& request, const String& entry_unit, const Stri
fcgi_forward_request(broker_socket, dispatch_params, ucb_encode(batch), 5);
}
- // A cli/serve_http unit that does not export the requested handler is a 404,
- // matching native compiler_invoke_cli. For page render a missing handler is
- // simply an empty body (native parity).
+ // A cli/serve_http unit that does not export the requested handler is a 404.
+ // For page render a missing handler is simply an empty body.
if(!response.handler_present && handler != "render")
{
request.set_status(404, handler == "cli" ? "CLI Entry Point Not Found" : "Handler Not Found");
diff --git a/src/wasm/core.cpp b/src/wasm/core.cpp
index 488b5bd..d7d99b8 100644
--- a/src/wasm/core.cpp
+++ b/src/wasm/core.cpp
@@ -139,32 +139,6 @@ bool unit_compile(String path)
}
static DValue wasm_unit_call_result;
-DValue* unit_call(String file_name, String function_name, DValue* call_param)
-{
- DValue request;
- request["op"] = "call";
- request["file"] = file_name;
- request["function"] = function_name;
- if(call_param)
- request["param"] = *call_param;
- DValue response = wasm_units_call(request);
- String output = response["output"].to_string();
- if(output != "")
- print(output);
- DValue* result = response.key("result");
- if(result)
- {
- wasm_unit_call_result = *result;
- return(&wasm_unit_call_result);
- }
- return(0);
-}
-
-SharedUnit* compiler_load_shared_unit(Request* context, String file_name, String current_path, bool opt_so_optional)
-{
- (void)context; (void)file_name; (void)current_path; (void)opt_so_optional;
- return(0);
-}
static DValue wasm_mysql_call(DValue request)
{
@@ -484,9 +458,9 @@ extern "C" int32_t uce_host_component_resolve(
// but a single workspace can render the same component many times)
static std::map wasm_component_slots;
-// These mirror small page-runtime pieces of compiler.cpp, which is carved
-// out of the wasm core wholesale (it is the native toolchain: parser, clang
-// driver, dlopen). Kept byte-identical where possible.
+// 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
+// build). Kept byte-identical where possible.
String component_normalize_path(String name)
{
name = trim(name);
@@ -595,6 +569,62 @@ static void wasm_run_once(const String& resolved, Request& request)
request.resources.current_unit_file = previous_unit;
}
+DValue* unit_call(String file_name, String function_name, DValue* call_param)
+{
+ String macro = to_upper(trim(function_name));
+ String handler = "";
+ if(macro == "RENDER" || macro.rfind("RENDER:", 0) == 0)
+ handler = "render" + (macro.length() > 7 ? ":" + trim(function_name.substr(function_name.find(":") + 1)) : String(""));
+ else if(macro == "COMPONENT" || macro.rfind("COMPONENT:", 0) == 0)
+ handler = "component" + (macro.length() > 10 ? ":" + trim(function_name.substr(function_name.find(":") + 1)) : String(""));
+ else if(macro == "ONCE")
+ handler = "once";
+ else if(macro == "INIT")
+ handler = "init";
+
+ if(handler != "")
+ {
+ String resolved;
+ s32 slot = wasm_resolve_target(file_name, handler, &resolved);
+ if(!slot)
+ {
+ print("Error: unit_call() function '", function_name, "' not found");
+ return(0);
+ }
+ RequestPropsScope props_scope(context, (call_param ? *call_param : DValue()));
+ if((handler == "render" || handler.rfind("render:", 0) == 0 || handler == "component" || handler.rfind("component:", 0) == 0) && resolved != "")
+ wasm_run_once(resolved, *context);
+ String previous_unit = context->resources.current_unit_file;
+ if(resolved != "")
+ context->resources.current_unit_file = resolved;
+ request_ref_handler handler_fn = (request_ref_handler)(uintptr_t)slot;
+ handler_fn(*context);
+ context->resources.current_unit_file = previous_unit;
+ return(0);
+ }
+
+ String resolved;
+ s32 slot = wasm_resolve_target(file_name, "export:" + function_name, &resolved);
+ if(!slot)
+ {
+ print("Error: unit_call() function '", function_name, "' not found");
+ return(0);
+ }
+
+ String previous_unit = context->resources.current_unit_file;
+ if(resolved != "")
+ context->resources.current_unit_file = resolved;
+ dv_call_handler handler_fn = (dv_call_handler)(uintptr_t)slot;
+ DValue* result = handler_fn(call_param);
+ context->resources.current_unit_file = previous_unit;
+ if(result)
+ {
+ wasm_unit_call_result = *result;
+ return(&wasm_unit_call_result);
+ }
+ return(0);
+}
+
void component_render(String name, DValue props, Request& request)
{
String file_name, render_name;
diff --git a/src/wasm/worker.cpp b/src/wasm/worker.cpp
index dfb4b92..46f564f 100644
--- a/src/wasm/worker.cpp
+++ b/src/wasm/worker.cpp
@@ -1046,6 +1046,9 @@ private:
// __uce_[_] for a handler spec like "component:CARD" / "render".
static String handler_export_symbol(const String& handler)
{
+ String export_prefix = "export:";
+ if(handler.rfind(export_prefix, 0) == 0)
+ return(handler.substr(export_prefix.size()));
auto colon = handler.find(":");
String symbol = "__uce_" + (colon == String::npos ? handler : handler.substr(0, colon));
if(colon != String::npos)
@@ -1086,6 +1089,9 @@ private:
return(1);
}
+ if(!file_exists_host(worker.unit_wasm_path(resolved)) || compiler_unit_needs_recompile(context, resolved, 0))
+ get_shared_unit(context, resolved, true);
+
size_t unit_index = 0;
String error = load_unit(resolved, unit_index);
if(error != "")
@@ -1791,7 +1797,7 @@ private:
return(std::monostate());
}));
if(mod == "env" && name == "uce_host_component_resolve")
- return(add([self](Caller, Span args, Span results) -> Result {
+ return(add([self](Caller caller, Span args, Span results) -> Result {
String target, handler, current, resolved;
self->hostcall_read(args[0].i32(), args[1].i32(), target);
self->hostcall_read(args[2].i32(), args[3].i32(), handler);
@@ -1806,6 +1812,7 @@ private:
self->hostcall_write(args[6].i32(), resolved);
}
results[0] = Val(slot);
+ caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks);
return(std::monostate());
}));