// W3 CLI driver — the exit gate for WASM-PROPOSAL §9.1 W3. // // Serves real requests through the production workspace runtime // (src/wasm/worker.cpp): UCEB1 context in → core + lazily loaded real // generated units → body/response-meta out. Each --repeat gets a fresh // workspace, proving birth/drop. An epoch ticker thread enforces the CPU // budget; the store limiter enforces memory; traps come back as collapsed // wasm_trace summaries. // // Amalgamation TU (project style, like uce_lib.cpp): native types + DValue // codec first, then the worker. // same order as uce_lib.cpp, minus the native compiler/connector block #include "../lib/types.cpp" #include "../lib/dvalue.cpp" #include "../lib/functionlib.cpp" #include "../lib/hash.cpp" #include "../lib/sys.cpp" #include "../lib/uri.cpp" #include "../lib/cli.cpp" #include "../lib/wasm_trace.h" #include "worker.cpp" #include #include static String arg_value(int argc, char** argv, int& i, const char* flag) { if(i + 1 >= argc) { fprintf(stderr, "missing value for %s\n", flag); exit(2); } return(String(argv[++i])); } static String absolute_path(String path) { if(path.rfind("/", 0) == 0) return(path); char buf[4096]; if(!getcwd(buf, sizeof(buf))) return(path); return(String(buf) + "/" + path); } int main(int argc, char** argv) { WasmWorkerConfig cfg; cfg.site_root = absolute_path("site"); String page; String route; StringMap params; StringMap get_params; std::vector expects; String expect_status; int repeat = 1; u64 epoch_period_ms = 50; bool show_body = true; for(int i = 1; i < argc; i++) { String arg = argv[i]; if(arg == "--core") cfg.core_wasm_path = arg_value(argc, argv, i, "--core"); else if(arg == "--site") cfg.site_root = absolute_path(arg_value(argc, argv, i, "--site")); else if(arg == "--cache") cfg.cache_root = arg_value(argc, argv, i, "--cache"); else if(arg == "--page") page = arg_value(argc, argv, i, "--page"); else if(arg == "--route") route = arg_value(argc, argv, i, "--route"); else if(arg == "--repeat") repeat = atoi(arg_value(argc, argv, i, "--repeat").c_str()); else if(arg == "--expect") expects.push_back(arg_value(argc, argv, i, "--expect")); else if(arg == "--expect-status") expect_status = arg_value(argc, argv, i, "--expect-status"); else if(arg == "--epoch-ticks") cfg.epoch_deadline_ticks = strtoull(arg_value(argc, argv, i, "--epoch-ticks").c_str(), 0, 10); else if(arg == "--epoch-ms") epoch_period_ms = strtoull(arg_value(argc, argv, i, "--epoch-ms").c_str(), 0, 10); else if(arg == "--mem-limit") cfg.memory_limit = strtoll(arg_value(argc, argv, i, "--mem-limit").c_str(), 0, 10); else if(arg == "--table-headroom") cfg.table_headroom = (u32)atoi(arg_value(argc, argv, i, "--table-headroom").c_str()); else if(arg == "--quiet") show_body = false; else if(arg == "--verbose") cfg.verbose = true; else if(arg == "--param" || arg == "--get") { String pair = arg_value(argc, argv, i, arg.c_str()); auto eq = pair.find("="); if(eq == String::npos) { fprintf(stderr, "%s expects K=V\n", arg.c_str()); return(2); } (arg == "--param" ? params : get_params)[pair.substr(0, eq)] = pair.substr(eq + 1); } else { fprintf(stderr, "unknown argument: %s\n", arg.c_str()); return(2); } } if(page == "") { fprintf(stderr, "usage: w3_driver --page /demo/hello.uce [--site site] [--cache /tmp/uce/work]\n" " [--core bin/wasm/core.wasm] [--param K=V ...] [--get K=V ...] [--route path]\n" " [--repeat N] [--expect STR ...] [--expect-status STR] [--quiet] [--verbose]\n" " [--epoch-ticks N] [--epoch-ms N] [--mem-limit BYTES]\n"); return(2); } String entry_unit = cfg.site_root + page; WasmWorker worker(cfg); String init_error = worker.init(); if(init_error != "") { fprintf(stderr, "FAIL: %s\n", init_error.c_str()); return(1); } // the epoch only advances through this ticker; period × deadline-ticks // is the per-request CPU budget std::atomic running(true); std::thread ticker([&] { while(running.load()) { std::this_thread::sleep_for(std::chrono::milliseconds(epoch_period_ms)); worker.engine.increment_epoch(); } }); DValue context_tree; for(auto& entry : params) context_tree["params"][entry.first] = entry.second; if(context_tree.key("params") == 0 || !params.count("HTTP_HOST")) context_tree["params"]["HTTP_HOST"] = "w3.test"; context_tree["params"]["SCRIPT_URL"] = page; context_tree["params"]["REQUEST_METHOD"] = "GET"; for(auto& entry : get_params) context_tree["get"][entry.first] = entry.second; if(route != "") { context_tree["route"]["l_path"] = route; context_tree["params"]["ROUTE_PATH"] = route; } context_tree["entry_unit"] = entry_unit; bool all_ok = true; WasmResponse last; for(int request = 0; request < repeat && all_ok; request++) { f64 started = (f64)clock() / CLOCKS_PER_SEC; last = wasm_worker_serve(worker, context_tree, entry_unit); f64 elapsed_ms = ((f64)clock() / CLOCKS_PER_SEC - started) * 1000.0; printf("==== request %d/%d (%.1f ms cpu) ====\n", request + 1, repeat, elapsed_ms); if(!last.ok) { printf("ERROR:\n%s\n", last.error.c_str()); all_ok = false; break; } printf("status: %s\n", last.meta["status"].to_string().c_str()); if(last.meta.key("headers")) last.meta["headers"].each([](const DValue& value, String key) { printf("header: %s: %s\n", key.c_str(), value.to_string().c_str()); }); if(show_body) printf("---- body (%zu bytes) ----\n%.*s\n----\n", last.body.size(), (int)last.body.size(), last.body.data()); else printf("body: %zu bytes\n", last.body.size()); } running.store(false); ticker.join(); if(all_ok && expect_status != "" && last.meta["status"].to_string().find(expect_status) == String::npos) { printf("FAIL: status %s does not contain %s\n", last.meta["status"].to_string().c_str(), expect_status.c_str()); all_ok = false; } for(auto& expect : expects) if(all_ok && last.body.find(expect) == String::npos) { printf("FAIL: body does not contain %s\n", expect.c_str()); all_ok = false; } printf(all_ok ? "W3 RESULT: PASS\n" : "W3 RESULT: FAIL\n"); return(all_ok ? 0 : 1); }