Skip to content

Commit 013492c

Browse files
committed
Improve the debug ergonomics of the Env generic.
Previously, we were passing our WebAPIs directly as an anonymous tuple. This resulted in Env(T) having an _awful_ name - a name composed of hundreds of classes. By wrapping the anonymous tuple into a normal struct, the Env now gets a sane name which helps improve stack traces (and profiling, and debugging, ...)
1 parent ca3fa3d commit 013492c

File tree

3 files changed

+39
-23
lines changed

3 files changed

+39
-23
lines changed

src/browser/env.zig

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,38 @@ const Loop = @import("../runtime/loop.zig").Loop;
99
const HttpClient = @import("../http/client.zig").Client;
1010
const Renderer = @import("browser.zig").Renderer;
1111

12-
const Interfaces = generate.Tuple(.{
13-
@import("console/console.zig").Console,
14-
@import("dom/dom.zig").Interfaces,
15-
@import("events/event.zig").Interfaces,
16-
@import("html/html.zig").Interfaces,
17-
@import("iterator/iterator.zig").Interfaces,
18-
@import("storage/storage.zig").Interfaces,
19-
@import("url/url.zig").Interfaces,
20-
@import("xhr/xhr.zig").Interfaces,
21-
@import("xmlserializer/xmlserializer.zig").Interfaces,
22-
});
12+
const WebApis = struct {
13+
// Wrapped like this for debug ergonomics.
14+
// When we create our Env, a few lines down, we define it as:
15+
// pub const Env = js.Env(*SessionState, WebApis);
16+
//
17+
// If there's a compile time error witht he Env, it's type will be readable,
18+
// i.e.: runtime.js.Env(*browser.env.SessionState, browser.env.WebApis)
19+
//
20+
// But if we didn't wrap it in the struct, like we once didn't, and defined
21+
// env as:
22+
// pub const Env = js.Env(*SessionState, Interfaces);
23+
//
24+
// Because Interfaces is an anynoumous type, it doesn't have a friendly name
25+
// and errors would be something like:
26+
// runtime.js.Env(*browser.env.SessionState, .{...A HUNDRED TYPES...})
27+
pub const Interfaces = generate.Tuple(.{
28+
@import("console/console.zig").Console,
29+
@import("dom/dom.zig").Interfaces,
30+
@import("events/event.zig").Interfaces,
31+
@import("html/html.zig").Interfaces,
32+
@import("iterator/iterator.zig").Interfaces,
33+
@import("storage/storage.zig").Interfaces,
34+
@import("url/url.zig").Interfaces,
35+
@import("xhr/xhr.zig").Interfaces,
36+
@import("xmlserializer/xmlserializer.zig").Interfaces,
37+
});
38+
};
2339

2440
pub const JsThis = Env.JsThis;
2541
pub const JsObject = Env.JsObject;
2642
pub const Callback = Env.Callback;
27-
pub const Env = js.Env(*SessionState, Interfaces{});
43+
pub const Env = js.Env(*SessionState, WebApis);
2844
pub const Global = @import("html/window.zig").Window;
2945

