feat: cut over to WASM backend
This commit is contained in:
+62
-7
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <wasmtime.hh>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
@@ -80,10 +81,15 @@ struct WasmResponse
|
||||
String body;
|
||||
DValue meta; // status / headers / cookies / session
|
||||
String error; // collapsed trace or loader error when !ok
|
||||
u64 workspace_birth_us = 0;
|
||||
u64 component_resolve_count = 0;
|
||||
u64 component_resolve_total_us = 0;
|
||||
};
|
||||
|
||||
// ---- module byte parsing (hardened; carried from the phase 3 spike) -------
|
||||
|
||||
// included into both w3_driver.cpp and the native server TU (via backend.cpp);
|
||||
// file-scope helpers are static so each TU gets its own copy with no clash
|
||||
static bool wasm_read_uleb(const std::vector<u8>& buf, size_t& pos, size_t end, u64& out)
|
||||
{
|
||||
out = 0;
|
||||
@@ -315,13 +321,17 @@ class WasmWorkspace
|
||||
public:
|
||||
WasmWorker& worker;
|
||||
wasmtime::Store store;
|
||||
u64 workspace_birth_us = 0;
|
||||
u64 component_resolve_count = 0;
|
||||
u64 component_resolve_total_us = 0;
|
||||
|
||||
explicit WasmWorkspace(WasmWorker& w) : worker(w), store(w.engine)
|
||||
{
|
||||
}
|
||||
|
||||
// resolve-kind values shared with the guest core (src/wasm/core.cpp)
|
||||
enum ResolveKind { RESOLVE_COMPONENT = 0, RESOLVE_RENDER = 1, RESOLVE_EXISTS = 2 };
|
||||
// must match WasmResolveKind in src/wasm/core.cpp
|
||||
enum ResolveKind { RESOLVE_COMPONENT = 0, RESOLVE_RENDER = 1, RESOLVE_EXISTS = 2, RESOLVE_ONCE = 3 };
|
||||
|
||||
String birth()
|
||||
{
|
||||
@@ -421,10 +431,23 @@ public:
|
||||
String error = load_unit(entry_source_path, unit_index);
|
||||
if(error != "")
|
||||
return(error);
|
||||
auto handler = unit_func(unit_index, "__uce_render");
|
||||
if(!handler)
|
||||
return(entry_source_path + " does not export __uce_render");
|
||||
auto result = handler->call(ctx(), { wasmtime::Val(request_ptr) });
|
||||
// a page with no RENDER block renders empty (native parity), not an error
|
||||
if(!unit_func(unit_index, "__uce_render"))
|
||||
return("");
|
||||
// Render through the core so the entry gets the same ONCE() + dispatch
|
||||
// path as components (unit is pre-loaded above; resolution is cached).
|
||||
auto entry = core_func("uce_wasm_render_entry");
|
||||
if(!entry)
|
||||
return("core does not export uce_wasm_render_entry");
|
||||
int32_t guest_ptr = 0;
|
||||
error = call_core("uce_alloc", { (int32_t)entry_source_path.size() }, &guest_ptr);
|
||||
if(error != "" || guest_ptr == 0)
|
||||
return(error == "" ? String("guest uce_alloc failed for entry path") : error);
|
||||
error = guest_write((u32)guest_ptr, entry_source_path);
|
||||
if(error != "")
|
||||
return(error);
|
||||
auto result = entry->call(ctx(), { wasmtime::Val(guest_ptr), wasmtime::Val((int32_t)entry_source_path.size()) });
|
||||
call_core("uce_free", { guest_ptr }, 0);
|
||||
if(!result)
|
||||
return(trap_text(result.err()));
|
||||
return("");
|
||||
@@ -872,6 +895,12 @@ private:
|
||||
// hostcall body: uce_host_component_resolve(target, kind, current) → slot
|
||||
int32_t component_resolve(const String& target, int32_t kind, const String& current_unit, String& resolved_out)
|
||||
{
|
||||
auto probe_start = std::chrono::steady_clock::now();
|
||||
auto record_probe = [&]() {
|
||||
component_resolve_count += 1;
|
||||
component_resolve_total_us += (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - probe_start).count();
|
||||
};
|
||||
String file_name = target;
|
||||
String render_name;
|
||||
auto split = target.find(":");
|
||||
@@ -883,33 +912,51 @@ private:
|
||||
if(file_name == "" && current_unit != "")
|
||||
file_name = current_unit;
|
||||
if(file_name == "")
|
||||
{
|
||||
record_probe();
|
||||
return(0);
|
||||
}
|
||||
|
||||
String resolved = resolve_source_path(file_name, current_unit);
|
||||
if(resolved == "")
|
||||
{
|
||||
record_probe();
|
||||
return(0);
|
||||
}
|
||||
resolved_out = resolved;
|
||||
if(kind == RESOLVE_EXISTS)
|
||||
{
|
||||
record_probe();
|
||||
return(1);
|
||||
}
|
||||
|
||||
size_t unit_index = 0;
|
||||
String error = load_unit(resolved, unit_index);
|
||||
if(error != "")
|
||||
{
|
||||
fprintf(stderr, "[wasm] component load failed: %s\n", error.c_str());
|
||||
record_probe();
|
||||
return(0);
|
||||
}
|
||||
String symbol = kind == RESOLVE_RENDER ? "__uce_render" : "__uce_component";
|
||||
String symbol = kind == RESOLVE_RENDER ? "__uce_render"
|
||||
: kind == RESOLVE_ONCE ? "__uce_once"
|
||||
: "__uce_component";
|
||||
if(render_name != "")
|
||||
symbol += "_" + sanitize_symbol_suffix(render_name);
|
||||
String slot_key = resolved + ":" + symbol;
|
||||
auto cached = handler_slots.find(slot_key);
|
||||
if(cached != handler_slots.end())
|
||||
{
|
||||
record_probe();
|
||||
return((int32_t)cached->second);
|
||||
}
|
||||
auto handler = unit_func(unit_index, symbol);
|
||||
if(!handler)
|
||||
{
|
||||
fprintf(stderr, "[wasm] %s does not export %s\n", resolved.c_str(), symbol.c_str());
|
||||
// ONCE is optional per unit; a missing __uce_once is not an error
|
||||
if(kind != RESOLVE_ONCE)
|
||||
fprintf(stderr, "[wasm] %s does not export %s\n", resolved.c_str(), symbol.c_str());
|
||||
record_probe();
|
||||
return(0);
|
||||
}
|
||||
u32 slot = 0;
|
||||
@@ -917,9 +964,11 @@ private:
|
||||
if(error != "")
|
||||
{
|
||||
fprintf(stderr, "[wasm] %s\n", error.c_str());
|
||||
record_probe();
|
||||
return(0);
|
||||
}
|
||||
handler_slots[slot_key] = slot;
|
||||
record_probe();
|
||||
return((int32_t)slot);
|
||||
}
|
||||
|
||||
@@ -1056,13 +1105,19 @@ inline WasmResponse wasm_worker_serve(WasmWorker& worker, const DValue& context_
|
||||
{
|
||||
WasmResponse response;
|
||||
WasmWorkspace workspace(worker);
|
||||
auto birth_start = std::chrono::steady_clock::now();
|
||||
String error = workspace.birth();
|
||||
workspace.workspace_birth_us = (u64)std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - birth_start).count();
|
||||
if(error == "")
|
||||
error = workspace.apply_context(context_tree);
|
||||
if(error == "")
|
||||
error = workspace.render_entry(entry_source_path);
|
||||
if(error == "")
|
||||
error = workspace.collect(response);
|
||||
response.workspace_birth_us = workspace.workspace_birth_us;
|
||||
response.component_resolve_count = workspace.component_resolve_count;
|
||||
response.component_resolve_total_us = workspace.component_resolve_total_us;
|
||||
if(error != "")
|
||||
{
|
||||
response.ok = false;
|
||||
|
||||
Reference in New Issue
Block a user