Skip to content

Commit 615453a

Browse files
committed
Change TypeLookup values from simple index (usize) to a TypeMeta
TypeMeta constains the index and the subtype. This allows retrieving the subtype based on the actual value being bound, as opposed to the struct type. (I.e. it returns the correct subtype when a Zig class is a proxy for another using the Self declaration). Internally store the subtype as an enum. Reduces @sizeof(TaggedAnyOpaque) from 32 to 16. Finally, sub_type renamed to subtype for consistency with v8.
1 parent 1c08b3e commit 615453a

File tree

3 files changed

+66
-20
lines changed

3 files changed

+66
-20
lines changed

src/browser/html/document.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub const HTMLDocument = struct {
3636
pub const Self = parser.DocumentHTML;
3737
pub const prototype = *Document;
3838

39-
pub const sub_type = "node";
39+
pub const subtype = "node";
4040

4141
// JS funcs
4242
// --------

src/runtime/js.zig

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ const std = @import("std");
2020
const builtin = @import("builtin");
2121
const v8 = @import("v8");
2222

23+
const SubType = @import("subtype.zig").SubType;
24+
2325
const Allocator = std.mem.Allocator;
2426
const ArenaAllocator = std.heap.ArenaAllocator;
2527

@@ -72,14 +74,14 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
7274
// that looks like:
7375
//
7476
// const TypeLookup = struct {
75-
// comptime cat: usize = 0,
76-
// comptime owner: usize = 1,
77+
// comptime cat: usize = TypeMeta{.index = 0, ...},
78+
// comptime owner: usize = TypeMeta{.index = 1, ...},
7779
// ...
7880
// }
7981
//
8082
// So to get the template index of `owner`, we can do:
8183
//
82-
// const index_id = @field(type_lookup, @typeName(@TypeOf(res));
84+
// const index_id = @field(type_lookup, @typeName(@TypeOf(res)).index;
8385
//
8486
const TypeLookup = comptime blk: {
8587
var fields: [Types.len]std.builtin.Type.StructField = undefined;
@@ -94,13 +96,16 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
9496
@compileError(std.fmt.comptimePrint("Prototype '{s}' for type '{s} must be a pointer", .{ @typeName(Struct.prototype), @typeName(Struct) }));
9597
}
9698

99+
const subtype: ?SubType =
100+
if (@hasDecl(Struct, "subtype")) std.meta.stringToEnum(SubType, Struct.subtype) else null;
101+
97102
const R = Receiver(@field(types, s.name));
98103
fields[i] = .{
99104
.name = @typeName(R),
100-
.type = usize,
105+
.type = TypeMeta,
101106
.is_comptime = true,
102107
.alignment = @alignOf(usize),
103-
.default_value_ptr = @ptrCast(&i),
108+
.default_value_ptr = &TypeMeta{ .index = i, .subtype = subtype },
104109
};
105110
}
106111
break :blk @Type(.{ .@"struct" = .{
@@ -135,7 +140,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
135140
if (@hasDecl(Struct, "prototype")) {
136141
const TI = @typeInfo(Struct.prototype);
137142
const proto_name = @typeName(Receiver(TI.pointer.child));
138-
prototype_index = @field(TYPE_LOOKUP, proto_name);
143+
prototype_index = @field(TYPE_LOOKUP, proto_name).index;
139144
}
140145
table[i] = prototype_index;
141146
}
@@ -158,7 +163,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
158163
// access to its TunctionTemplate (the thing we need to create an instance
159164
// of it)
160165
// I.e.:
161-
// const index = @field(TYPE_LOOKUP, @typeName(type_name))
166+
// const index = @field(TYPE_LOOKUP, @typeName(type_name)).index
162167
// const template = templates[index];
163168
templates: [Types.len]v8.FunctionTemplate,
164169

@@ -214,7 +219,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
214219
// Populate our templates lookup. generateClass creates the
215220
// v8.FunctionTemplate, which we store in our env.templates.
216221
// The ordering doesn't matter. What matters is that, given a type
217-
// we can get its index via: @field(TYPE_LOOKUP, type_name)
222+
// we can get its index via: @field(TYPE_LOOKUP, type_name).index
218223
const templates = &env.templates;
219224
inline for (Types, 0..) |s, i| {
220225
templates[i] = env.generateClass(@field(types, s.name));
@@ -234,7 +239,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
234239
// Just like we said above, given a type, we can get its
235240
// template index.
236241

237-
const proto_index = @field(TYPE_LOOKUP, proto_name);
242+
const proto_index = @field(TYPE_LOOKUP, proto_name).index;
238243
templates[i].inherit(templates[proto_index]);
239244
}
240245
}
@@ -288,7 +293,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
288293
if (@hasDecl(Global, "prototype")) {
289294
const proto_type = Receiver(@typeInfo(Global.prototype).pointer.child);
290295
const proto_name = @typeName(proto_type);
291-
const proto_index = @field(TYPE_LOOKUP, proto_name);
296+
const proto_index = @field(TYPE_LOOKUP, proto_name).index;
292297
globals.inherit(templates[proto_index]);
293298
}
294299

@@ -309,7 +314,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
309314
@compileError("Type '" ++ @typeName(Struct) ++ "' defines an unknown prototype: " ++ proto_name);
310315
}
311316

312-
const proto_index = @field(TYPE_LOOKUP, proto_name);
317+
const proto_index = @field(TYPE_LOOKUP, proto_name).index;
313318
const proto_obj = templates[proto_index].getFunction(context).toObject();
314319

