mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
214 lines
7.1 KiB
Zig
214 lines
7.1 KiB
Zig
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const abi = std.Build.abi;
|
|
const gpa = std.heap.wasm_allocator;
|
|
const log = std.log;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const fuzz = @import("fuzz.zig");
|
|
const time_report = @import("time_report.zig");
|
|
|
|
/// Nanoseconds.
|
|
var server_base_timestamp: i64 = 0;
|
|
/// Milliseconds.
|
|
var client_base_timestamp: i64 = 0;
|
|
|
|
pub var step_list: []Step = &.{};
|
|
/// Not accessed after initialization, but must be freed alongside `step_list`.
|
|
pub var step_list_data: []u8 = &.{};
|
|
|
|
const Step = struct {
|
|
name: []const u8,
|
|
status: abi.StepUpdate.Status,
|
|
};
|
|
|
|
const js = struct {
|
|
extern "core" fn log(ptr: [*]const u8, len: usize) void;
|
|
extern "core" fn panic(ptr: [*]const u8, len: usize) noreturn;
|
|
extern "core" fn timestamp() i64;
|
|
extern "core" fn hello(
|
|
steps_len: u32,
|
|
status: abi.BuildStatus,
|
|
time_report: bool,
|
|
) void;
|
|
extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;
|
|
extern "core" fn updateStepStatus(step_idx: u32) void;
|
|
extern "core" fn sendWsMessage(ptr: [*]const u8, len: usize) void;
|
|
};
|
|
|
|
pub const std_options: std.Options = .{
|
|
.logFn = logFn,
|
|
};
|
|
|
|
pub fn panic(msg: []const u8, st: ?*std.builtin.StackTrace, addr: ?usize) noreturn {
|
|
_ = st;
|
|
_ = addr;
|
|
log.err("panic: {s}", .{msg});
|
|
@trap();
|
|
}
|
|
|
|
fn logFn(
|
|
comptime message_level: log.Level,
|
|
comptime scope: @EnumLiteral(),
|
|
comptime format: []const u8,
|
|
args: anytype,
|
|
) void {
|
|
const level_txt = comptime message_level.asText();
|
|
const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
|
|
var buf: [500]u8 = undefined;
|
|
const line = std.fmt.bufPrint(&buf, level_txt ++ prefix2 ++ format, args) catch l: {
|
|
buf[buf.len - 3 ..][0..3].* = "...".*;
|
|
break :l &buf;
|
|
};
|
|
js.log(line.ptr, line.len);
|
|
}
|
|
|
|
export fn alloc(n: usize) [*]u8 {
|
|
const slice = gpa.alloc(u8, n) catch @panic("OOM");
|
|
return slice.ptr;
|
|
}
|
|
|
|
var message_buffer: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty;
|
|
|
|
/// Resizes the message buffer to be the correct length; returns the pointer to
|
|
/// the query string.
|
|
export fn message_begin(len: usize) [*]u8 {
|
|
message_buffer.resize(gpa, len) catch @panic("OOM");
|
|
return message_buffer.items.ptr;
|
|
}
|
|
|
|
export fn message_end() void {
|
|
const msg_bytes = message_buffer.items;
|
|
|
|
const tag: abi.ToClientTag = @enumFromInt(msg_bytes[0]);
|
|
switch (tag) {
|
|
_ => @panic("malformed message"),
|
|
|
|
.hello => return helloMessage(msg_bytes) catch @panic("OOM"),
|
|
.status_update => return statusUpdateMessage(msg_bytes) catch @panic("OOM"),
|
|
.step_update => return stepUpdateMessage(msg_bytes) catch @panic("OOM"),
|
|
|
|
.fuzz_source_index => return fuzz.sourceIndexMessage(msg_bytes) catch @panic("OOM"),
|
|
.fuzz_coverage_update => return fuzz.coverageUpdateMessage(msg_bytes) catch @panic("OOM"),
|
|
.fuzz_entry_points => return fuzz.entryPointsMessage(msg_bytes) catch @panic("OOM"),
|
|
|
|
.time_report_generic_result => return time_report.genericResultMessage(msg_bytes) catch @panic("OOM"),
|
|
.time_report_compile_result => return time_report.compileResultMessage(msg_bytes) catch @panic("OOM"),
|
|
.time_report_run_test_result => return time_report.runTestResultMessage(msg_bytes) catch @panic("OOM"),
|
|
}
|
|
}
|
|
|
|
const String = Slice(u8);
|
|
|
|
pub fn Slice(T: type) type {
|
|
return packed struct(u64) {
|
|
ptr: u32,
|
|
len: u32,
|
|
|
|
pub fn init(s: []const T) @This() {
|
|
return .{
|
|
.ptr = @intFromPtr(s.ptr),
|
|
.len = s.len,
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
|
|
var buf: [500]u8 = undefined;
|
|
const line = std.fmt.bufPrint(&buf, format, args) catch l: {
|
|
buf[buf.len - 3 ..][0..3].* = "...".*;
|
|
break :l &buf;
|
|
};
|
|
js.panic(line.ptr, line.len);
|
|
}
|
|
|
|
fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void {
|
|
if (msg_bytes.len < @sizeOf(abi.Hello)) @panic("malformed Hello message");
|
|
const hdr: *const abi.Hello = @ptrCast(msg_bytes[0..@sizeOf(abi.Hello)]);
|
|
const trailing = msg_bytes[@sizeOf(abi.Hello)..];
|
|
|
|
client_base_timestamp = js.timestamp();
|
|
server_base_timestamp = hdr.timestamp;
|
|
|
|
const steps = try gpa.alloc(Step, hdr.steps_len);
|
|
errdefer gpa.free(steps);
|
|
|
|
const step_name_lens: []align(1) const u32 = @ptrCast(trailing[0 .. steps.len * 4]);
|
|
|
|
const step_name_data_len: usize = len: {
|
|
var sum: usize = 0;
|
|
for (step_name_lens) |n| sum += n;
|
|
break :len sum;
|
|
};
|
|
const step_name_data: []const u8 = trailing[steps.len * 4 ..][0..step_name_data_len];
|
|
const step_status_bits: []const u8 = trailing[steps.len * 4 + step_name_data_len ..];
|
|
|
|
const duped_step_name_data = try gpa.dupe(u8, step_name_data);
|
|
errdefer gpa.free(duped_step_name_data);
|
|
|
|
var name_off: usize = 0;
|
|
for (steps, step_name_lens, 0..) |*step_out, name_len, step_idx| {
|
|
step_out.* = .{
|
|
.name = duped_step_name_data[name_off..][0..name_len],
|
|
.status = @enumFromInt(@as(u2, @truncate(step_status_bits[step_idx / 4] >> @intCast((step_idx % 4) * 2)))),
|
|
};
|
|
name_off += name_len;
|
|
}
|
|
|
|
gpa.free(step_list);
|
|
gpa.free(step_list_data);
|
|
step_list = steps;
|
|
step_list_data = duped_step_name_data;
|
|
|
|
js.hello(step_list.len, hdr.status, hdr.flags.time_report);
|
|
}
|
|
fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
|
|
if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");
|
|
const msg: *const abi.StatusUpdate = @ptrCast(msg_bytes[0..@sizeOf(abi.StatusUpdate)]);
|
|
js.updateBuildStatus(msg.new);
|
|
}
|
|
fn stepUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
|
|
if (msg_bytes.len < @sizeOf(abi.StepUpdate)) @panic("malformed StepUpdate message");
|
|
const msg: *const abi.StepUpdate = @ptrCast(msg_bytes[0..@sizeOf(abi.StepUpdate)]);
|
|
if (msg.step_idx >= step_list.len) @panic("malformed StepUpdate message");
|
|
step_list[msg.step_idx].status = msg.bits.status;
|
|
js.updateStepStatus(msg.step_idx);
|
|
}
|
|
|
|
export fn stepName(idx: usize) String {
|
|
return .init(step_list[idx].name);
|
|
}
|
|
export fn stepStatus(idx: usize) u8 {
|
|
return @intFromEnum(step_list[idx].status);
|
|
}
|
|
|
|
export fn rebuild() void {
|
|
const msg: abi.Rebuild = .{};
|
|
const raw: []const u8 = @ptrCast(&msg);
|
|
js.sendWsMessage(raw.ptr, raw.len);
|
|
}
|
|
|
|
/// Nanoseconds passed since a server timestamp.
|
|
pub fn nsSince(server_timestamp: i64) i64 {
|
|
const ms_passed = js.timestamp() - client_base_timestamp;
|
|
const ns_passed = server_base_timestamp - server_timestamp;
|
|
return ns_passed + ms_passed * std.time.ns_per_ms;
|
|
}
|
|
|
|
pub fn fmtEscapeHtml(unescaped: []const u8) HtmlEscaper {
|
|
return .{ .unescaped = unescaped };
|
|
}
|
|
const HtmlEscaper = struct {
|
|
unescaped: []const u8,
|
|
pub fn format(he: HtmlEscaper, w: *std.Io.Writer) !void {
|
|
for (he.unescaped) |c| switch (c) {
|
|
'&' => try w.writeAll("&"),
|
|
'<' => try w.writeAll("<"),
|
|
'>' => try w.writeAll(">"),
|
|
'"' => try w.writeAll("""),
|
|
'\'' => try w.writeAll("'"),
|
|
else => try w.writeByte(c),
|
|
};
|
|
}
|
|
};
|