3046
pub const SessionState = struct {

src/runtime/js.zig

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ pub const Platform = struct {
5757
// The `S` parameter is arbitrary state. When we start an Executor, an instance
5858
// of S must be given. This instance is available to any Zig binding.
5959
// The `types` parameter is a tuple of Zig structures we want to bind to V8.
60-
pub fn Env(comptime S: type, comptime types: anytype) type {
61-
const Types = @typeInfo(@TypeOf(types)).@"struct".fields;
60+
pub fn Env(comptime S: type, comptime WebApis: type) type {
61+
const Types = @typeInfo(WebApis.Interfaces).@"struct".fields;
6262

6363
// Imagine we have a type Cat which has a getter:
6464
//
@@ -97,14 +97,14 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
9797
// TypeLookup. But we put it here, early, so that the rest of the
9898
// code doesn't have to worry about checking if Struct.prototype is
9999
// a pointer.
100-
const Struct = @field(types, s.name);
100+
const Struct = s.defaultValue().?;
101101
if (@hasDecl(Struct, "prototype") and @typeInfo(Struct.prototype) != .pointer) {
102102
@compileError(std.fmt.comptimePrint("Prototype '{s}' for type '{s} must be a pointer", .{ @typeName(Struct.prototype), @typeName(Struct) }));
103103
}
104104

105105
const subtype: ?SubType = if (@hasDecl(Struct, "subtype")) Struct.subtype else null;
106106

107-
const R = Receiver(@field(types, s.name));
107+
const R = Receiver(Struct);
108108
fields[i] = .{
109109
.name = @typeName(R),
110110
.type = TypeMeta,
@@ -141,7 +141,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
141141
const TYPE_LOOKUP = TypeLookup{};
142142
for (Types, 0..) |s, i| {
143143
var prototype_index = i;
144-
const Struct = @field(types, s.name);
144+
const Struct = s.defaultValue().?;
145145
if (@hasDecl(Struct, "prototype")) {
146146
const TI = @typeInfo(Struct.prototype);
147147
const proto_name = @typeName(Receiver(TI.pointer.child));
@@ -225,13 +225,13 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
225225
const templates = &env.templates;
226226
inline for (Types, 0..) |s, i| {
227227
@setEvalBranchQuota(10_000);
228-
templates[i] = v8.Persistent(v8.FunctionTemplate).init(isolate, generateClass(@field(types, s.name), isolate)).castToFunctionTemplate();
228+
templates[i] = v8.Persistent(v8.FunctionTemplate).init(isolate, generateClass(s.defaultValue().?, isolate)).castToFunctionTemplate();
229229
}
230230

231231
// Above, we've created all our our FunctionTemplates. Now that we
232232
// have them all, we can hook up the prototypes.
233233
inline for (Types, 0..) |s, i| {
234-
const Struct = @field(types, s.name);
234+
const Struct = s.defaultValue().?;
235235
if (@hasDecl(Struct, "prototype")) {
236236
const TI = @typeInfo(Struct.prototype);
237237
const proto_name = @typeName(Receiver(TI.pointer.child));
@@ -360,7 +360,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
360360
// are now going to get associated with our global instance.
361361
const templates = &self.env.templates;
362362
inline for (Types, 0..) |s, i| {
363-
const Struct = @field(types, s.name);
363+
const Struct = s.defaultValue().?;
364364
const class_name = v8.String.initUtf8(isolate, comptime classNameForStruct(Struct));
365365
global_template.set(class_name.toName(), templates[i], v8.PropertyAttribute.None);
366366
}
@@ -387,7 +387,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
387387
// https://groups.google.com/g/v8-users/c/qAQQBmbi--8
388388
// TODO: see if newer V8 engines have a way around this.
389389
inline for (Types, 0..) |s, i| {
390-
const Struct = @field(types, s.name);
390+
const Struct = s.defaultValue().?;
391391

392392
if (@hasDecl(Struct, "prototype")) {
393393
const proto_type = Receiver(@typeInfo(Struct.prototype).pointer.child);
@@ -463,7 +463,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
463463
// NOTE: there is no way in v8 to subclass the Error built-in type
464464
// TODO: this is an horrible hack
465465
inline for (Types) |s| {
466-
const Struct = @field(types, s.name);
466+
const Struct = s.defaultValue().?;
467467
if (@hasDecl(Struct, "ErrorSet")) {
468468
const script = comptime classNameForStruct(Struct) ++ ".prototype.__proto__ = Error.prototype";
469469
_ = try scope.exec(script, "errorSubclass");

src/runtime/testing.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub const allocator = std.testing.allocator;
2626
// browser.Env or the browser.SessionState
2727
pub fn Runner(comptime State: type, comptime Global: type, comptime types: anytype) type {
2828
const AdjustedTypes = if (Global == void) generate.Tuple(.{ types, DefaultGlobal }) else types;
29-
const Env = js.Env(State, AdjustedTypes{});
29+
const Env = js.Env(State, struct {pub const Interfaces = AdjustedTypes;});
3030

3131
return struct {
3232
env: *Env,

0 commit comments

Comments
 (0)