@@ -27,104 +27,127 @@ const Interfaces = generate.Tuple(.{
2727
2828pub const Types = @typeInfo (Interfaces ).@"struct" .fields ;
2929
30- // Imagine we have a type Cat which has a getter:
31- //
32- // fn get_owner(self: *Cat) *Owner {
33- // return self.owner;
34- // }
35- //
36- // When we execute caller.getter, we'll end up doing something like:
37- // const res = @call(.auto, Cat.get_owner, .{cat_instance});
38- //
39- // How do we turn `res`, which is an *Owner, into something we can return
40- // to v8? We need the ObjectTemplate associated with Owner. How do we
41- // get that? Well, we store all the ObjectTemplates in an array that's
42- // tied to env. So we do something like:
43- //
44- // env.templates[index_of_owner].initInstance(...);
45- //
46- // But how do we get that `index_of_owner`? `Lookup` is a struct
47- // that looks like:
48- //
49- // const Lookup = struct {
50- // comptime cat: usize = 0,
51- // comptime owner: usize = 1,
52- // ...
53- // }
54- //
55- // So to get the template index of `owner`, we can do:
56- //
57- // const index_id = @field(type_lookup, @typeName(@TypeOf(res));
58- //
59- pub const Lookup = blk : {
60- var fields : [Types .len ]std.builtin.Type.StructField = undefined ;
61- for (Types , 0.. ) | s , i | {
30+ /// Integer type we use for `Index` enum. Can be u8 at min.
31+ pub const BackingInt = std .math .IntFittingRange (0 , @max (std .math .maxInt (u8 ), Types .len ));
6232
63- // This prototype type check has nothing to do with building our
64- // Lookup. But we put it here, early, so that the rest of the
65- // code doesn't have to worry about checking if Struct.prototype is
66- // a pointer.
33+ /// Imagine we have a type `Cat` which has a getter:
34+ ///
35+ /// fn get_owner(self: *Cat) *Owner {
36+ /// return self.owner;
37+ /// }
38+ ///
39+ /// When we execute `caller.getter`, we'll end up doing something like:
40+ ///
41+ /// const res = @call(.auto, Cat.get_owner, .{cat_instance});
42+ ///
43+ /// How do we turn `res`, which is an *Owner, into something we can return
44+ /// to v8? We need the ObjectTemplate associated with Owner. How do we
45+ /// get that? Well, we store all the ObjectTemplates in an array that's
46+ /// tied to env. So we do something like:
47+ ///
48+ /// env.templates[index_of_owner].initInstance(...);
49+ ///
50+ /// But how do we get that `index_of_owner`? `Index` is an enum
51+ /// that looks like:
52+ ///
53+ /// pub const Index = enum(BackingInt) {
54+ /// cat = 0,
55+ /// owner = 1,
56+ /// ...
57+ /// }
58+ ///
59+ /// (`BackingInt` is calculated at comptime regarding to interfaces we have)
60+ /// So to get the template index of `owner`, simply do:
61+ ///
62+ /// const index_id = types.getId(@TypeOf(res));
63+ pub const Index = blk : {
64+ var fields : [Types .len ]std.builtin.Type.EnumField = undefined ;
65+ for (Types , 0.. ) | s , i | {
6766 const Struct = s .defaultValue ().? ;
68- if (@hasDecl (Struct , "prototype" ) and @typeInfo (Struct .prototype ) != .pointer ) {
69- @compileError (std .fmt .comptimePrint ("Prototype '{s}' for type '{s} must be a pointer" , .{ @typeName (Struct .prototype ), @typeName (Struct ) }));
70- }
71-
72- fields [i ] = .{
73- .name = @typeName (Receiver (Struct )),
74- .type = usize ,
75- .is_comptime = true ,
76- .alignment = @alignOf (usize ),
77- .default_value_ptr = & i ,
78- };
67+ fields [i ] = .{ .name = @typeName (Receiver (Struct )), .value = i };
7968 }
80- break :blk @Type (.{ .@"struct" = .{
81- .layout = .auto ,
82- .decls = &.{},
83- .is_tuple = false ,
84- .fields = & fields ,
85- } });
69+
70+ break :blk @Type (.{
71+ .@"enum" = .{
72+ .fields = & fields ,
73+ .tag_type = BackingInt ,
74+ .is_exhaustive = true ,
75+ .decls = &.{},
76+ },
77+ });
8678};
8779
88- pub const LOOKUP = Lookup {};
80+ /// Returns a boolean indicating if a type exist in the `Index`.
81+ pub inline fn has (t : type ) bool {
82+ return @hasField (Index , @typeName (t ));
83+ }
8984
90- // Creates a list where the index of a type contains its prototype index
91- // const Animal = struct{};
92- // const Cat = struct{
93- // pub const prototype = *Animal;
94- // };
95- //
96- // Would create an array: [0, 0]
97- // Animal, at index, 0, has no prototype, so we set it to itself
98- // Cat, at index 1, has an Animal prototype, so we set it to 0.
99- //
100- // When we're trying to pass an argument to a Zig function, we'll know the
101- // target type (the function parameter type), and we'll have a
102- // TaggedAnyOpaque which will have the index of the type of that parameter.
103- // We'll use the PROTOTYPE_TABLE to see if the TaggedAnyType should be
104- // cast to a prototype.
105- pub const PROTOTYPE_TABLE = blk : {
106- var table : [Types .len ]u16 = undefined ;
85+ /// Returns the `Index` for the given type.
86+ pub inline fn getIndex (t : type ) Index {
87+ return @field (Index , @typeName (t ));
88+ }
89+
90+ /// Returns the ID for the given type.
91+ pub inline fn getId (t : type ) BackingInt {
92+ return @intFromEnum (getIndex (t ));
93+ }
94+
95+ /// Creates a list where the index of a type contains its prototype index.
96+ /// const Animal = struct{};
97+ /// const Cat = struct{
98+ /// pub const prototype = *Animal;
99+ /// };
100+ ///
101+ /// Would create an array of indexes:
102+ /// [Index.Animal, Index.Animal]
103+ ///
104+ /// `Animal`, at index, 0, has no prototype, so we set it to itself.
105+ /// `Cat`, at index 1, has an `Animal` prototype, so we set it to `Animal`.
106+ ///
107+ /// When we're trying to pass an argument to a Zig function, we'll know the
108+ /// target type (the function parameter type), and we'll have a
109+ /// TaggedAnyOpaque which will have the index of the type of that parameter.
110+ /// We'll use the `PrototypeTable` to see if the TaggedAnyType should be
111+ /// cast to a prototype.
112+ pub const PrototypeTable = blk : {
113+ var table : [Types .len ]BackingInt = undefined ;
107114 for (Types , 0.. ) | s , i | {
108- var prototype_index = i ;
109115 const Struct = s .defaultValue ().? ;
110- if (@hasDecl (Struct , "prototype" )) {
111- const TI = @typeInfo (Struct .prototype );
112- const proto_name = @typeName (Receiver (TI .pointer .child ));
113- prototype_index = @field (LOOKUP , proto_name );
114- }
115- table [i ] = prototype_index ;
116+ table [i ] = proto_index : {
117+ if (@hasDecl (Struct , "prototype" )) {
118+ const prototype_field = @field (Struct , "prototype" );
119+ // This prototype type check has nothing to do with building our
120+ // Lookup. But we put it here, early, so that the rest of the
121+ // code doesn't have to worry about checking if Struct.prototype is
122+ // a pointer.
123+ break :proto_index switch (@typeInfo (prototype_field )) {
124+ .pointer = > | pointer | getId (Receiver (pointer .child )),
125+ inline else = > @compileError (std .fmt .comptimePrint ("Prototype '{s}' for type '{s}' must be a pointer" , .{
126+ prototype_field ,
127+ @typeName (Struct ),
128+ })),
129+ };
130+ }
131+
132+ break :proto_index i ;
133+ };
116134 }
135+
117136 break :blk table ;
118137};
119138
120- // This is essentially meta data for each type. Each is stored in env.meta_lookup
121- // The index for a type can be retrieved via:
122- // const index = @field(TYPE_LOOKUP, @typeName(Receiver(Struct)));
123- // const meta = env.meta_lookup[index];
139+ /// This is essentially meta data for each type. Each is stored in `env.meta_lookup`.
140+ /// The index for a type can be retrieved via:
141+ /// const index = types.getIndex(Receiver(Struct));
142+ /// const meta = env.meta_lookup[@intFromEnum(index)];
143+ ///
144+ /// Or:
145+ /// const id = types.getId(Receiver(Struct));
146+ /// const meta = env.meta_lookup[id];
124147pub const Meta = struct {
125148 // Every type is given a unique index. That index is used to lookup various
126149 // things, i.e. the prototype chain.
127- index : u16 ,
150+ index : BackingInt ,
128151
129152 // We store the type's subtype here, so that when we create an instance of
130153 // the type, and bind it to JavaScript, we can store the subtype along with
0 commit comments