aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/bindings/header-gen.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/javascript/jsc/bindings/header-gen.zig')
-rw-r--r--src/javascript/jsc/bindings/header-gen.zig368
1 files changed, 368 insertions, 0 deletions
diff --git a/src/javascript/jsc/bindings/header-gen.zig b/src/javascript/jsc/bindings/header-gen.zig
new file mode 100644
index 000000000..e0ea96a21
--- /dev/null
+++ b/src/javascript/jsc/bindings/header-gen.zig
@@ -0,0 +1,368 @@
+const std = @import("std");
+const Dir = std.fs.Dir;
+const FnMeta = std.builtin.TypeInfo.Fn;
+const FnDecl = std.builtin.TypeInfo.Declaration.Data.FnDecl;
+const StructMeta = std.builtin.TypeInfo.Struct;
+const EnumMeta = std.builtin.TypeInfo.Enum;
+const UnionMeta = std.builtin.TypeInfo.Union;
+const warn = std.debug.warn;
+
+pub const C_Generator = struct {
+ file: std.fs.File,
+ filebase: []const u8,
+ const Self = @This();
+
+ pub fn init(comptime src_file: []const u8, file: std.fs.File) Self {
+ var res = Self{ .file = file, .filebase = src_file };
+
+ file.writeAll("\n/**** " ++ src_file ++ " /*****/\n\n") catch unreachable;
+ return res;
+ }
+
+ pub fn deinit(self: *Self) void {
+ self.file.writeAll("\n/***** ") catch unreachable;
+ self.file.writeAll(self.filebase) catch unreachable;
+ self.file.writeAll(" *****/") catch unreachable;
+ }
+
+ pub fn gen_func(self: *Self, comptime name: []const u8, comptime func: FnDecl, comptime meta: FnMeta, comptime arg_names: []const []const u8) void {
+ switch (meta.calling_convention) {
+ .Naked => self.write("__attribute__((naked)) "),
+ .Stdcall => self.write("__attribute__((stdcall)) "),
+ .Fastcall => self.write("__attribute__((fastcall)) "),
+ .Thiscall => self.write("__attribute__((thiscall)) "),
+ else => {},
+ }
+
+ self.write("extern \"C\" ");
+ self.writeType(func.return_type);
+ self.write(" " ++ name ++ "(");
+
+ inline for (meta.args) |arg, i| {
+ self.writeType(arg.arg_type.?);
+ if (func.arg_names.len > i) {
+ self.write(comptime arg_names[i]);
+ } else {
+ self.write(comptime std.fmt.comptimePrint(" arg{d}", .{i}));
+ }
+
+ //TODO: Figure out how to get arg names; for now just do arg0..argN
+ if (i != meta.args.len - 1)
+ self.write(", ");
+ }
+
+ self.write(");\n");
+ }
+
+ pub fn gen_struct(self: *Self, comptime name: []const u8, comptime meta: StructMeta) void {
+ self.write("typedef struct ");
+
+ if (meta.layout == .Packed)
+ self.write("__attribute__((__packed__)) ");
+
+ self.write(name ++ " {\n");
+
+ inline for (meta.fields) |field| {
+ self.write(" ");
+
+ const info = @typeInfo(field.field_type);
+
+ if (info == .Array) {
+ self.writeType(info.Array.child);
+ } else {
+ self.writeType(field.field_type);
+ }
+
+ self.write(" " ++ field.name);
+
+ if (info == .Array) {
+ _ = self.file.writer().print("[{}]", .{info.Array.len}) catch unreachable;
+ }
+
+ self.write(";\n");
+ }
+ self.write("} " ++ name ++ "_t;\n\n");
+ }
+
+ pub fn gen_enum(self: *Self, comptime name: []const u8, comptime meta: EnumMeta) void {
+ self.write("enum " ++ name ++ " {\n");
+
+ comptime var last = 0;
+ inline for (meta.fields) |field, i| {
+ self.write(" " ++ field.name);
+
+ // if field value is unexpected/custom, manually define it
+ if ((i == 0 and field.value != 0) or (i > 0 and field.value > last + 1)) {
+ _ = self.file.writer().print(" = {}", .{field.value}) catch unreachable;
+ }
+
+ self.write(",\n");
+
+ last = field.value;
+ }
+
+ self.write("};\n\n");
+ }
+
+ pub fn gen_union(self: *Self, comptime name: []const u8, comptime meta: UnionMeta) void {
+ self.write("typedef union ");
+
+ self.write(name ++ " {\n");
+
+ inline for (meta.fields) |field| {
+ self.write(" ");
+ self.writeType(field.field_type);
+ self.write(" " ++ field.name ++ ";\n");
+ }
+ self.write("} " ++ name ++ "_t;\n\n");
+ }
+
+ fn writeType(self: *Self, comptime T: type) void {
+ const TT = if (@typeInfo(T) == .Pointer) @typeInfo(T).Pointer.child else T;
+
+ if (comptime std.meta.trait.hasDecls(TT, .{"C"}) and std.meta.trait.hasDecls(TT.C, .{"name"})) {
+ writeType(self, TT.C);
+ if (std.meta.trait.isSingleItemPtr(T)) {
+ write(self, "*");
+ }
+ return;
+ }
+
+ if (comptime std.meta.trait.hasDecls(TT, .{"name"})) {
+ self.write(comptime T.name);
+ if (std.meta.trait.isSingleItemPtr(T)) {
+ write(self, "*");
+ }
+ return;
+ }
+
+ switch (T) {
+ void => self.write("void"),
+ bool => self.write("bool"),
+ usize => self.write("size_t"),
+ isize => self.write("int"),
+ u8 => self.write("uint8_t"),
+ u16 => self.write("uint16_t"),
+ u32 => self.write("uint32_t"),
+ u64 => self.write("uint64_t"),
+ i8 => self.write("int8_t"),
+ i16 => self.write("int16_t"),
+ i24 => self.write("int24_t"),
+ i32 => self.write("int32_t"),
+ i64 => self.write("int64_t"),
+ [*]bool => self.write("bool*"),
+ [*]usize => self.write("size_t*"),
+ [*]isize => self.write("int*"),
+ [*]u8 => self.write("uint8_t*"),
+ [*]u16 => self.write("uint16_t*"),
+ [*]u32 => self.write("uint32_t*"),
+ [*]u64 => self.write("uint64_t*"),
+ [*]i8 => self.write("int8_t*"),
+ [*]i16 => self.write("int16_t*"),
+ [*]i32 => self.write("int32_t*"),
+ [*]i64 => self.write("int64_t*"),
+ else => {
+ const meta = @typeInfo(T);
+ switch (meta) {
+ .Pointer => {
+ const child = meta.Pointer.child;
+ const childmeta = @typeInfo(child);
+ // if (childmeta == .Struct and childmeta.Struct.layout != .Extern) {
+ // self.write("void");
+ // } else {
+ self.writeType(child);
+ // }
+ self.write("*");
+ },
+ .Optional => self.writeType(meta.Optional.child),
+ .Array => @compileError("Handle goofy looking C Arrays in the calling function"),
+ else => self.write(@typeName(T) ++ "_t"),
+ }
+ },
+ }
+ }
+
+ fn write(self: *Self, comptime str: []const u8) void {
+ _ = self.file.writeAll(str) catch {};
+ }
+};
+
+const builtin = std.builtin;
+const TypeInfo = builtin.TypeInfo;
+const Declaration = TypeInfo.Declaration;
+
+const GeneratorInterface = struct {
+ fn init() void {}
+ fn deinit() void {}
+ fn gen_func() void {}
+ fn gen_struct() void {}
+ fn gen_enum() void {}
+ fn gen_union() void {}
+};
+
+fn validateGenerator(comptime Generator: type) void {
+ comptime {
+ const interface = @typeInfo(GeneratorInterface).Struct.decls;
+
+ for (interface) |decl| {
+ if (@hasDecl(Generator, decl.name) == false) {
+ @compileError("Generator: '" ++
+ @typeName(Generator) ++
+ "' is missing function: " ++
+ decl.name);
+ }
+ }
+ }
+}
+
+const NamedStruct = struct {
+ name: []const u8,
+ Type: type,
+};
+
+pub fn getCStruct(comptime T: type) ?NamedStruct {
+ if (!std.meta.trait.isContainer(T) or (std.meta.trait.isSingleItemPtr(T) and !std.meta.trait.isContainer(std.meta.Child(T)))) {
+ return null;
+ }
+
+ inline for (std.meta.declarations(T)) |decl| {
+ if (std.mem.eql(u8, decl.name, "C")) {
+ switch (decl.data) {
+ .Type => |TT| {
+ return NamedStruct{ .Type = TT, .name = @typeName(T) };
+ },
+ else => {},
+ }
+ }
+ }
+
+ return null;
+}
+
+pub fn HeaderGen(comptime import: type, comptime fname: []const u8) type {
+ const all_decls = std.meta.declarations(import);
+
+ return struct {
+ source_file: []const u8 = fname,
+
+ const Self = @This();
+
+ pub fn init() Self {
+ return Self{};
+ }
+
+ pub fn processDecls(
+ comptime self: Self,
+ file: std.fs.File,
+ comptime Parent: type,
+ comptime Type: type,
+ comptime prefix: []const u8,
+ ) void {
+ const decls = std.meta.declarations(Type);
+ var gen = C_Generator.init(prefix, file);
+ defer gen.deinit();
+
+ if (comptime std.meta.trait.hasDecls(Type, .{"include"})) {
+ comptime var new_name = std.mem.zeroes([Type.include.len]u8);
+
+ comptime {
+ _ = std.mem.replace(u8, Type.include, "/", "_", std.mem.span(&new_name));
+ _ = std.mem.replace(u8, &new_name, ".", "_", std.mem.span(&new_name));
+ }
+ const inner_name = comptime std.mem.trim(u8, &new_name, "<>\"");
+ file.writeAll("#ifndef BINDINGS__decls__" ++ inner_name ++ "\n") catch {};
+ file.writeAll("#define BINDINGS__decls__" ++ inner_name ++ "\n") catch {};
+ file.writeAll("#include " ++ Type.include ++ "\n") catch {};
+ file.writeAll("namespace Wundle {\n class " ++ prefix ++ ";\n}\n") catch {};
+ file.writeAll("#endif\n\n") catch {};
+ }
+
+ // iterate exported enums
+ // do this first in case target lang needs enums defined before use
+ inline for (decls) |decl| {
+ if (decl.is_pub and decl.data == .Type and comptime std.ascii.isUpper(decl.name[0])) {
+ const T = decl.data.Type;
+ const info = @typeInfo(T);
+ if (info == .Enum and decl.is_pub) {
+ const layout = info.Enum.layout;
+ gen.gen_enum(prefix ++ "__" ++ decl.name, info.Enum);
+ }
+ }
+ }
+
+ // iterate exported structs
+ inline for (decls) |decl| {
+ if (decl.is_pub and decl.data == .Type and decl.is_pub and comptime std.ascii.isUpper(decl.name[0])) {
+ const T = decl.data.Type;
+ const info = @typeInfo(T);
+ if (info == .Struct and decl.is_pub) {
+ gen.gen_struct(decl.name, @typeInfo(T).Struct);
+ }
+ }
+ }
+
+ inline for (decls) |decl| {
+ if (decl.is_pub and decl.data == .Type and decl.is_pub) {
+ const T = decl.data.Type;
+ const info = @typeInfo(T);
+ if (info == .Union and comptime std.ascii.isUpper(decl.name[0])) {
+ const layout = info.Union.layout;
+ gen.gen_union(prefix ++ "__" ++ decl.name, info.Union);
+ }
+ }
+ }
+
+ // iterate exported fns
+ inline for (decls) |decl, decl_i| {
+ if (decl.is_pub and decl.data == .Fn and decl.is_pub) {
+ const func = decl.data.Fn;
+ // if (func.) {
+ const fn_meta = @typeInfo(func.fn_type).Fn;
+ const info = @typeInfo(Type);
+ const struct_decl = info.Struct.decls[decl_i];
+ // blocked by https://github.com/ziglang/zig/issues/8259
+ gen.gen_func(
+ prefix ++ "__" ++ decl.name,
+ func,
+ fn_meta,
+ struct_decl.data.Fn.arg_names,
+ );
+ // }
+ }
+ }
+ }
+
+ pub fn exec(comptime self: Self, file: std.fs.File) void {
+ const Generator = C_Generator;
+ validateGenerator(Generator);
+
+ file.writeAll("#pragma once\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n") catch {};
+
+ inline for (all_decls) |_decls| {
+ if (comptime _decls.is_pub) {
+ switch (_decls.data) {
+ .Type => |Type| {
+ if (getCStruct(Type)) |CStruct| {
+ processDecls(
+ self,
+ file,
+ Type,
+ CStruct.Type,
+ CStruct.name,
+ );
+ }
+ },
+ else => {},
+ }
+ }
+ }
+
+ // processDecls(
+ // self,
+ // file,
+ // import,
+ // "Bindings",
+ // );
+ }
+ };
+}