Skip to content

Commit 7c2f7b6

Browse files
authored
Merge pull request #595 from lightpanda-io/env_debug_ergonomics
Improve the debug ergonomics of the Env generic.
2 parents 7741de7 + 5f05de3 commit 7c2f7b6

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)