Support binding JS strings to [:0]const u8

Some APIs need a null-terminated string. Currently, they have to ask for a
`[]const u8` and then convert it to a `[:0]const u8`. This is 2 allocations: 1
for jsruntime to get the `[]const u8` from v8, and then one to get the [:0]. By
supporting `[:0]const u8` directly, this is now a single allocation.
This commit is contained in:
Karl Seguin
2025-04-19 16:19:46 +08:00
parent 66ec087416
commit 7bdea1befa
3 changed files with 36 additions and 35 deletions

View File

@@ -31,45 +31,20 @@ pub const DOMImplementation = struct {
pub fn _createDocumentType(
_: *DOMImplementation,
qname: []const u8,
publicId: []const u8,
systemId: []const u8,
state: *SessionState,
qname: [:0]const u8,
publicId: [:0]const u8,
systemId: [:0]const u8,
) !*parser.DocumentType {
const allocator = state.arena;
const cqname = try allocator.dupeZ(u8, qname);
defer allocator.free(cqname);
const cpublicId = try allocator.dupeZ(u8, publicId);
defer allocator.free(cpublicId);
const csystemId = try allocator.dupeZ(u8, systemId);
defer allocator.free(csystemId);
return try parser.domImplementationCreateDocumentType(cqname, cpublicId, csystemId);
return try parser.domImplementationCreateDocumentType(qname, publicId, systemId);
}
pub fn _createDocument(
_: *DOMImplementation,
namespace: ?[]const u8,
qname: ?[]const u8,
namespace: ?[:0]const u8,
qname: ?[:0]const u8,
doctype: ?*parser.DocumentType,
state: *SessionState,
) !*parser.Document {
const allocator = state.arena;
var cnamespace: ?[:0]const u8 = null;
if (namespace) |ns| {
cnamespace = try allocator.dupeZ(u8, ns);
}
defer if (cnamespace) |v| allocator.free(v);
var cqname: ?[:0]const u8 = null;
if (qname) |qn| {
cqname = try allocator.dupeZ(u8, qn);
}
defer if (cqname) |v| allocator.free(v);
return try parser.domImplementationCreateDocument(cnamespace, cqname, doctype);
return try parser.domImplementationCreateDocument(namespace, qname, doctype);
}
pub fn _createHTMLDocument(_: *DOMImplementation, title: ?[]const u8) !*parser.DocumentHTML {
@@ -79,8 +54,6 @@ pub const DOMImplementation = struct {
pub fn _hasFeature(_: *DOMImplementation) bool {
return true;
}
pub fn deinit(_: *DOMImplementation, _: std.mem.Allocator) void {}
};
// Tests

View File

@@ -1862,7 +1862,13 @@ fn Caller(comptime E: type) type {
},
.slice => {
if (ptr.child == u8) {
return valueToString(self.call_allocator, js_value, self.isolate, self.context);
if (ptr.sentinel()) |s| {
if (comptime s == 0) {
return valueToStringZ(self.call_allocator, js_value, self.isolate, self.context);
}
} else {
return valueToString(self.call_allocator, js_value, self.isolate, self.context);
}
}
// TODO: TypedArray
@@ -2241,6 +2247,16 @@ fn valueToString(allocator: Allocator, value: v8.Value, isolate: v8.Isolate, con
return buf;
}
fn valueToStringZ(allocator: Allocator, value: v8.Value, isolate: v8.Isolate, context: v8.Context) ![:0]u8 {
const str = try value.toString(context);
const len = str.lenUtf8(isolate);
const buf = try allocator.alloc(u8, len + 1);
const n = str.writeUtf8(isolate, buf[0..len]);
std.debug.assert(n == len);
buf[len] = 0;
return buf[0..len :0];
}
const NoopInspector = struct {
pub fn onInspectorResponse(_: *anyopaque, _: u32, _: []const u8) void {}
pub fn onInspectorEvent(_: *anyopaque, _: []const u8) void {}

View File

@@ -96,6 +96,14 @@ const Primitives = struct {
pub fn _checkOptionalReturnString(_: *const Primitives) ?[]const u8 {
return "ok";
}
pub fn _echoString(_: *const Primitives, a: []const u8) []const u8 {
return a;
}
pub fn _echoStringZ(_: *const Primitives, a: [:0]const u8) []const u8 {
return a;
}
};
const testing = @import("testing.zig");
@@ -172,5 +180,9 @@ test "JS: primitive types" {
.{ "p.checkOptionalReturn() === true;", "true" },
.{ "p.checkOptionalReturnNull() === null;", "true" },
.{ "p.checkOptionalReturnString() === 'ok';", "true" },
// strings
.{ "p.echoString('over 9000!');", "over 9000!" },
.{ "p.echoStringZ('Teg');", "Teg" },
}, .{});
}