zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

main.js (12488B) - Raw


      1 const domConnectionStatus = document.getElementById("connectionStatus");
      2 const domFirefoxWebSocketBullshitExplainer = document.getElementById("firefoxWebSocketBullshitExplainer");
      3 
      4 const domMain = document.getElementsByTagName("main")[0];
      5 const domSummary = {
      6   stepCount: document.getElementById("summaryStepCount"),
      7   status: document.getElementById("summaryStatus"),
      8 };
      9 const domButtonRebuild = document.getElementById("buttonRebuild");
     10 const domStepList = document.getElementById("stepList");
     11 let domSteps = [];
     12 
     13 let wasm_promise = fetch("main.wasm");
     14 let wasm_exports = null;
     15 
     16 const text_decoder = new TextDecoder();
     17 const text_encoder = new TextEncoder();
     18 
     19 domButtonRebuild.addEventListener("click", () => wasm_exports.rebuild());
     20 
     21 setConnectionStatus("Loading WebAssembly...", false);
     22 WebAssembly.instantiateStreaming(wasm_promise, {
     23   core: {
     24     log: function(ptr, len) {
     25       const msg = decodeString(ptr, len);
     26       console.log(msg);
     27     },
     28     panic: function (ptr, len) {
     29       const msg = decodeString(ptr, len);
     30       throw new Error("panic: " + msg);
     31     },
     32     timestamp: function () {
     33       return BigInt(new Date());
     34     },
     35     hello: hello,
     36     updateBuildStatus: updateBuildStatus,
     37     updateStepStatus: updateStepStatus,
     38     sendWsMessage: (ptr, len) => ws.send(new Uint8Array(wasm_exports.memory.buffer, ptr, len)),
     39   },
     40   fuzz: {
     41     requestSources: fuzzRequestSources,
     42     ready: fuzzReady,
     43     updateStats: fuzzUpdateStats,
     44     updateEntryPoints: fuzzUpdateEntryPoints,
     45     updateSource: fuzzUpdateSource,
     46     updateCoverage: fuzzUpdateCoverage,
     47   },
     48   time_report: {
     49     updateCompile: timeReportUpdateCompile,
     50     updateGeneric: timeReportUpdateGeneric,
     51   },
     52 }).then(function(obj) {
     53   setConnectionStatus("Connecting to WebSocket...", true);
     54   connectWebSocket();
     55 
     56   wasm_exports = obj.instance.exports;
     57   window.wasm = obj; // for debugging
     58 });
     59 
     60 function connectWebSocket() {
     61   const host = document.location.host;
     62   const pathname = document.location.pathname;
     63   const isHttps = document.location.protocol === 'https:';
     64   const match = host.match(/^(.+):(\d+)$/);
     65   const defaultPort = isHttps ? 443 : 80;
     66   const port = match ? parseInt(match[2], 10) : defaultPort;
     67   const hostName = match ? match[1] : host;
     68   const wsProto = isHttps ? "wss:" : "ws:";
     69   const wsUrl = wsProto + '//' + hostName + ':' + port + pathname;
     70   ws = new WebSocket(wsUrl);
     71   ws.binaryType = "arraybuffer";
     72   ws.addEventListener('message', onWebSocketMessage, false);
     73   ws.addEventListener('error', onWebSocketClose, false);
     74   ws.addEventListener('close', onWebSocketClose, false);
     75   ws.addEventListener('open', onWebSocketOpen, false);
     76 }
     77 function onWebSocketOpen() {
     78   setConnectionStatus("Waiting for data...", false);
     79 }
     80 function onWebSocketMessage(ev) {
     81   const jsArray = new Uint8Array(ev.data);
     82   const ptr = wasm_exports.message_begin(jsArray.length);
     83   const wasmArray = new Uint8Array(wasm_exports.memory.buffer, ptr, jsArray.length);
     84   wasmArray.set(jsArray);
     85   wasm_exports.message_end();
     86 }
     87 function onWebSocketClose() {
     88   setConnectionStatus("WebSocket connection closed. Re-connecting...", true);
     89   ws.removeEventListener('message', onWebSocketMessage, false);
     90   ws.removeEventListener('error', onWebSocketClose, false);
     91   ws.removeEventListener('close', onWebSocketClose, false);
     92   ws.removeEventListener('open', onWebSocketOpen, false);
     93   ws = null;
     94   setTimeout(connectWebSocket, 1000);
     95 }
     96 
     97 function setConnectionStatus(msg, is_websocket_connect) {
     98   domConnectionStatus.textContent = msg;
     99   if (msg.length > 0) {
    100     domConnectionStatus.classList.remove("hidden");
    101     domMain.classList.add("hidden");
    102   } else {
    103     domConnectionStatus.classList.add("hidden");
    104     domMain.classList.remove("hidden");
    105   }
    106   if (is_websocket_connect) {
    107     domFirefoxWebSocketBullshitExplainer.classList.remove("hidden");
    108   } else {
    109     domFirefoxWebSocketBullshitExplainer.classList.add("hidden");
    110   }
    111 }
    112 
    113 function hello(
    114   steps_len,
    115   build_status,
    116   time_report,
    117 ) {
    118   domSummary.stepCount.textContent = steps_len;
    119   updateBuildStatus(build_status);
    120   setConnectionStatus("", false);
    121 
    122   {
    123     let entries = [];
    124     for (let i = 0; i < steps_len; i += 1) {
    125       const step_name = unwrapString(wasm_exports.stepName(i));
    126       const code = document.createElement("code");
    127       code.textContent = step_name;
    128       const li = document.createElement("li");
    129       li.appendChild(code);
    130       entries.push(li);
    131     }
    132     domStepList.replaceChildren(...entries);
    133     for (let i = 0; i < steps_len; i += 1) {
    134       updateStepStatus(i);
    135     }
    136   }
    137 
    138   if (time_report) timeReportReset(steps_len);
    139   fuzzReset();
    140 }
    141 
    142 function updateBuildStatus(s) {
    143   let text;
    144   let active = false;
    145   let reset_time_reports = false;
    146   if (s == 0) {
    147     text = "Idle";
    148   } else if (s == 1) {
    149     text = "Watching for changes...";
    150   } else if (s == 2) {
    151     text = "Running...";
    152     active = true;
    153     reset_time_reports = true;
    154   } else if (s == 3) {
    155     text = "Starting fuzzer...";
    156     active = true;
    157   } else {
    158     console.log(`bad build status: ${s}`);
    159   }
    160   domSummary.status.textContent = text;
    161   if (active) {
    162     domSummary.status.classList.add("status-running");
    163     domSummary.status.classList.remove("status-idle");
    164     domButtonRebuild.disabled = true;
    165   } else {
    166     domSummary.status.classList.remove("status-running");
    167     domSummary.status.classList.add("status-idle");
    168     domButtonRebuild.disabled = false;
    169   }
    170   if (reset_time_reports) {
    171     // Grey out and collapse all the time reports
    172     for (const time_report_host of domTimeReportList.children) {
    173       const details = time_report_host.shadowRoot.querySelector(":host > details");
    174       details.classList.add("pending");
    175       details.open = false;
    176     }
    177   }
    178 }
    179 function updateStepStatus(step_idx) {
    180   const li = domStepList.children[step_idx];
    181   const step_status = wasm_exports.stepStatus(step_idx);
    182   li.classList.remove("step-wip", "step-success", "step-failure");
    183   if (step_status == 0) {
    184     // pending
    185   } else if (step_status == 1) {
    186     li.classList.add("step-wip");
    187   } else if (step_status == 2) {
    188     li.classList.add("step-success");
    189   } else if (step_status == 3) {
    190     li.classList.add("step-failure");
    191   } else {
    192     console.log(`bad step status: ${step_status}`);
    193   }
    194 }
    195 
    196 function decodeString(ptr, len) {
    197   if (len === 0) return "";
    198   return text_decoder.decode(new Uint8Array(wasm_exports.memory.buffer, ptr, len));
    199 }
    200 function getU32Array(ptr, len) {
    201   if (len === 0) return new Uint32Array();
    202   return new Uint32Array(wasm_exports.memory.buffer, ptr, len);
    203 }
    204 function unwrapString(bigint) {
    205   const ptr = Number(bigint & 0xffffffffn);
    206   const len = Number(bigint >> 32n);
    207   return decodeString(ptr, len);
    208 }
    209 
    210 const time_report_entry_template = document.getElementById("timeReportEntryTemplate").content;
    211 const domTimeReport = document.getElementById("timeReport");
    212 const domTimeReportList = document.getElementById("timeReportList");
    213 function timeReportReset(steps_len) {
    214   let entries = [];
    215   for (let i = 0; i < steps_len; i += 1) {
    216     const step_name = unwrapString(wasm_exports.stepName(i));
    217     const host = document.createElement("div");
    218     const shadow = host.attachShadow({ mode: "open" });
    219     shadow.appendChild(time_report_entry_template.cloneNode(true));
    220     shadow.querySelector(":host > details").classList.add("pending");
    221     const slotted_name = document.createElement("code");
    222     slotted_name.setAttribute("slot", "step-name");
    223     slotted_name.textContent = step_name;
    224     host.appendChild(slotted_name);
    225     entries.push(host);
    226   }
    227   domTimeReportList.replaceChildren(...entries);
    228   domTimeReport.classList.remove("hidden");
    229 }
    230 function timeReportUpdateCompile(
    231   step_idx,
    232   inner_html_ptr,
    233   inner_html_len,
    234   file_table_html_ptr,
    235   file_table_html_len,
    236   decl_table_html_ptr,
    237   decl_table_html_len,
    238   use_llvm,
    239 ) {
    240   const inner_html = decodeString(inner_html_ptr, inner_html_len);
    241   const file_table_html = decodeString(file_table_html_ptr, file_table_html_len);
    242   const decl_table_html = decodeString(decl_table_html_ptr, decl_table_html_len);
    243 
    244   const host = domTimeReportList.children.item(step_idx);
    245   const shadow = host.shadowRoot;
    246 
    247   shadow.querySelector(":host > details").classList.remove("pending", "no-llvm");
    248 
    249   shadow.getElementById("genericReport").classList.add("hidden");
    250   shadow.getElementById("compileReport").classList.remove("hidden");
    251 
    252   if (!use_llvm) shadow.querySelector(":host > details").classList.add("no-llvm");
    253   host.innerHTML = inner_html;
    254   shadow.getElementById("fileTableBody").innerHTML = file_table_html;
    255   shadow.getElementById("declTableBody").innerHTML = decl_table_html;
    256 }
    257 function timeReportUpdateGeneric(
    258   step_idx,
    259   inner_html_ptr,
    260   inner_html_len,
    261 ) {
    262   const inner_html = decodeString(inner_html_ptr, inner_html_len);
    263   const host = domTimeReportList.children.item(step_idx);
    264   const shadow = host.shadowRoot;
    265   shadow.querySelector(":host > details").classList.remove("pending", "no-llvm");
    266   shadow.getElementById("genericReport").classList.remove("hidden");
    267   shadow.getElementById("compileReport").classList.add("hidden");
    268   host.innerHTML = inner_html;
    269 }
    270 
    271 const fuzz_entry_template = document.getElementById("fuzzEntryTemplate").content;
    272 const domFuzz = document.getElementById("fuzz");
    273 const domFuzzStatus = document.getElementById("fuzzStatus");
    274 const domFuzzEntries = document.getElementById("fuzzEntries");
    275 let domFuzzInstance = null;
    276 function fuzzRequestSources() {
    277   domFuzzStatus.classList.remove("hidden");
    278   domFuzzStatus.textContent = "Loading sources tarball...";
    279   fetch("sources.tar").then(function(response) {
    280     if (!response.ok) throw new Error("unable to download sources");
    281     domFuzzStatus.textContent = "Parsing fuzz test sources...";
    282     return response.arrayBuffer();
    283   }).then(function(buffer) {
    284     if (buffer.length === 0) throw new Error("sources.tar was empty");
    285     const js_array = new Uint8Array(buffer);
    286     const ptr = wasm_exports.alloc(js_array.length);
    287     const wasm_array = new Uint8Array(wasm_exports.memory.buffer, ptr, js_array.length);
    288     wasm_array.set(js_array);
    289     wasm_exports.fuzzUnpackSources(ptr, js_array.length);
    290     domFuzzStatus.textContent = "";
    291     domFuzzStatus.classList.add("hidden");
    292   });
    293 }
    294 function fuzzReady() {
    295   domFuzz.classList.remove("hidden");
    296 
    297   // TODO: multiple fuzzer instances
    298   if (domFuzzInstance !== null) return;
    299 
    300   const host = document.createElement("div");
    301   const shadow = host.attachShadow({ mode: "open" });
    302   shadow.appendChild(fuzz_entry_template.cloneNode(true));
    303 
    304   domFuzzInstance = host;
    305   domFuzzEntries.appendChild(host);
    306 }
    307 function fuzzReset() {
    308   domFuzz.classList.add("hidden");
    309   domFuzzEntries.replaceChildren();
    310   domFuzzInstance = null;
    311 }
    312 function fuzzUpdateStats(stats_html_ptr, stats_html_len) {
    313   if (domFuzzInstance === null) throw new Error("fuzzUpdateStats called when fuzzer inactive");
    314   const stats_html = decodeString(stats_html_ptr, stats_html_len);
    315   const host = domFuzzInstance;
    316   host.innerHTML = stats_html;
    317 }
    318 function fuzzUpdateEntryPoints(entry_points_html_ptr, entry_points_html_len) {
    319   if (domFuzzInstance === null) throw new Error("fuzzUpdateEntryPoints called when fuzzer inactive");
    320   const entry_points_html = decodeString(entry_points_html_ptr, entry_points_html_len);
    321   const domEntryPointList = domFuzzInstance.shadowRoot.getElementById("entryPointList");
    322   domEntryPointList.innerHTML = entry_points_html;
    323 }
    324 function fuzzUpdateSource(source_html_ptr, source_html_len) {
    325   if (domFuzzInstance === null) throw new Error("fuzzUpdateSource called when fuzzer inactive");
    326   const source_html = decodeString(source_html_ptr, source_html_len);
    327   const domSourceText = domFuzzInstance.shadowRoot.getElementById("sourceText");
    328   domSourceText.innerHTML = source_html;
    329   domFuzzInstance.shadowRoot.getElementById("source").classList.remove("hidden");
    330 }
    331 function fuzzUpdateCoverage(covered_ptr, covered_len) {
    332   if (domFuzzInstance === null) throw new Error("fuzzUpdateCoverage called when fuzzer inactive");
    333   const shadow = domFuzzInstance.shadowRoot;
    334   const domSourceText = shadow.getElementById("sourceText");
    335   const covered = getU32Array(covered_ptr, covered_len);
    336   for (let i = 0; i < domSourceText.children.length; i += 1) {
    337     const childDom = domSourceText.children[i];
    338     if (childDom.id != null && childDom.id[0] == "l") {
    339       childDom.classList.add("l");
    340       childDom.classList.remove("c");
    341     }
    342   }
    343   for (const sli of covered) {
    344     shadow.getElementById(`l${sli}`).classList.add("c");
    345   }
    346 }