diff options
Diffstat (limited to 'lib/fuzzer')
| -rw-r--r-- | lib/fuzzer/main.js | 52 | ||||
| -rw-r--r-- | lib/fuzzer/wasm/main.zig | 87 |
2 files changed, 89 insertions, 50 deletions
diff --git a/lib/fuzzer/main.js b/lib/fuzzer/main.js index ac3a06ca5e..040aeeb6c4 100644 --- a/lib/fuzzer/main.js +++ b/lib/fuzzer/main.js @@ -68,6 +68,8 @@ } function navigate(location_hash) { + domSectSource.classList.add("hidden"); + curNavLocation = null; curNavSearch = null; @@ -82,23 +84,19 @@ curNavSearch = decodeURIComponent(query.substring(qpos + 1)); } - if (nonSearchPart.length > 0) { - curNavLocation = nonSearchPart; + if (nonSearchPart[0] == "l") { + curNavLocation = +nonSearchPart.substring(1); + renderSource(curNavLocation); } } render(); - - if (curNavLocation != null) { - // TODO - // scrollToSourceLocation(findSourceLocationIndex(curNavLocation)); - } } function connectWebSocket() { - const host = window.document.location.host; - const pathname = window.document.location.pathname; - const isHttps = window.document.location.protocol === 'https:'; + const host = document.location.host; + const pathname = document.location.pathname; + const isHttps = document.location.protocol === 'https:'; const match = host.match(/^(.+):(\d+)$/); const defaultPort = isHttps ? 443 : 80; const port = match ? parseInt(match[2], 10) : defaultPort; @@ -139,17 +137,12 @@ } function onSourceIndexChange() { - console.log("source location index metadata updated"); render(); + if (curNavLocation != null) renderSource(curNavLocation); } function render() { domStatus.classList.add("hidden"); - domSectSource.classList.add("hidden"); - - if (curNavLocation != null) { - renderSource(curNavLocation.split(":")[0]); - } } function renderStats() { @@ -186,22 +179,23 @@ return ((Number(a) / Number(b)) * 100).toFixed(1); } - function renderSource(path) { - const decl_index = findFileRoot(path); - if (decl_index == null) throw new Error("file not found: " + path); + function renderSource(sourceLocationIndex) { + const pathName = unwrapString(wasm_exports.sourceLocationPath(sourceLocationIndex)); + if (pathName.length === 0) return; const h2 = domSectSource.children[0]; - h2.innerText = path; - domSourceText.innerHTML = declSourceHtml(decl_index); + h2.innerText = pathName; + domSourceText.innerHTML = unwrapString(wasm_exports.sourceLocationFileHtml(sourceLocationIndex)); domSectSource.classList.remove("hidden"); - } - function findFileRoot(path) { - setInputString(path); - const result = wasm_exports.find_file_root(); - if (result === -1) return null; - return result; + const slDom = document.getElementById("l" + sourceLocationIndex); + if (slDom != null) { + slDom.scrollIntoView({ + behavior: "smooth", + block: "center", + }); + } } function decodeString(ptr, len) { @@ -224,10 +218,6 @@ wasmArray.set(jsArray); } - function declSourceHtml(decl_index) { - return unwrapString(wasm_exports.decl_source_html(decl_index)); - } - function unwrapString(bigint) { const ptr = Number(bigint & 0xffffffffn); const len = Number(bigint >> 32n); diff --git a/lib/fuzzer/wasm/main.zig b/lib/fuzzer/wasm/main.zig index 3226527f3a..5859a0ca0e 100644 --- a/lib/fuzzer/wasm/main.zig +++ b/lib/fuzzer/wasm/main.zig @@ -235,7 +235,59 @@ export fn entryPoints() Slice(u32) { return Slice(u32).init(entry_points.items); } +/// Index into `coverage_source_locations`. +const SourceLocationIndex = enum(u32) { + _, + + fn haveCoverage(sli: SourceLocationIndex) bool { + return @intFromEnum(sli) < coverage_source_locations.items.len; + } + + fn ptr(sli: SourceLocationIndex) *Coverage.SourceLocation { + return &coverage_source_locations.items[@intFromEnum(sli)]; + } + + fn sourceLocationLinkHtml( + sli: SourceLocationIndex, + out: *std.ArrayListUnmanaged(u8), + ) Allocator.Error!void { + const sl = sli.ptr(); + try out.writer(gpa).print("<a href=\"#l{d}\">", .{@intFromEnum(sli)}); + try sli.appendPath(out); + try out.writer(gpa).print(":{d}:{d}</a>", .{ sl.line, sl.column }); + } + + fn appendPath(sli: SourceLocationIndex, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { + const sl = sli.ptr(); + const file = coverage.fileAt(sl.file); + const file_name = coverage.stringAt(file.basename); + const dir_name = coverage.stringAt(coverage.directories.keys()[file.directory_index]); + try html_render.appendEscaped(out, dir_name); + try out.appendSlice(gpa, "/"); + try html_render.appendEscaped(out, file_name); + } + + fn toWalkFile(sli: SourceLocationIndex) ?Walk.File.Index { + var buf: std.ArrayListUnmanaged(u8) = .{}; + defer buf.deinit(gpa); + sli.appendPath(&buf) catch @panic("OOM"); + return @enumFromInt(Walk.files.getIndex(buf.items) orelse return null); + } + + fn fileHtml( + sli: SourceLocationIndex, + out: *std.ArrayListUnmanaged(u8), + ) error{ OutOfMemory, SourceUnavailable }!void { + const walk_file_index = sli.toWalkFile() orelse return error.SourceUnavailable; + const root_node = walk_file_index.findRootDecl().get().ast_node; + html_render.fileSourceHtml(walk_file_index, out, root_node, .{}) catch |err| { + fatal("unable to render source: {s}", .{@errorName(err)}); + }; + } +}; + var coverage = Coverage.init; +/// Index of type `SourceLocationIndex`. var coverage_source_locations: std.ArrayListUnmanaged(Coverage.SourceLocation) = .{}; /// Contains the most recent coverage update message, unmodified. var recent_coverage_update: std.ArrayListUnmanaged(u8) = .{}; @@ -263,27 +315,24 @@ fn updateCoverage( try coverage.directories.reIndexContext(gpa, .{ .string_bytes = coverage.string_bytes.items }); } -export fn sourceLocationLinkHtml(index: u32) String { +export fn sourceLocationLinkHtml(index: SourceLocationIndex) String { string_result.clearRetainingCapacity(); - sourceLocationLinkHtmlFallible(index, &string_result) catch @panic("OOM"); + index.sourceLocationLinkHtml(&string_result) catch @panic("OOM"); return String.init(string_result.items); } -fn sourceLocationLinkHtmlFallible(index: u32, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { - const sl = coverage_source_locations.items[index]; - const file = coverage.fileAt(sl.file); - const file_name = coverage.stringAt(file.basename); - const dir_name = coverage.stringAt(coverage.directories.keys()[file.directory_index]); - - out.clearRetainingCapacity(); - try out.appendSlice(gpa, "<a href=\"#"); - _ = html_render.missing_feature_url_escape; - try out.writer(gpa).print("{s}/{s}:{d}:{d}", .{ - dir_name, file_name, sl.line, sl.column, - }); - try out.appendSlice(gpa, "\">"); - try html_render.appendEscaped(out, dir_name); - try out.appendSlice(gpa, "/"); - try html_render.appendEscaped(out, file_name); - try out.writer(gpa).print(":{d}:{d}</a>", .{ sl.line, sl.column }); +/// Returns empty string if coverage metadata is not available for this source location. +export fn sourceLocationPath(sli: SourceLocationIndex) String { + string_result.clearRetainingCapacity(); + if (sli.haveCoverage()) sli.appendPath(&string_result) catch @panic("OOM"); + return String.init(string_result.items); +} + +export fn sourceLocationFileHtml(sli: SourceLocationIndex) String { + string_result.clearRetainingCapacity(); + sli.fileHtml(&string_result) catch |err| switch (err) { + error.OutOfMemory => @panic("OOM"), + error.SourceUnavailable => {}, + }; + return String.init(string_result.items); } |