315320
const self_obj = templates[i].getFunction(context).toObject();
@@ -640,7 +645,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
640645
.one => {
641646
const type_name = @typeName(ptr.child);
642647
if (@hasField(TypeLookup, type_name)) {
643-
const template = templates[@field(TYPE_LOOKUP, type_name)];
648+
const template = templates[@field(TYPE_LOOKUP, type_name).index];
644649
const js_obj = try Executor.mapZigInstanceToJs(context, template, value);
645650
return js_obj.toValue();
646651
}
@@ -676,7 +681,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
676681
.@"struct" => |s| {
677682
const type_name = @typeName(T);
678683
if (@hasField(TypeLookup, type_name)) {
679-
const template = templates[@field(TYPE_LOOKUP, type_name)];
684+
const template = templates[@field(TYPE_LOOKUP, type_name).index];
680685
const js_obj = try Executor.mapZigInstanceToJs(context, template, value);
681686
return js_obj.toValue();
682687
}
@@ -960,10 +965,11 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
960965
// well as any meta data we'll need to use it later.
961966
// See the TaggedAnyOpaque struct for more details.
962967
const tao = try scope_arena.create(TaggedAnyOpaque);
968+
const meta = @field(TYPE_LOOKUP, @typeName(ptr.child));
963969
tao.* = .{
964970
.ptr = value,
965-
.index = @field(TYPE_LOOKUP, @typeName(ptr.child)),
966-
.sub_type = if (@hasDecl(ptr.child, "sub_type")) ptr.child.sub_type else null,
971+
.index = meta.index,
972+
.subtype = meta.subtype,
967973
.offset = if (@typeInfo(ptr.child) != .@"opaque" and @hasField(ptr.child, "proto")) @offsetOf(ptr.child, "proto") else -1,
968974
};
969975

@@ -1345,7 +1351,7 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
13451351

13461352
const op = js_obj.getInternalField(0).castTo(v8.External).get();
13471353
const toa: *TaggedAnyOpaque = @alignCast(@ptrCast(op));
1348-
const expected_type_index = @field(TYPE_LOOKUP, type_name);
1354+
const expected_type_index = @field(TYPE_LOOKUP, type_name).index;
13491355

13501356
var type_index = toa.index;
13511357
if (type_index == expected_type_index) {
@@ -1379,6 +1385,26 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
13791385
};
13801386
}
13811387

1388+
// We'll create a struct with all the types we want to bind to JavaScript. The
1389+
// fields for this struct will be the type names. The values, will be an
1390+
// instance of this struct.
1391+
// const TypeLookup = struct {
1392+
// comptime cat: usize = TypeMeta{.index = 0, subtype = null},
1393+
// comptime owner: usize = TypeMeta{.index = 1, subtype = .array}.
1394+
// ...
1395+
// }
1396+
// This is essentially meta data for each type.
1397+
const TypeMeta = struct {
1398+
// Every type is given a unique index. That index is used to lookup various
1399+
// things, i.e. the prototype chain.
1400+
index: usize,
1401+
1402+
// We store the type's subtype here, so that when we create an instance of
1403+
// the type, and bind it to JavaScript, we can store the subtype along with
1404+
// the created TaggedAnyOpaque.s
1405+
subtype: ?SubType,
1406+
};
1407+
13821408
fn isEmpty(comptime T: type) bool {
13831409
return @typeInfo(T) != .@"opaque" and @sizeOf(T) == 0;
13841410
}
@@ -2204,7 +2230,7 @@ const TaggedAnyOpaque = struct {
22042230
// V8 will give us a Value and ask us for the subtype. From the v8.Value we
22052231
// can get a v8.Object, and from the v8.Object, we can get out TaggedAnyOpaque
22062232
// which is where we store the subtype.
2207-
sub_type: ?[*c]const u8,
2233+
subtype: ?SubType,
22082234
};
22092235

22102236
fn valueToString(allocator: Allocator, value: v8.Value, isolate: v8.Isolate, context: v8.Context) ![]u8 {
@@ -2263,7 +2289,7 @@ pub export fn v8_inspector__Client__IMPL__valueSubtype(
22632289
c_value: *const v8.C_Value,
22642290
) callconv(.C) [*c]const u8 {
22652291
const external_entry = getTaggedAnyOpaque(.{ .handle = c_value }) orelse return null;
2266-
return if (external_entry.sub_type) |st| st else null;
2292+
return if (external_entry.subtype) |st| @tagName(st) else null;
22672293
}
22682294

22692295
// Same as valueSubType above, but for the optional description field.
@@ -2280,7 +2306,7 @@ pub export fn v8_inspector__Client__IMPL__descriptionForValueSubtype(
22802306
// We _must_ include a non-null description in order for the subtype value
22812307
// to be included. Besides that, I don't know if the value has any meaning
22822308
const external_entry = getTaggedAnyOpaque(.{ .handle = c_value }) orelse return null;
2283-
return if (external_entry.sub_type == null) null else "";
2309+
return if (external_entry.subtype == null) null else "";
22842310
}
22852311

22862312
fn getTaggedAnyOpaque(value: v8.Value) ?*TaggedAnyOpaque {

src/runtime/subtype.zig

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
pub const SubType = enum {
2+
@"error",
3+
array,
4+
arraybuffer,
5+
dataview,
6+
date,
7+
generator,
8+
iterator,
9+
map,
10+
node,
11+
promise,
12+
proxy,
13+
regexp,
14+
set,
15+
typedarray,
16+
wasmvalue,
17+
weakmap,
18+
weakset,
19+
webassemblymemory,
20+
};

0 commit comments

Comments
 (0)