diff --git a/src/browser/env.zig b/src/browser/env.zig index ffb78a48..3f04ff09 100644 --- a/src/browser/env.zig +++ b/src/browser/env.zig @@ -9,23 +9,39 @@ const Loop = @import("../runtime/loop.zig").Loop; const HttpClient = @import("../http/client.zig").Client; const Renderer = @import("browser.zig").Renderer; -const Interfaces = generate.Tuple(.{ - @import("crypto/crypto.zig").Crypto, - @import("console/console.zig").Console, - @import("dom/dom.zig").Interfaces, - @import("events/event.zig").Interfaces, - @import("html/html.zig").Interfaces, - @import("iterator/iterator.zig").Interfaces, - @import("storage/storage.zig").Interfaces, - @import("url/url.zig").Interfaces, - @import("xhr/xhr.zig").Interfaces, - @import("xmlserializer/xmlserializer.zig").Interfaces, -}); +const WebApis = struct { + // Wrapped like this for debug ergonomics. + // When we create our Env, a few lines down, we define it as: + // pub const Env = js.Env(*SessionState, WebApis); + // + // If there's a compile time error witht he Env, it's type will be readable, + // i.e.: runtime.js.Env(*browser.env.SessionState, browser.env.WebApis) + // + // But if we didn't wrap it in the struct, like we once didn't, and defined + // env as: + // pub const Env = js.Env(*SessionState, Interfaces); + // + // Because Interfaces is an anynoumous type, it doesn't have a friendly name + // and errors would be something like: + // runtime.js.Env(*browser.env.SessionState, .{...A HUNDRED TYPES...}) + pub const Interfaces = generate.Tuple(.{ + @import("crypto/crypto.zig").Crypto, + @import("console/console.zig").Console, + @import("dom/dom.zig").Interfaces, + @import("events/event.zig").Interfaces, + @import("html/html.zig").Interfaces, + @import("iterator/iterator.zig").Interfaces, + @import("storage/storage.zig").Interfaces, + @import("url/url.zig").Interfaces, + @import("xhr/xhr.zig").Interfaces, + @import("xmlserializer/xmlserializer.zig").Interfaces, + }); +}; pub const JsThis = Env.JsThis; pub const JsObject = Env.JsObject; pub const Callback = Env.Callback; -pub const Env = js.Env(*SessionState, Interfaces{}); +pub const Env = js.Env(*SessionState, WebApis); pub const Global = @import("html/window.zig").Window; pub const SessionState = struct { diff --git a/src/runtime/js.zig b/src/runtime/js.zig index 8ce3889d..f4822987 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -57,8 +57,8 @@ pub const Platform = struct { // The `S` parameter is arbitrary state. When we start an Executor, an instance // of S must be given. This instance is available to any Zig binding. // The `types` parameter is a tuple of Zig structures we want to bind to V8. -pub fn Env(comptime S: type, comptime types: anytype) type { - const Types = @typeInfo(@TypeOf(types)).@"struct".fields; +pub fn Env(comptime S: type, comptime WebApis: type) type { + const Types = @typeInfo(WebApis.Interfaces).@"struct".fields; // Imagine we have a type Cat which has a getter: // @@ -97,14 +97,14 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // TypeLookup. But we put it here, early, so that the rest of the // code doesn't have to worry about checking if Struct.prototype is // a pointer. - const Struct = @field(types, s.name); + const Struct = s.defaultValue().?; if (@hasDecl(Struct, "prototype") and @typeInfo(Struct.prototype) != .pointer) { @compileError(std.fmt.comptimePrint("Prototype '{s}' for type '{s} must be a pointer", .{ @typeName(Struct.prototype), @typeName(Struct) })); } const subtype: ?SubType = if (@hasDecl(Struct, "subtype")) Struct.subtype else null; - const R = Receiver(@field(types, s.name)); + const R = Receiver(Struct); fields[i] = .{ .name = @typeName(R), .type = TypeMeta, @@ -141,7 +141,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type { const TYPE_LOOKUP = TypeLookup{}; for (Types, 0..) |s, i| { var prototype_index = i; - const Struct = @field(types, s.name); + const Struct = s.defaultValue().?; if (@hasDecl(Struct, "prototype")) { const TI = @typeInfo(Struct.prototype); const proto_name = @typeName(Receiver(TI.pointer.child)); @@ -225,13 +225,13 @@ pub fn Env(comptime S: type, comptime types: anytype) type { const templates = &env.templates; inline for (Types, 0..) |s, i| { @setEvalBranchQuota(10_000); - templates[i] = v8.Persistent(v8.FunctionTemplate).init(isolate, generateClass(@field(types, s.name), isolate)).castToFunctionTemplate(); + templates[i] = v8.Persistent(v8.FunctionTemplate).init(isolate, generateClass(s.defaultValue().?, isolate)).castToFunctionTemplate(); } // Above, we've created all our our FunctionTemplates. Now that we // have them all, we can hook up the prototypes. inline for (Types, 0..) |s, i| { - const Struct = @field(types, s.name); + const Struct = s.defaultValue().?; if (@hasDecl(Struct, "prototype")) { const TI = @typeInfo(Struct.prototype); const proto_name = @typeName(Receiver(TI.pointer.child)); @@ -359,7 +359,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // are now going to get associated with our global instance. const templates = &self.env.templates; inline for (Types, 0..) |s, i| { - const Struct = @field(types, s.name); + const Struct = s.defaultValue().?; const class_name = v8.String.initUtf8(isolate, comptime classNameForStruct(Struct)); global_template.set(class_name.toName(), templates[i], v8.PropertyAttribute.None); } @@ -386,7 +386,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // https://groups.google.com/g/v8-users/c/qAQQBmbi--8 // TODO: see if newer V8 engines have a way around this. inline for (Types, 0..) |s, i| { - const Struct = @field(types, s.name); + const Struct = s.defaultValue().?; if (@hasDecl(Struct, "prototype")) { const proto_type = Receiver(@typeInfo(Struct.prototype).pointer.child); @@ -462,7 +462,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type { // NOTE: there is no way in v8 to subclass the Error built-in type // TODO: this is an horrible hack inline for (Types) |s| { - const Struct = @field(types, s.name); + const Struct = s.defaultValue().?; if (@hasDecl(Struct, "ErrorSet")) { const script = comptime classNameForStruct(Struct) ++ ".prototype.__proto__ = Error.prototype"; _ = try scope.exec(script, "errorSubclass"); diff --git a/src/runtime/testing.zig b/src/runtime/testing.zig index 1f59b046..57550879 100644 --- a/src/runtime/testing.zig +++ b/src/runtime/testing.zig @@ -26,7 +26,7 @@ pub const allocator = std.testing.allocator; // browser.Env or the browser.SessionState pub fn Runner(comptime State: type, comptime Global: type, comptime types: anytype) type { const AdjustedTypes = if (Global == void) generate.Tuple(.{ types, DefaultGlobal }) else types; - const Env = js.Env(State, AdjustedTypes{}); + const Env = js.Env(State, struct {pub const Interfaces = AdjustedTypes;}); return struct { env: *Env,