-
Notifications
You must be signed in to change notification settings - Fork 3
Functions
A function written in Zig can be made available for use in JavaScript provided that it does not:
- accept any comptime argument
- accept
anytype
as an argument - accept a function pointer as an argument
- return a data type that only exists in comptime
- return a function pointer
Naturally, the function would need to be made public using the pub
keyword.
A function that accepts
std.mem.Allocator
will
automatically get one from Zigar. On the JavaScript side, it will end up with one fewer arguments:
const std = @import("std");
pub fn toUpperCase(allocator: std.mem.Allocator, s: []const u8) ![]const u8 {
return try std.ascii.allocUpperString(allocator, s);
}
import { toUpperCase } from './function-example-1.zig';
console.log(toUpperCase('Hello world').string);
HELLO WORLD
The allocator given is only valid in for the duration of the call. Code that saves a copy of the allocator will likely crash when it tries to use it again. Memory from the allocator comes from the JavaScript language engine, which employs garbage collection. There is never a need to manually free up memory.
A function defined within a struct that accepts an instance of the struct itself as the first argument can be invoked in JavaScript in the manner of an instance method:
pub const Rectangle = struct {
left: f64,
right: f64,
width: f64,
height: f64,
pub fn size(self: Rectangle) f64 {
return self.width * self.height;
}
};
import { Rectangle } from './function-example-2.zig';
const rect = new Rectangle({ left: 5, right: 10, width: 20, height: 10 });
console.log(rect.size());
200
At the same time, it can be invoked like a static method:
import { Rectangle } from './function-example-2.zig';
console.log(Rectangle.size({ left: 5, right: 10, width: 30, height: 15 }));
450
It works the same way when the self
argument is a pointer:
pub const Rectangle = struct {
left: f64,
right: f64,
width: f64,
height: f64,
pub fn size(self: *const Rectangle) f64 {
return self.width * self.height;
}
};
import { Rectangle } from './function-example-3.zig';
const rect = new Rectangle({ left: 5, right: 10, width: 20, height: 10 });
console.log(rect.size());
console.log(Rectangle.size({ left: 5, right: 10, width: 30, height: 15 }));
200
450
Union, Enum, and Opaque can also have methods attached to them.
Variable arguments passed to a C-style variadic function need to be explicitly typed:
const c = @cImport(
@cInclude("stdio.h"),
);
pub const I32 = i32;
pub const I64 = i64;
pub const F64 = f64;
pub const CStr = [*:0]u8;
pub const printf = c.printf;
import { printf, I32, I64, F64, CStr } from './variadic-function-example-1.zig';
printf("i32: %d\n", new I32(1234));
printf("i64: %lx\n", new I64(0x7777_7777_7777_7777n));
printf("f64: %.5f\n", new F64(Math.PI));
printf("string: %s\n", new CStr('Hello world'));
i32: 1234
i64: 7777777777777777
f64: 3.14159
string: Hello world
printf has one fixed argument--the format string. This does not need explicit typing, since Zigar knows what's expected. Arguments after the format string must be Zigar data object.
It's possible to write variadic functions in Zig:
const std = @import("std");
pub const I64 = i64;
pub fn print(count: usize, ...) callconv(.C) void {
var va_list = @cVaStart();
defer @cVaEnd(&va_list);
for (0..count) |_| {
const number = @cVaArg(&va_list, i64);
std.debug.print("{x}\n", .{number});
}
}
import { print, I64 } from './variadic-function-example-2.zig';
print(4,
new I64(0x7777_7777_7777_7777n),
new I64(0x7777_7777_7777_0000n),
new I64(0x7777_7777_0000_0000n),
new I64(0x7777_0000_0000_0000n),
);
7777777777777777
7777777777770000
7777777700000000
7777000000000000
For now you should not use this feature. As of version 0.13.0, support for variadic functions
is still incomplete. It does not work at all when the compilation target is x86_64-windows
or aarch64-linux
. printf
and friends do work on these platforms.
Zigar uses archecture-specific code to handle variadic functions. Support is currently limited
to wasm32
, x86
, x86_64
, arm
, aarch64
, riscv64
, and powerpc64le
.
The number of arguments you can pass to a variadic function is not unlimited. Up to 1024 bytes can be used on a 32-bit platform, while 2048 bytes can be used on a 64-bit latform. This translates to roughly 256 arguments. The limit does not apply to WebAssembly.
Zigar currently does not support function pointers.