mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-14 15:28:57 +00:00
Merge pull request #1219 from lightpanda-io/nikneym/rework-types
Refactor `types.zig`
This commit is contained in:
@@ -409,9 +409,8 @@ pub fn zigValueToJs(self: *Context, value: anytype) !v8.Value {
|
||||
},
|
||||
.pointer => |ptr| switch (ptr.size) {
|
||||
.one => {
|
||||
const type_name = @typeName(ptr.child);
|
||||
if (@hasField(types.Lookup, type_name)) {
|
||||
const template = self.templates[@field(types.LOOKUP, type_name)];
|
||||
if (types.has(ptr.child)) {
|
||||
const template = self.templates[types.getId(ptr.child)];
|
||||
const js_obj = try self.mapZigInstanceToJs(template, value);
|
||||
return js_obj.toValue();
|
||||
}
|
||||
@@ -445,9 +444,8 @@ pub fn zigValueToJs(self: *Context, value: anytype) !v8.Value {
|
||||
else => {},
|
||||
},
|
||||
.@"struct" => |s| {
|
||||
const type_name = @typeName(T);
|
||||
if (@hasField(types.Lookup, type_name)) {
|
||||
const template = self.templates[@field(types.LOOKUP, type_name)];
|
||||
if (types.has(T)) {
|
||||
const template = self.templates[types.getId(T)];
|
||||
const js_obj = try self.mapZigInstanceToJs(template, value);
|
||||
return js_obj.toValue();
|
||||
}
|
||||
@@ -583,8 +581,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_or_template: anytype, value: an
|
||||
// well as any meta data we'll need to use it later.
|
||||
// See the TaggedAnyOpaque struct for more details.
|
||||
const tao = try arena.create(TaggedAnyOpaque);
|
||||
const meta_index = @field(types.LOOKUP, @typeName(ptr.child));
|
||||
const meta = self.meta_lookup[meta_index];
|
||||
const meta = self.meta_lookup[types.getId(ptr.child)];
|
||||
|
||||
tao.* = .{
|
||||
.ptr = value,
|
||||
@@ -664,7 +661,7 @@ pub fn jsValueToZig(self: *Context, comptime named_function: NamedFunction, comp
|
||||
if (!js_value.isObject()) {
|
||||
return error.InvalidArgument;
|
||||
}
|
||||
if (@hasField(types.Lookup, @typeName(ptr.child))) {
|
||||
if (types.has(ptr.child)) {
|
||||
const js_obj = js_value.castTo(v8.Object);
|
||||
return self.typeTaggedAnyOpaque(named_function, *types.Receiver(ptr.child), js_obj);
|
||||
}
|
||||
@@ -1454,14 +1451,13 @@ pub fn typeTaggedAnyOpaque(self: *const Context, comptime named_function: NamedF
|
||||
return error.InvalidArgument;
|
||||
}
|
||||
|
||||
const type_name = @typeName(T);
|
||||
if (@hasField(types.Lookup, type_name) == false) {
|
||||
if (!types.has(T)) {
|
||||
@compileError(named_function.full_name ++ "has an unknown Zig type: " ++ @typeName(R));
|
||||
}
|
||||
|
||||
const op = js_obj.getInternalField(0).castTo(v8.External).get();
|
||||
const tao: *TaggedAnyOpaque = @ptrCast(@alignCast(op));
|
||||
const expected_type_index = @field(types.LOOKUP, type_name);
|
||||
const expected_type_index = types.getId(T);
|
||||
|
||||
var type_index = tao.index;
|
||||
if (type_index == expected_type_index) {
|
||||
@@ -1489,7 +1485,7 @@ pub fn typeTaggedAnyOpaque(self: *const Context, comptime named_function: NamedF
|
||||
total_offset += @intCast(proto_offset);
|
||||
}
|
||||
|
||||
const prototype_index = types.PROTOTYPE_TABLE[type_index];
|
||||
const prototype_index = types.PrototypeTable[type_index];
|
||||
if (prototype_index == expected_type_index) {
|
||||
return @ptrFromInt(base_ptr + total_offset);
|
||||
}
|
||||
@@ -1582,7 +1578,7 @@ fn probeJsValueToZig(self: *Context, comptime named_function: NamedFunction, com
|
||||
if (!js_value.isObject()) {
|
||||
return .{ .invalid = {} };
|
||||
}
|
||||
if (@hasField(types.Lookup, @typeName(ptr.child))) {
|
||||
if (types.has(ptr.child)) {
|
||||
const js_obj = js_value.castTo(v8.Object);
|
||||
// There's a bit of overhead in doing this, so instead
|
||||
// of having a version of typeTaggedAnyOpaque which
|
||||
|
||||
@@ -111,16 +111,14 @@ pub fn init(allocator: Allocator, platform: *const Platform, _: Opts) !*Env {
|
||||
const Struct = s.defaultValue().?;
|
||||
if (@hasDecl(Struct, "prototype")) {
|
||||
const TI = @typeInfo(Struct.prototype);
|
||||
const proto_name = @typeName(types.Receiver(TI.pointer.child));
|
||||
if (@hasField(types.Lookup, proto_name) == false) {
|
||||
@compileError(std.fmt.comptimePrint("Prototype '{s}' for '{s}' is undefined", .{ proto_name, @typeName(Struct) }));
|
||||
const ProtoType = types.Receiver(TI.pointer.child);
|
||||
if (!types.has(ProtoType)) {
|
||||
@compileError(std.fmt.comptimePrint("Prototype '{s}' for '{s}' is undefined", .{ @typeName(ProtoType), @typeName(Struct) }));
|
||||
}
|
||||
// Hey, look! This is our first real usage of the types.LOOKUP.
|
||||
// Hey, look! This is our first real usage of the `types.Index`.
|
||||
// Just like we said above, given a type, we can get its
|
||||
// template index.
|
||||
|
||||
const proto_index = @field(types.LOOKUP, proto_name);
|
||||
templates[i].inherit(templates[proto_index]);
|
||||
templates[i].inherit(templates[types.getId(ProtoType)]);
|
||||
}
|
||||
|
||||
// while we're here, let's populate our meta lookup
|
||||
|
||||
@@ -104,10 +104,8 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool, global_cal
|
||||
// though it's also a Window, we need to set the prototype for this
|
||||
// specific instance of the the Window.
|
||||
if (@hasDecl(Global, "prototype")) {
|
||||
const proto_type = types.Receiver(@typeInfo(Global.prototype).pointer.child);
|
||||
const proto_name = @typeName(proto_type);
|
||||
const proto_index = @field(types.LOOKUP, proto_name);
|
||||
js_global.inherit(templates[proto_index]);
|
||||
const ProtoType = types.Receiver(@typeInfo(Global.prototype).pointer.child);
|
||||
js_global.inherit(templates[types.getId(ProtoType)]);
|
||||
}
|
||||
|
||||
const context_local = v8.Context.init(isolate, global_template, null);
|
||||
@@ -123,14 +121,12 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool, global_cal
|
||||
const Struct = s.defaultValue().?;
|
||||
|
||||
if (@hasDecl(Struct, "prototype")) {
|
||||
const proto_type = types.Receiver(@typeInfo(Struct.prototype).pointer.child);
|
||||
const proto_name = @typeName(proto_type);
|
||||
if (@hasField(types.Lookup, proto_name) == false) {
|
||||
@compileError("Type '" ++ @typeName(Struct) ++ "' defines an unknown prototype: " ++ proto_name);
|
||||
const ProtoType = types.Receiver(@typeInfo(Struct.prototype).pointer.child);
|
||||
if (!types.has(ProtoType)) {
|
||||
@compileError("Type '" ++ @typeName(Struct) ++ "' defines an unknown prototype: " ++ @typeName(ProtoType));
|
||||
}
|
||||
|
||||
const proto_index = @field(types.LOOKUP, proto_name);
|
||||
const proto_obj = templates[proto_index].getFunction(v8_context).toObject();
|
||||
const proto_obj = templates[types.getId(ProtoType)].getFunction(v8_context).toObject();
|
||||
|
||||
const self_obj = templates[i].getFunction(v8_context).toObject();
|
||||
_ = self_obj.setPrototype(v8_context, proto_obj);
|
||||
|
||||
@@ -27,104 +27,127 @@ const Interfaces = generate.Tuple(.{
|
||||
|
||||
pub const Types = @typeInfo(Interfaces).@"struct".fields;
|
||||
|
||||
// Imagine we have a type Cat which has a getter:
|
||||
//
|
||||
// fn get_owner(self: *Cat) *Owner {
|
||||
// return self.owner;
|
||||
// }
|
||||
//
|
||||
// When we execute caller.getter, we'll end up doing something like:
|
||||
// const res = @call(.auto, Cat.get_owner, .{cat_instance});
|
||||
//
|
||||
// How do we turn `res`, which is an *Owner, into something we can return
|
||||
// to v8? We need the ObjectTemplate associated with Owner. How do we
|
||||
// get that? Well, we store all the ObjectTemplates in an array that's
|
||||
// tied to env. So we do something like:
|
||||
//
|
||||
// env.templates[index_of_owner].initInstance(...);
|
||||
//
|
||||
// But how do we get that `index_of_owner`? `Lookup` is a struct
|
||||
// that looks like:
|
||||
//
|
||||
// const Lookup = struct {
|
||||
// comptime cat: usize = 0,
|
||||
// comptime owner: usize = 1,
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// So to get the template index of `owner`, we can do:
|
||||
//
|
||||
// const index_id = @field(type_lookup, @typeName(@TypeOf(res));
|
||||
//
|
||||
pub const Lookup = blk: {
|
||||
var fields: [Types.len]std.builtin.Type.StructField = undefined;
|
||||
/// Integer type we use for `Index` enum. Can be u8 at min.
|
||||
pub const BackingInt = std.math.IntFittingRange(0, @max(std.math.maxInt(u8), Types.len));
|
||||
|
||||
/// Imagine we have a type `Cat` which has a getter:
|
||||
///
|
||||
/// fn get_owner(self: *Cat) *Owner {
|
||||
/// return self.owner;
|
||||
/// }
|
||||
///
|
||||
/// When we execute `caller.getter`, we'll end up doing something like:
|
||||
///
|
||||
/// const res = @call(.auto, Cat.get_owner, .{cat_instance});
|
||||
///
|
||||
/// How do we turn `res`, which is an *Owner, into something we can return
|
||||
/// to v8? We need the ObjectTemplate associated with Owner. How do we
|
||||
/// get that? Well, we store all the ObjectTemplates in an array that's
|
||||
/// tied to env. So we do something like:
|
||||
///
|
||||
/// env.templates[index_of_owner].initInstance(...);
|
||||
///
|
||||
/// But how do we get that `index_of_owner`? `Index` is an enum
|
||||
/// that looks like:
|
||||
///
|
||||
/// pub const Index = enum(BackingInt) {
|
||||
/// cat = 0,
|
||||
/// owner = 1,
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// (`BackingInt` is calculated at comptime regarding to interfaces we have)
|
||||
/// So to get the template index of `owner`, simply do:
|
||||
///
|
||||
/// const index_id = types.getId(@TypeOf(res));
|
||||
pub const Index = blk: {
|
||||
var fields: [Types.len]std.builtin.Type.EnumField = undefined;
|
||||
for (Types, 0..) |s, i| {
|
||||
|
||||
// This prototype type check has nothing to do with building our
|
||||
// Lookup. 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 = 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) }));
|
||||
}
|
||||
|
||||
fields[i] = .{
|
||||
.name = @typeName(Receiver(Struct)),
|
||||
.type = usize,
|
||||
.is_comptime = true,
|
||||
.alignment = @alignOf(usize),
|
||||
.default_value_ptr = &i,
|
||||
};
|
||||
fields[i] = .{ .name = @typeName(Receiver(Struct)), .value = i };
|
||||
}
|
||||
break :blk @Type(.{ .@"struct" = .{
|
||||
.layout = .auto,
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
.fields = &fields,
|
||||
} });
|
||||
|
||||
break :blk @Type(.{
|
||||
.@"enum" = .{
|
||||
.fields = &fields,
|
||||
.tag_type = BackingInt,
|
||||
.is_exhaustive = true,
|
||||
.decls = &.{},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
pub const LOOKUP = Lookup{};
|
||||
/// Returns a boolean indicating if a type exist in the `Index`.
|
||||
pub inline fn has(t: type) bool {
|
||||
return @hasField(Index, @typeName(t));
|
||||
}
|
||||
|
||||
// Creates a list where the index of a type contains its prototype index
|
||||
// const Animal = struct{};
|
||||
// const Cat = struct{
|
||||
// pub const prototype = *Animal;
|
||||
// };
|
||||
//
|
||||
// Would create an array: [0, 0]
|
||||
// Animal, at index, 0, has no prototype, so we set it to itself
|
||||
// Cat, at index 1, has an Animal prototype, so we set it to 0.
|
||||
//
|
||||
// When we're trying to pass an argument to a Zig function, we'll know the
|
||||
// target type (the function parameter type), and we'll have a
|
||||
// TaggedAnyOpaque which will have the index of the type of that parameter.
|
||||
// We'll use the PROTOTYPE_TABLE to see if the TaggedAnyType should be
|
||||
// cast to a prototype.
|
||||
pub const PROTOTYPE_TABLE = blk: {
|
||||
var table: [Types.len]u16 = undefined;
|
||||
/// Returns the `Index` for the given type.
|
||||
pub inline fn getIndex(t: type) Index {
|
||||
return @field(Index, @typeName(t));
|
||||
}
|
||||
|
||||
/// Returns the ID for the given type.
|
||||
pub inline fn getId(t: type) BackingInt {
|
||||
return @intFromEnum(getIndex(t));
|
||||
}
|
||||
|
||||
/// Creates a list where the index of a type contains its prototype index.
|
||||
/// const Animal = struct{};
|
||||
/// const Cat = struct{
|
||||
/// pub const prototype = *Animal;
|
||||
/// };
|
||||
///
|
||||
/// Would create an array of indexes:
|
||||
/// [Index.Animal, Index.Animal]
|
||||
///
|
||||
/// `Animal`, at index, 0, has no prototype, so we set it to itself.
|
||||
/// `Cat`, at index 1, has an `Animal` prototype, so we set it to `Animal`.
|
||||
///
|
||||
/// When we're trying to pass an argument to a Zig function, we'll know the
|
||||
/// target type (the function parameter type), and we'll have a
|
||||
/// TaggedAnyOpaque which will have the index of the type of that parameter.
|
||||
/// We'll use the `PrototypeTable` to see if the TaggedAnyType should be
|
||||
/// cast to a prototype.
|
||||
pub const PrototypeTable = blk: {
|
||||
var table: [Types.len]BackingInt = undefined;
|
||||
for (Types, 0..) |s, i| {
|
||||
var prototype_index = i;
|
||||
const Struct = s.defaultValue().?;
|
||||
if (@hasDecl(Struct, "prototype")) {
|
||||
const TI = @typeInfo(Struct.prototype);
|
||||
const proto_name = @typeName(Receiver(TI.pointer.child));
|
||||
prototype_index = @field(LOOKUP, proto_name);
|
||||
}
|
||||
table[i] = prototype_index;
|
||||
table[i] = proto_index: {
|
||||
if (@hasDecl(Struct, "prototype")) {
|
||||
const prototype_field = @field(Struct, "prototype");
|
||||
// This prototype type check has nothing to do with building our
|
||||
// Lookup. 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.
|
||||
break :proto_index switch (@typeInfo(prototype_field)) {
|
||||
.pointer => |pointer| getId(Receiver(pointer.child)),
|
||||
inline else => @compileError(std.fmt.comptimePrint("Prototype '{s}' for type '{s}' must be a pointer", .{
|
||||
prototype_field,
|
||||
@typeName(Struct),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
break :proto_index i;
|
||||
};
|
||||
}
|
||||
|
||||
break :blk table;
|
||||
};
|
||||
|
||||
// This is essentially meta data for each type. Each is stored in env.meta_lookup
|
||||
// The index for a type can be retrieved via:
|
||||
// const index = @field(TYPE_LOOKUP, @typeName(Receiver(Struct)));
|
||||
// const meta = env.meta_lookup[index];
|
||||
/// This is essentially meta data for each type. Each is stored in `env.meta_lookup`.
|
||||
/// The index for a type can be retrieved via:
|
||||
/// const index = types.getIndex(Receiver(Struct));
|
||||
/// const meta = env.meta_lookup[@intFromEnum(index)];
|
||||
///
|
||||
/// Or:
|
||||
/// const id = types.getId(Receiver(Struct));
|
||||
/// const meta = env.meta_lookup[id];
|
||||
pub const Meta = struct {
|
||||
// Every type is given a unique index. That index is used to lookup various
|
||||
// things, i.e. the prototype chain.
|
||||
index: u16,
|
||||
index: BackingInt,
|
||||
|
||||
// We store the type's subtype here, so that when we create an instance of
|
||||
// the type, and bind it to JavaScript, we can store the subtype along with
|
||||
|
||||
Reference in New Issue
Block a user