Skip to content

Commit 5f05de3

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 7741de7 commit 5f05de3

File tree

3 files changed

+40
-24
lines changed

3 files changed

+40
-24
lines changed

src/browser/env.zig

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,39 @@ 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("crypto/crypto.zig").Crypto,
14-
@import("console/console.zig").Console,
15-
@import("dom/dom.zig").Interfaces,
16-
@import("events/event.zig").Interfaces,
17-
@import("html/html.zig").Interfaces,
18-
@import("iterator/iterator.zig").Interfaces,
19-
@import("storage/storage.zig").Interfaces,
20-
@import("url/url.zig").Interfaces,
21-
@import("xhr/xhr.zig").Interfaces,
22-
@import("xmlserializer/xmlserializer.zig").Interfaces,
23-
});
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("crypto/crypto.zig").Crypto,
29+
@import("console/console.zig").Console,
30+
@import("dom/dom.zig").Interfaces,
31+
@import("events/event.zig").Interfaces,
32+
@import("html/html.zig").Interfaces,
33+
@import("iterator/iterator.zig").Interfaces,
34+
@import("storage/storage.zig").Interfaces,
35+
@import("url/url.zig").Interfaces,
36+
@import("xhr/xhr.zig").Interfaces,
37+
@import("xmlserializer/xmlserializer.zig").Interfaces,
38+
});
39+
};
2440

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

3147
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));
@@ -359,7 +359,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
359359
// are now going to get associated with our global instance.
360360
const templates = &self.env.templates;
361361
inline for (Types, 0..) |s, i| {
362-
const Struct = @field(types, s.name);
362+
const Struct = s.defaultValue().?;
363363
const class_name = v8.String.initUtf8(isolate, comptime classNameForStruct(Struct));
364364
global_template.set(class_name.toName(), templates[i], v8.PropertyAttribute.None);
365365
}
@@ -386,7 +386,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
386386
// https://groups.google.com/g/v8-users/c/qAQQBmbi--8
387387
// TODO: see if newer V8 engines have a way around this.
388388
inline for (Types, 0..) |s, i| {
389-
const Struct = @field(types, s.name);
389+
const Struct = s.defaultValue().?;
390390

391391
if (@hasDecl(Struct, "prototype")) {
392392
const proto_type = Receiver(@typeInfo(Struct.prototype).pointer.child);
@@ -462,7 +462,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
462462
// NOTE: there is no way in v8 to subclass the Error built-in type
463463
// TODO: this is an horrible hack
464464
inline for (Types) |s| {
465-
const Struct = @field(types, s.name);
465+
const Struct = s.defaultValue().?;
466466
if (@hasDecl(Struct, "ErrorSet")) {
467467
const script = comptime classNameForStruct(Struct) ++ ".prototype.__proto__ = Error.prototype";
468468
_ = 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)