Files
castle/packages/js/src/root.zig
2025-10-04 16:25:50 +02:00

1219 lines
44 KiB
Zig

const std = @import("std");
/// Manually translated quickjs.h, only for 64-bit targets.
const import = struct {
comptime {
// 64-bit targets only
std.debug.assert(@sizeOf(usize) == 8);
// all c_int has been translated to i32
std.debug.assert(@sizeOf(c_int) == @sizeOf(i32));
}
pub const JSRuntime = opaque {};
pub const JSContext = opaque {};
pub const JSClass = opaque {};
pub const JSGCObjectHeader = opaque {};
pub const JS_BOOL = i32;
pub const JSClassID = u32;
pub const JSAtom = u32;
pub const JSShortBigInt = i64;
pub const JS_TAG = enum(i32) {
/// First negative tag.
pub const FIRST: i32 = -9;
BIG_INT = -9,
SYMBOL = -8,
STRING = -7,
STRING_ROPE = -6,
/// Used internally.
MODULE = -3,
/// Used internally.
FUNCTION_BYTECODE = -2,
OBJECT = -1,
INT = 0,
BOOL = 1,
NULL = 2,
UNDEFINED = 3,
UNINITIALIZED = 4,
CATCH_OFFSET = 5,
EXCEPTION = 6,
SHORT_BIG_INT = 7,
FLOAT64 = 8,
_,
};
pub const JSRefCountHeader = extern struct {
ref_count: i32,
};
pub const JSValueUnion = extern union {
int32: i32,
float64: f64,
ptr: ?*anyopaque,
short_big_int: JSShortBigInt,
};
pub const JSValue = extern struct {
value: JSValueUnion,
tag: i64,
};
pub const JSValueConst = JSValue;
pub inline fn JS_VALUE_GET_TAG(v: JSValue) JS_TAG {
return @enumFromInt(@as(i32, @intCast(v.tag)));
}
pub inline fn JS_VALUE_GET_NORM_TAG(v: JSValue) JS_TAG {
return JS_VALUE_GET_TAG(v);
}
pub inline fn JS_VALUE_GET_INT(v: JSValue) i32 {
return v.value.int32;
}
pub inline fn JS_VALUE_GET_BOOL(v: JSValue) JS_BOOL {
return v.value.int32;
}
pub inline fn JS_VALUE_GET_FLOAT64(v: JSValue) f64 {
return v.value.float64;
}
pub inline fn JS_VALUE_GET_PTR(v: JSValue) ?*anyopaque {
return v.value.ptr;
}
pub inline fn JS_VALUE_GET_SHORT_BIG_INT(v: JSValue) JSShortBigInt {
return v.value.short_big_int;
}
pub inline fn JS_MKVAL(tag: JS_TAG, val: i32) JSValue {
return .{
.value = .{ .int32 = val },
.tag = @intFromEnum(tag),
};
}
pub inline fn JS_MKPTR(tag: JS_TAG, ptr: ?*anyopaque) JSValue {
return .{
.value = .{ .ptr = ptr },
.tag = @intFromEnum(tag),
};
}
pub inline fn JS_TAG_IS_FLOAT64(tag: JS_TAG) bool {
return tag == JS_TAG.FLOAT64;
}
pub inline fn JS_VALUE_IS_NAN(v: JSValue) bool {
if (v.tag != JS_TAG.FLOAT64) {
return false;
}
const u: u64 = @bitCast(v.u.float64);
return (u & 0x7FFFFFFFFFFFFFFF) > 0x7FF0000000000000;
}
pub const JS_NAN: JSValue = .{
.value = .{ .float64 = std.math.nan(f64) },
.tag = @intFromEnum(JS_TAG.FLOAT64),
};
pub inline fn JS_VALUE_IS_BOTH_INT(v1: JSValue, v2: JSValue) bool {
return @intFromEnum(JS_VALUE_GET_TAG(v1)) | @intFromEnum(JS_VALUE_GET_TAG(v2)) == 0;
}
pub inline fn JS_VALUE_IS_BOTH_FLOAT(v1: JSValue, v2: JSValue) bool {
return JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) and JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2));
}
pub inline fn JS_VALUE_HAS_REF_COUNT(v: JSValue) bool {
return @as(u32, @bitCast(@intFromEnum(JS_VALUE_GET_TAG(v)))) >= @as(u32, @bitCast(JS_TAG.FIRST));
}
pub const JS_NULL = JS_MKVAL(JS_TAG.NULL, 0);
pub const JS_UNDEFINED = JS_MKVAL(JS_TAG.UNDEFINED, 0);
pub const JS_FALSE = JS_MKVAL(JS_TAG.BOOL, 0);
pub const JS_TRUE = JS_MKVAL(JS_TAG.BOOL, 1);
pub const JS_EXCEPTION = JS_MKVAL(JS_TAG.EXCEPTION, 0);
pub const JS_UNINITIALIZED = JS_MKVAL(JS_TAG.UNINITIALIZED, 0);
pub inline fn __JS_NewFloat64(ctx: *JSContext, d: f64) JSValue {
_ = ctx;
return .{
.value = .{ .float64 = d },
.tag = @intFromEnum(JS_TAG.FLOAT64),
};
}
pub inline fn __JS_NewShortBigInt(ctx: *JSContext, d: JSShortBigInt) JSValue {
_ = ctx;
return .{
.value = .{ .short_big_int = d },
.tag = @intFromEnum(JS_TAG.SHORT_BIG_INT),
};
}
pub const JS_PROP = packed struct(u32) {
props: packed struct(u4) {
configurable: bool = false,
writable: bool = false,
enumerable: bool = false,
/// Used internally in Arrays.
length: bool = false,
pub const cwe = @This(){
.configurable = true,
.writable = true,
.enumerable = true,
};
} = .{},
tmask: enum(u2) {
NORMAL = 0,
GETSET = 1,
/// Used internally.
VARREF = 2,
/// Used internally.
AUTOINIT = 3,
} = .NORMAL,
_pad6: u2 = 0,
has: packed struct(u6) {
configurable: bool = false,
writable: bool = false,
enumerable: bool = false,
get: bool = false,
set: bool = false,
value: bool = false,
} = .{},
throw: bool = false,
throw_strict: bool = false,
/// Internal use.
no_add: bool = false,
/// Internal use.
no_exotic: bool = false,
_pad14: u18 = 0,
};
pub const JS_EVAL = packed struct(u32) {
type: enum(u2) {
/// Global code (default).
GLOBAL = 0,
/// Module code.
MODULE = 1,
/// Direct call (internal use).
DIRECT = 2,
/// Indirect call (internal use).
INDIRECT = 3,
} = .GLOBAL,
flags: packed struct(u30) {
_pad2: u1 = 0,
/// Force "strict" mode.
strict: bool = false,
_pad4: u1 = 0,
/// Compile but do not run. The result is an object with a
/// JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
/// with JS_EvalFunction().
compile_only: bool = false,
/// Don't include the stack frames before this eval in the Error()
/// backtraces.
backtrace_barrier: bool = false,
/// Allow top-level await in normal script. JS_Eval() returns a promise.
/// Only allowed with .type == .GLOBAL
async: bool = false,
_pad8: u24 = 0,
} = .{},
};
pub const JSCFunction = fn (ctx: *JSContext, this_val: JSValueConst, argc: i32, argv: [*]JSValueConst) callconv(.c) JSValue;
pub const JSCFunctionMagic = fn (ctx: *JSContext, this_val: JSValueConst, argc: i32, argv: [*]JSValueConst, magic: i32) callconv(.c) JSValue;
pub const JSCFunctionData = fn (ctx: *JSContext, this_val: JSValueConst, argc: i32, argv: [*]JSValueConst, magic: i32, func_data: *JSValue) callconv(.c) JSValue;
pub const JS_MarkFunc = fn (rt: *JSRuntime, gp: *JSGCObjectHeader) callconv(.c) void;
pub const JSMallocState = extern struct {
malloc_count: usize,
malloc_suze: usize,
malloc_limit: usize,
/// User opaque.
@"opaque": ?*anyopaque,
};
pub const JSMallocFunctions = extern struct {
js_malloc: *const fn (s: *JSMallocState, size: usize) callconv(.c) ?*anyopaque,
js_free: *const fn (s: *JSMallocState, ptr: ?*anyopaque) callconv(.c) void,
js_realloc: *const fn (s: *JSMallocState, ptr: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque,
js_malloc_usable_size: ?*const fn (ptr: ?*const anyopaque) callconv(.c) usize,
};
pub extern fn JS_NewRuntime() ?*JSRuntime;
pub extern fn JS_SetRuntimeInfo(rt: *JSRuntime, info: [*:0]const u8) void;
pub extern fn JS_SetMemoryLimit(rt: *JSRuntime, limit: usize) void;
pub extern fn JS_SetGCThreshold(rt: *JSRuntime, gc_threshold: usize) void;
pub extern fn JS_SetMaxStackSize(rt: *JSRuntime, stack_size: usize) void;
pub extern fn JS_UpdateStackTop(rt: *JSRuntime) void;
pub extern fn JS_NewRuntime2(mf: *const JSMallocFunctions, @"opaque": ?*anyopaque) ?*JSRuntime;
pub extern fn JS_FreeRuntime(rt: ?*JSRuntime) void;
pub extern fn JS_GetRuntimeOpaque(rt: *JSRuntime) ?*anyopaque;
pub extern fn JS_SetRuntimeOpaque(rt: *JSRuntime, @"opaque": ?*anyopaque) void;
pub extern fn JS_MarkValue(rt: *JSRuntime, val: JSValue, mark_func: ?*const JS_MarkFunc) void;
pub extern fn JS_RunGC(rt: *JSRuntime) void;
pub extern fn JS_IsLiveObject(rt: *JSRuntime, obj: JSValue) JS_BOOL;
pub extern fn JS_NewContext(rt: *JSRuntime) ?*JSContext;
pub extern fn JS_FreeContext(s: ?*JSContext) void;
pub extern fn JS_DupContext(ctx: *JSContext) ?*JSContext;
pub extern fn JS_GetContextOpaque(ctx: *JSContext) ?*anyopaque;
pub extern fn JS_SetContextOpaque(ctx: *JSContext, @"opaque": ?*anyopaque) void;
pub extern fn JS_GetRuntime(ctx: *JSContext) *JSRuntime;
pub extern fn JS_SetClassProto(ctx: *JSContext, class_id: JSClassID, obj: JSValue) void;
pub extern fn JS_GetClassProto(ctx: *JSContext, class_id: JSClassID) JSValue;
pub extern fn JS_NewContextRaw(rt: *JSRuntime) ?*JSContext;
pub extern fn JS_AddIntrinsicBaseObjects(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicDate(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicEval(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicStringNormalize(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicRegExpCompiler(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicRegExp(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicJSON(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicProxy(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicMapSet(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicTypedArrays(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicPromise(ctx: *JSContext) void;
pub extern fn JS_AddIntrinsicWeakRef(ctx: *JSContext) void;
pub extern fn js_malloc_rt(rt: *JSRuntime, size: usize) ?*anyopaque;
pub extern fn js_free_rt(rt: *JSRuntime, ptr: ?*anyopaque) void;
pub extern fn js_realloc_rt(rt: *JSRuntime, ptr: ?*anyopaque, size: usize) ?*anyopaque;
pub extern fn js_malloc_usable_size_rt(rt: *JSRuntime, ptr: ?*const anyopaque) usize;
pub extern fn js_mallocz_rt(rt: *JSRuntime, size: usize) ?*anyopaque;
pub extern fn js_malloc(ctx: *JSContext, size: usize) ?*anyopaque;
pub extern fn js_free(ctx: *JSContext, ptr: ?*anyopaque) void;
pub extern fn js_realloc(ctx: *JSContext, ptr: ?*anyopaque, size: usize) ?*anyopaque;
pub extern fn js_malloc_usable_size(ctx: *JSContext, ptr: ?*const anyopaque) usize;
pub extern fn js_realloc2(ctx: *JSContext, ptr: ?*anyopaque, size: usize, pslack: ?*usize) ?*anyopaque;
pub extern fn js_mallocz(ctx: *JSContext, size: usize) ?*anyopaque;
pub extern fn js_strdup(ctx: *JSContext, str: [*:0]const u8) ?[*:0]u8;
pub extern fn js_strndup(ctx: *JSContext, s: [*]const u8, n: usize) ?[*]u8;
pub const JSMemoryUsage = extern struct {
malloc_size: i64,
malloc_limit: i64,
memory_used_size: i64,
malloc_count: i64,
memory_used_count: i64,
atom_count: i64,
atom_size: i64,
str_count: i64,
str_size: i64,
obj_count: i64,
obj_size: i64,
prop_count: i64,
prop_size: i64,
shape_count: i64,
shape_size: i64,
js_func_count: i64,
js_func_size: i64,
js_func_code_size: i64,
js_func_pc2line_count: i64,
js_func_pc2line_size: i64,
c_func_count: i64,
array_count: i64,
fast_array_count: i64,
fast_array_elements: i64,
binary_object_count: i64,
binary_object_size: i64,
};
pub extern fn JS_ComputeMemoryUsage(rt: *JSRuntime, s: *JSMemoryUsage) void;
pub extern fn JS_DumpMemoryUsage(fp: *std.c.FILE, s: *const JSMemoryUsage, rt: ?*JSRuntime) void;
pub const JS_ATOM_NULL: JSAtom = 0;
pub extern fn JS_NewAtomLen(ctx: *JSContext, str: [*]const u8, len: usize) JSAtom;
pub extern fn JS_NewAtom(ctx: *JSContext, str: [*:0]const u8) JSAtom;
pub extern fn JS_NewAtomUInt32(ctx: *JSContext, n: u32) JSAtom;
pub extern fn JS_DupAtom(ctx: *JSContext, v: JSAtom) JSAtom;
pub extern fn JS_FreeAtom(ctx: *JSContext, v: JSAtom) void;
pub extern fn JS_FreeAtomRT(rt: *JSRuntime, v: JSAtom) void;
pub extern fn JS_AtomToValue(ctx: *JSContext, atom: JSAtom) JSValue;
pub extern fn JS_AtomToString(ctx: *JSContext, atom: JSAtom) JSValue;
pub extern fn JS_AtomToCStringLen(ctx: *JSContext, plen: ?*usize, atom: JSAtom) ?[*:0]const u8;
pub inline fn JS_AtomToCString(ctx: *JSContext, atom: JSAtom) ?[*:0]const u8 {
return JS_AtomToCStringLen(ctx, null, atom);
}
pub extern fn JS_ValueToAtom(ctx: *JSContext, val: JSValue) JSAtom;
pub const JSPropertyEnum = extern struct {
is_enumerable: JS_BOOL,
atom: JSAtom,
};
pub const JSPropertyDescriptor = extern struct {
flags: JS_PROP,
value: JSValue,
getter: JSValue,
setter: JSValue,
};
pub const JSClassExoticMethods = extern struct {
/// Return -1 if exception (can only happen in case of Proxy object), 0
/// if the property does not exists, 1 if it exists. If 1 is returned,
/// the property descriptor 'desc' is filled if != NULL.
get_own_property: ?*const fn (ctx: *JSContext, desc: *JSPropertyDescriptor, obj: JSValueConst, prop: JSAtom) callconv(.c) i32 = null,
/// `ptab.*` should hold the `plen.*` property keys. Return 0 if OK, -1
/// if exception. The `is_enumerable` field is ignored.
get_own_property_names: ?*const fn (ctx: *JSContext, ptab: *[*]JSPropertyEnum, plen: *u32, obj: JSValueConst) callconv(.c) i32 = null,
/// return < 0 if exception or 1/0
delete_property: ?*const fn (ctx: *JSContext, obj: JSValueConst, prop: JSAtom) callconv(.c) i32 = null,
/// return < 0 if exception or 1/0
define_own_property: ?*const fn (ctx: *JSContext, this_obj: JSValueConst, prop: JSAtom, val: JSValueConst, getter: JSValueConst, setter: JSValueConst, flags: JS_PROP) callconv(.c) i32 = null,
// The following methods can be emulated with the previous ones, so they
// are usually not needed.
/// return < 0 if exception or 1/0
has_property: ?*const fn (ctx: *JSContext, obj: JSValueConst, prop: JSAtom) callconv(.c) i32 = null,
get_property: ?*const fn (ctx: *JSContext, obj: JSValueConst, prop: JSAtom, receiver: JSValueConst) callconv(.c) JSValue = null,
/// return < 0 if exception or 1/0
set_property: ?*const fn (ctx: *JSContext, obj: JSValueConst, atom: JSAtom, value: JSValueConst, receiver: JSValueConst, flags: JS_PROP) callconv(.c) i32 = null,
/// To get a consistent object behavior when get_prototype != null,
/// get_property, set_property and set_prototype must be != null and the
/// object must be created with a JS_NULL prototype.
get_prototype: ?*const fn (ctx: *JSContext, obj: JSValueConst) callconv(.c) JSValue = null,
/// return < 0 if exception or 1/0
set_prototype: ?*const fn (ctx: *JSContext, obj: JSValueConst, proto_val: JSValueConst) callconv(.c) i32 = null,
/// return < 0 if exception or 1/0
is_extensible: ?*const fn (ctx: *JSContext, obj: JSValueConst) callconv(.c) i32 = null,
/// return < 0 if exception or 1/0
prevent_extensions: ?*const fn (ctx: *JSContext, obj: JSValueConst) callconv(.c) i32 = null,
};
pub const JS_CALL_FLAG = packed struct(u32) {
constructor: bool = false,
_pad1: u31 = 0,
};
pub const JSClassFinalizer = fn (rt: *JSRuntime, val: JSValue) callconv(.c) void;
pub const JSClassGCMark = fn (rt: *JSRuntime, val: JSValueConst, mark_func: *const JS_MarkFunc) void;
pub const JSClassCall = fn (ctx: *JSContext, func_obj: JSValueConst, this_val: JSValueConst, argc: i32, argv: [*]JSValueConst, flags: JS_CALL_FLAG) JSValue;
pub const JSClassDef = extern struct {
class_name: [*:0]const u8,
finalizer: ?*const JSClassFinalizer,
gc_mark: ?*const JSClassGCMark,
/// if `call != null`, the object is a function. If
/// `flags.constructor == true`, the function is called as a
/// constructor. In this case, `this_val` is new.target. A constructor
/// call only happens if the object constructor bit is set (see
/// `JS_SetConstructorBit`).
call: ?*const JSClassCall,
exotic: ?*const JSClassExoticMethods,
};
pub const JS_INVALID_CLASS_ID: JSClassID = 0;
pub extern fn JS_NewClassID(pclass_id: *JSClassID) JSClassID;
/// Returns the class ID if `v` is an object, otherwise returns
/// `JS_INVALID_CLASS_ID`.
pub extern fn JS_GetClassID(v: JSValue) JSClassID;
pub extern fn JS_NewClass(rt: *JSRuntime, class_id: JSClassID, class_def: *const JSClassDef) i32;
pub extern fn JS_IsRegisteredClass(rt: ?*JSRuntime, class_id: JSClassID) i32;
pub inline fn JS_NewBool(ctx: *JSContext, val: JS_BOOL) JSValue {
_ = ctx;
return JS_MKVAL(JS_TAG.BOOL, @intFromBool(val != 0));
}
pub inline fn JS_NewInt32(ctx: *JSContext, val: i32) JSValue {
_ = ctx;
return JS_MKVAL(JS_TAG.INT, val);
}
pub inline fn JS_NewCatchOffset(ctx: *JSContext, val: i32) JSValue {
_ = ctx;
return JS_MKVAL(JS_TAG.CATCH_OFFSET, val);
}
pub inline fn JS_NewInt64(ctx: *JSContext, val: i64) JSValue {
if (val == @as(i64, @as(i32, @truncate(val)))) {
return JS_NewInt32(ctx, @truncate(val));
} else {
return __JS_NewFloat64(ctx, @floatFromInt(val));
}
}
pub inline fn JS_NewUint32(ctx: *JSContext, val: u32) JSValue {
if (val <= 0x7FFFFFFF) {
return JS_NewInt32(ctx, @intCast(val));
} else {
return JS_NewFloat64(ctx, @floatFromInt(val));
}
}
pub inline fn JS_NewFloat64(ctx: *JSContext, d: f64) JSValue {
if (d >= @as(f64, @floatFromInt(std.math.minInt(i32))) and d <= @as(f64, @floatFromInt(std.math.maxInt(i32)))) {
const val: i32 = @intFromFloat(d);
// -0 cannot be represented as integer, so we compare the bit representation
if (@as(u64, @bitCast(d)) == @as(u64, @bitCast(@as(f64, @floatFromInt(val))))) {
return JS_MKVAL(JS_TAG.INT, val);
}
}
return __JS_NewFloat64(ctx, d);
}
pub extern fn JS_NewBigInt64(ctx: *JSContext, v: i64) JSValue;
pub extern fn JS_NewBigUint64(ctx: *JSContext, v: u64) JSValue;
pub inline fn JS_IsNumber(v: JSValue) bool {
const tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG.INT or JS_TAG_IS_FLOAT64(tag);
}
pub inline fn JS_IsBigInt(v: JSValue) bool {
const tag = JS_VALUE_GET_TAG(v);
return tag == JS_TAG.BIG_INT or tag == JS_TAG.SHORT_BIG_INT;
}
pub inline fn JS_IsBool(v: JSValue) bool {
return JS_VALUE_GET_TAG(v) == JS_TAG.BOOL;
}
pub inline fn JS_IsNull(v: JSValue) bool {
return JS_VALUE_GET_TAG(v) == JS_TAG.NULL;
}
pub inline fn JS_IsUndefined(v: JSValue) bool {
return JS_VALUE_GET_TAG(v) == JS_TAG.UNDEFINED;
}
pub inline fn JS_IsException(v: JSValue) bool {
if (JS_VALUE_GET_TAG(v) == JS_TAG.EXCEPTION) {
@branchHint(.unlikely);
return true;
} else {
return false;
}
}
pub inline fn JS_IsUninitialized(v: JSValue) bool {
if (JS_VALUE_GET_TAG(v) == JS_TAG.UNINITIALIZED) {
@branchHint(.unlikely);
return true;
} else {
return false;
}
}
pub inline fn JS_IsString(v: JSValue) bool {
return JS_VALUE_GET_TAG(v) == JS_TAG.STRING or JS_VALUE_GET_TAG(v) == JS_TAG.STRING_ROPE;
}
pub inline fn JS_IsSymbol(v: JSValue) bool {
return JS_VALUE_GET_TAG(v) == JS_TAG.SYMBOL;
}
pub inline fn JS_IsObject(v: JSValue) bool {
return JS_VALUE_GET_TAG(v) == JS_TAG.OBJECT;
}
pub extern fn JS_Throw(ctx: *JSContext, obj: JSValue) JSValue;
pub extern fn JS_SetUncatchableException(ctx: *JSContext, flag: JS_BOOL) void;
pub extern fn JS_GetException(ctx: *JSContext) JSValue;
pub extern fn JS_HasException(ctx: *JSContext) JS_BOOL;
pub extern fn JS_IsError(ctx: *JSContext, val: JSValue) JS_BOOL;
pub extern fn JS_NewError(ctx: *JSContext) JSValue;
pub extern fn JS_ThrowSyntaxError(ctx: *JSContext, fmt: [*:0]const u8, ...) JSValue;
pub extern fn JS_ThrowTypeError(ctx: *JSContext, fmt: [*:0]const u8, ...) JSValue;
pub extern fn JS_ThrowReferenceError(ctx: *JSContext, fmt: [*:0]const u8, ...) JSValue;
pub extern fn JS_ThrowRangeError(ctx: *JSContext, fmt: [*:0]const u8, ...) JSValue;
pub extern fn JS_ThrowInternalError(ctx: *JSContext, fmt: [*:0]const u8, ...) JSValue;
pub extern fn JS_ThrowOutOfMemory(ctx: *JSContext) JSValue;
pub extern fn __JS_FreeValue(ctx: *JSContext, v: JSValue) void;
pub extern fn __JS_FreeValueRT(rt: *JSRuntime, v: JSValue) void;
pub inline fn JS_FreeValue(ctx: *JSContext, v: JSValue) void {
if (JS_VALUE_HAS_REF_COUNT(v)) {
const p: *JSRefCountHeader = @ptrCast(@alignCast(JS_VALUE_GET_PTR(v).?));
p.ref_count -= 1;
if (p.ref_count <= 0) {
__JS_FreeValue(ctx, v);
}
}
}
pub inline fn JS_FreeValueRT(rt: *JSRuntime, v: JSValue) void {
if (JS_VALUE_HAS_REF_COUNT(v)) {
const p: *JSRefCountHeader = @ptrCast(@alignCast(JS_VALUE_GET_PTR(v).?));
p.ref_count -= 1;
if (p.ref_count <= 0) {
__JS_FreeValueRT(rt, v);
}
}
}
pub inline fn JS_DupValue(ctx: *JSContext, v: JSValueConst) JSValue {
_ = ctx;
if (JS_VALUE_HAS_REF_COUNT(v)) {
const p: *JSRefCountHeader = @ptrCast(@alignCast(JS_VALUE_GET_PTR(v).?));
p.ref_count += 1;
}
return v;
}
pub inline fn JS_DupValueRT(rt: *JSRuntime, v: JSValueConst) JSValue {
_ = rt;
if (JS_VALUE_HAS_REF_COUNT(v)) {
const p: *JSRefCountHeader = @ptrCast(@alignCast(JS_VALUE_GET_PTR(v).?));
p.ref_count += 1;
}
return v;
}
pub extern fn JS_StrictEq(ctx: *JSContext, op1: JSValue, op2: JSValue) JS_BOOL;
pub extern fn JS_SameValue(ctx: *JSContext, op1: JSValue, op2: JSValue) JS_BOOL;
pub extern fn JS_SameValueZero(ctx: *JSContext, op1: JSValue, op2: JSValue) JS_BOOL;
// -------------------------------------------------------------------------
pub extern fn JS_NewObject(ctx: *JSContext) JSValue;
pub extern fn JS_NewObjectProto(ctx: *JSContext, proto: JSValueConst) JSValue;
pub extern fn JS_IsFunction(ctx: *JSContext, val: JSValueConst) JS_BOOL;
pub extern fn JS_IsConstructor(ctx: *JSContext, val: JSValueConst) JS_BOOL;
pub extern fn JS_SetConstructorBit(ctx: *JSContext, func_obj: JSValueConst, val: JS_BOOL) JS_BOOL;
pub extern fn JS_NewArray(ctx: *JSContext) JSValue;
pub extern fn JS_IsArray(ctx: *JSContext, val: JSValue) JS_BOOL;
pub extern fn JS_NewDate(ctx: *JSContext, epoch_ms: f64) JSValue;
pub extern fn JS_Eval(ctx: *JSContext, input: [*:0]const u8, input_len: usize, filename: [*:0]const u8, eval_flags: JS_EVAL) JSValue;
pub extern fn JS_EvalThis(ctx: *JSContext, this_obj: JSValue, input: [*:0]const u8, input_len: usize, filename: [*:0]const u8, eval_flags: JS_EVAL) JSValue;
};
pub const Allocator = struct {
const alignment_bytes = 16;
const alignment: std.mem.Alignment = .fromByteUnits(alignment_bytes);
const AllocationPtr = *align(alignment_bytes) anyopaque;
const AllocationSlice = []align(alignment_bytes) u8;
const LengthPtr = *align(alignment_bytes) usize;
const Allocation = struct {
allocation_ptr: AllocationPtr,
pub fn fromAllocationSlice(slice: AllocationSlice) Allocation {
return .{
.allocation_ptr = @ptrCast(slice.ptr),
};
}
pub fn fromForeignPtr(ptr: *anyopaque) Allocation {
return .{
.allocation_ptr = @ptrCast(@alignCast(@as([*]u8, @ptrCast(ptr)) - alignment_bytes)),
};
}
pub fn getAllocationSize(self: Allocation) usize {
return @as(LengthPtr, @ptrCast(self.allocation_ptr)).*;
}
pub fn setAllocationSize(self: Allocation, allocation_size: usize) void {
@as(LengthPtr, @ptrCast(self.allocation_ptr)).* = allocation_size;
}
pub fn toAllocationSlice(self: Allocation) AllocationSlice {
return @alignCast(@as([*]u8, @ptrCast(self.allocation_ptr))[0..self.getAllocationSize()]);
}
pub fn toForeignPtr(self: Allocation) *anyopaque {
return @ptrCast(@alignCast(@as([*]u8, @ptrCast(self.allocation_ptr)) + alignment_bytes));
}
};
pub const adapter: *const import.JSMallocFunctions = &.{
.js_malloc = &js_malloc,
.js_free = &js_free,
.js_realloc = &js_realloc,
.js_malloc_usable_size = null,
};
fn js_malloc(s: *import.JSMallocState, size: usize) callconv(.c) ?*anyopaque {
const allocator: *const std.mem.Allocator = @ptrCast(@alignCast(s.@"opaque".?));
const allocation_size = alignment_bytes + size;
const allocation_slice: AllocationSlice = allocator.alignedAlloc(u8, alignment, allocation_size) catch return null;
const allocation = Allocation.fromAllocationSlice(allocation_slice);
allocation.setAllocationSize(allocation_size);
return allocation.toForeignPtr();
}
fn js_free(s: *import.JSMallocState, maybe_foreign_ptr: ?*anyopaque) callconv(.c) void {
if (maybe_foreign_ptr) |foreign_ptr| {
const allocator: *const std.mem.Allocator = @ptrCast(@alignCast(s.@"opaque".?));
const allocation = Allocation.fromForeignPtr(foreign_ptr);
const allocation_slice = allocation.toAllocationSlice();
allocator.free(allocation_slice);
}
}
fn js_realloc(s: *import.JSMallocState, maybe_foreign_ptr: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque {
if (maybe_foreign_ptr) |foreign_ptr| {
const allocator: *const std.mem.Allocator = @ptrCast(@alignCast(s.@"opaque".?));
const old_allocation = Allocation.fromForeignPtr(foreign_ptr);
const old_allocation_slice = old_allocation.toAllocationSlice();
if (size > 0) {
const new_allocation_size = alignment_bytes + size;
const new_allocation_slice = allocator.realloc(old_allocation_slice, new_allocation_size) catch return null;
const new_allocation = Allocation.fromAllocationSlice(new_allocation_slice);
new_allocation.setAllocationSize(new_allocation_size);
return new_allocation.toForeignPtr();
} else {
allocator.free(old_allocation_slice);
return null;
}
} else {
return js_malloc(s, size);
}
}
};
pub const Runtime = extern struct {
runtime: *import.JSRuntime,
pub fn newRuntime() error{OutOfMemory}!Runtime {
if (import.JS_NewRuntime()) |runtime| {
return .{ .runtime = runtime };
} else {
return error.OutOfMemory;
}
}
/// Create a new runtime with provided allocator.
///
/// `std.mem.Allocator` is a fat pointer, i.e. two pointers in a struct,
/// usually passed around as value. However, QuickJS only supports a single
/// context pointer. Therefore, you have to allocate the fat pointer
/// structure yourself (e.g. as a stack variable) and provide a pointer to
/// it. The pointer must be valid as long as the runtime is being used.
pub fn newRuntimeAllocator(allocator: *const std.mem.Allocator) error{OutOfMemory}!Runtime {
if (import.JS_NewRuntime2(Allocator.adapter, @ptrCast(@constCast(allocator)))) |runtime| {
return .{ .runtime = runtime };
} else {
return error.OutOfMemory;
}
}
/// The lifetime of `info` must exceed that of `self`.
pub fn setRuntimeinfo(self: Runtime, info: [*:0]const u8) void {
import.JS_SetRuntimeInfo(self.runtime, info);
}
pub fn setMemoryLimit(self: Runtime, limit: usize) void {
import.JS_SetMemoryLimit(self.runtime, limit);
}
pub fn setGcThreshold(self: Runtime, gc_threshold: usize) void {
import.JS_SetGCThreshold(self.runtime, gc_threshold);
}
/// Use `0` to disable maximum stack size check.
pub fn setMaxStackSize(self: Runtime, stack_size: usize) void {
import.JS_SetMaxStackSize(self.runtime, stack_size);
}
/// Should be called when changing thread to update the stack top value used
/// to check stack overflow.
pub fn updateStackTop(self: Runtime) void {
import.JS_UpdateStackTop(self.runtime);
}
pub fn freeRuntime(self: *Runtime) void {
import.JS_FreeRuntime(self.runtime);
self.* = undefined;
}
pub fn getRuntimeOpaque(self: Runtime) ?*anyopaque {
return import.JS_GetRuntimeOpaque(self.runtime);
}
pub fn setRuntimeOpaque(self: Runtime, @"opaque": ?*anyopaque) void {
import.JS_SetRuntimeOpaque(self.runtime, @"opaque");
}
pub fn markValue(self: Runtime, value: Value, mark_func: *const import.JS_MarkFunc) void {
import.JS_MarkValue(self.runtime, value.value, mark_func);
}
pub fn runGc(self: Runtime) void {
import.JS_RunGC(self.runtime);
}
pub fn isLiveObject(self: Runtime, object: Value) bool {
return import.JS_IsLiveObject(self.runtime, object.value) != 0;
}
pub fn newContext(self: Runtime) error{OutOfMemory}!Context {
if (import.JS_NewContext(self.runtime)) |context| {
return .{ .context = context };
} else {
return error.OutOfMemory;
}
}
pub fn newContextRaw(self: Runtime) error{OutOfMemory}!Context {
if (import.JS_NewContextRaw(self.runtime)) |context| {
return .{ .context = context };
} else {
return error.OutOfMemory;
}
}
pub fn malloc(self: Runtime, size: usize) error{OutOfMemory}!*anyopaque {
return import.js_malloc_rt(self.runtime, size) orelse error.OutOfMemory;
}
pub fn free(self: Runtime, ptr: ?*anyopaque) void {
import.js_free_rt(self.runtime, ptr);
}
pub fn realloc(self: Runtime, ptr: ?*anyopaque, size: usize) error{OutOfMemory}!*anyopaque {
return import.js_realloc_rt(self.runtime, ptr, size) orelse error.OutOfMemory;
}
pub fn mallocUsableSize(self: Runtime, ptr: ?*const anyopaque) usize {
return import.js_malloc_usable_size_rt(self.runtime, ptr);
}
pub fn mallocz(self: Runtime, size: usize) error{OutOfMemory}!*anyopaque {
return import.js_mallocz_rt(self.runtime, size) orelse error.OutOfMemory;
}
pub fn computeMemoryUsage(self: Runtime) import.JSMemoryUsage {
var ret: import.JSMemoryUsage = undefined;
import.JS_ComputeMemoryUsage(self.runtime, &ret);
return ret;
}
pub fn freeAtom(self: Runtime, v: Atom) void {
import.JS_FreeAtomRT(self.runtime, v.atom);
}
pub fn newClass(self: Runtime, class_id: ClassId, class_def: *const import.JSClassDef) error{Exception}!void {
const res = import.JS_NewClass(self.runtime, class_id.class_id, class_def);
if (res < 0) {
return error.Exception;
}
}
pub fn isRegisteredClass(self: Runtime, class_id: ClassId) bool {
return import.JS_IsRegisteredClass(self.runtime, class_id.class_id) != 0;
}
pub fn freeValue(self: Runtime, v: Value) void {
import.JS_FreeValueRT(self.runtime, v.value);
}
pub fn dupValue(self: Runtime, v: Value) Value {
return .{ .value = import.JS_DupValueRT(self.runtime, v.value) };
}
};
pub const Context = extern struct {
context: *import.JSContext,
pub fn freeContext(self: *Context) void {
import.JS_FreeContext(self.context);
self.* = undefined;
}
pub fn dupContext(self: Context) error{OutOfMemory}!Context {
if (import.JS_DupContext(self.context)) |context| {
return .{ .context = context };
} else {
return error.OutOfMemory;
}
}
pub fn getContextOpaque(self: Context) ?*anyopaque {
return import.JS_GetContextOpaque(self.context);
}
pub fn setContextOpaque(self: Context, @"opaque": ?*anyopaque) void {
import.JS_SetContextOpaque(self.context, @"opaque");
}
pub fn getRuntime(self: Context) Runtime {
return .{ .runtime = import.JS_GetRuntime(self.context) };
}
pub fn setClassProto(self: Context, class_id: import.JSClassID, object: Value) void {
import.JS_SetClassProto(self.context, class_id, object.value);
}
pub fn getClassProto(self: Context, class_id: import.JSClassID) Value {
return .{ .value = import.JS_GetClassProto(self.context, class_id) };
}
pub fn addIntrinsicBaseObjects(self: Context) void {
import.JS_AddIntrinsicBaseObjects(self.context);
}
pub fn addIntrinsicDate(self: Context) void {
import.JS_AddIntrinsicDate(self.context);
}
pub fn addIntrinsicEval(self: Context) void {
import.JS_AddIntrinsicEval(self.context);
}
pub fn addIntrinsicStringNormalize(self: Context) void {
import.JS_AddIntrinsicStringNormalize(self.context);
}
pub fn addIntrinsicRegExpCompiler(self: Context) void {
import.JS_AddIntrinsicRegExpCompiler(self.context);
}
pub fn addIntrinsicRegExp(self: Context) void {
import.JS_AddIntrinsicRegExp(self.context);
}
pub fn addIntrinsicJSON(self: Context) void {
import.JS_AddIntrinsicJSON(self.context);
}
pub fn addIntrinsicProxy(self: Context) void {
import.JS_AddIntrinsicProxy(self.context);
}
pub fn addIntrinsicMapSet(self: Context) void {
import.JS_AddIntrinsicMapSet(self.context);
}
pub fn addIntrinsicTypedArrays(self: Context) void {
import.JS_AddIntrinsicTypedArrays(self.context);
}
pub fn addIntrinsicPromise(self: Context) void {
import.JS_AddIntrinsicPromise(self.context);
}
pub fn addIntrinsicWeakRef(self: Context) void {
import.JS_AddIntrinsicWeakRef(self.context);
}
pub fn malloc(self: Context, size: usize) error{OutOfMemory}!*anyopaque {
return import.js_malloc(self.context, size) orelse error.OutOfMemory;
}
pub fn free(self: Context, ptr: ?*anyopaque) void {
import.js_free(self.context, ptr);
}
pub fn realloc(self: Context, ptr: ?*anyopaque, size: usize) error{OutOfMemory}!*anyopaque {
return import.js_realloc(self.context, ptr, size) orelse error.OutOfMemory;
}
pub fn mallocUsableSize(self: Context, ptr: ?*const anyopaque) usize {
return import.js_malloc_usable_size(self.context, ptr);
}
pub const Realloc2Result = struct {
ptr: *anyopaque,
slack: usize,
};
pub fn realloc2(self: Context, ptr: ?*anyopaque, size: usize) error{OutOfMemory}!Realloc2Result {
var slack: usize = undefined;
if (import.js_realloc2(self.context, ptr, size, &slack)) |next_ptr| {
return .{ .ptr = next_ptr, .slack = slack };
} else {
return error.OutOfMemory;
}
}
pub fn mallocz(self: Context, size: usize) error{OutOfMemory}!*anyopaque {
return import.js_mallocz(self.context, size) orelse error.OutOfMemory;
}
pub fn strdup(self: Context, str: [*:0]const u8) error{OutOfMemory}![*:0]u8 {
return import.js_strdup(self.context, str) orelse error.OutOfMemory;
}
pub fn strndup(self: Context, s: [*]const u8, n: usize) error{OutOfMemory}![*]u8 {
return import.js_strndup(self.context, s, n) orelse error.OutOfMemory;
}
pub fn newAtomLen(self: Context, str: []const u8) Atom {
return .{ .atom = import.JS_NewAtomLen(self.context, str.ptr, str.len) };
}
pub fn newAtom(self: Context, str: [*:0]const u8) Atom {
return .{ .atom = import.JS_NewAtom(self.context, str) };
}
pub fn newAtomUInt32(self: Context, n: u32) Atom {
return .{ .atom = import.JS_NewAtomUInt32(self.context, n) };
}
pub fn dupAtom(self: Context, v: Atom) Atom {
return .{ .atom = import.JS_DupAtom(self.context, v.atom) };
}
pub fn freeAtom(self: Context, v: Atom) void {
import.JS_FreeAtom(self.context, v.atom);
}
pub fn atomToValue(self: Context, atom: Atom) Value {
return .{ .value = import.JS_AtomToValue(self.context, atom.atom) };
}
pub fn atomToString(self: Context, atom: Atom) Value {
return .{ .value = import.JS_AtomToString(self.context, atom.atom) };
}
pub fn atomToCString(self: Context, atom: Atom) ?[:0]const u8 {
var len: usize = undefined;
if (import.JS_AtomToCStringLen(self.context, &len, atom.atom)) |ptr| {
return ptr[0..len :0];
} else {
return null;
}
}
pub fn valueToAtom(self: Context, val: Value) Atom {
return .{ .atom = import.JS_ValueToAtom(self.context, val.value) };
}
pub fn newBool(self: Context, val: bool) Value {
return .{ .value = import.JS_NewBool(self.context, @intFromBool(val)) };
}
pub fn newInt32(self: Context, val: i32) Value {
return .{ .value = import.JS_NewInt32(self.context, val) };
}
pub fn newCatchOffset(self: Context, val: i32) Value {
return .{ .value = import.JS_NewCatchOffset(self.context, val) };
}
pub fn newInt64(self: Context, val: i64) Value {
return .{ .value = import.JS_NewInt64(self.context, val) };
}
pub fn newUint32(self: Context, val: u32) Value {
return .{ .value = import.JS_NewUint32(self.context, val) };
}
pub fn newFloat64(self: Context, val: f64) Value {
return .{ .value = import.JS_NewFloat64(self.context, val) };
}
pub fn newBigInt64(self: Context, val: i64) Value {
return .{ .value = import.JS_NewBigInt64(self.context, val) };
}
pub fn throw(self: Context, obj: Value) Value {
return .{ .value = import.JS_Throw(self.context, obj.value) };
}
pub fn setUncatchableException(self: Context, flag: bool) void {
import.JS_SetUncatchableException(self.context, @intFromBool(flag));
}
pub fn getException(self: Context) Value {
return .{ .value = import.JS_GetException(self.context) };
}
pub fn hasException(self: Context) bool {
return import.JS_HasException(self.context) != 0;
}
pub fn isError(self: Context, val: Value) bool {
return import.JS_IsError(self.context, val.value) != 0;
}
pub fn newError(self: Context) Value {
return .{ .value = import.JS_NewError(self.context) };
}
pub fn throwOutOfMemory(self: Context) Value {
return .{ .value = import.JS_ThrowOutOfMemory(self.context) };
}
pub fn freeValue(self: Context, v: Value) void {
import.JS_FreeValue(self.context, v.value);
}
pub fn dupValue(self: Context, v: Value) Value {
return .{ .value = import.JS_DupValue(self.context, v.value) };
}
pub fn strictEq(self: Context, op1: Value, op2: Value) bool {
return import.JS_StrictEq(self.context, op1.value, op2.value) != 0;
}
pub fn sameValue(self: Context, op1: Value, op2: Value) bool {
return import.JS_SameValue(self.context, op1.value, op2.value) != 0;
}
pub fn sameValueZero(self: Context, op1: Value, op2: Value) bool {
return import.JS_SameValueZero(self.context, op1.value, op2.value) != 0;
}
pub fn newObject(self: Context) Value {
return .{ .value = import.JS_NewObject(self.context) };
}
pub fn newObjectProto(self: Context, proto: Value) Value {
return .{ .value = import.JS_NewObjectProto(self.context, proto.value) };
}
pub fn isFunction(self: Context, val: Value) bool {
return import.JS_IsFunction(self.context, val.value) != 0;
}
pub fn isConstructor(self: Context, val: Value) bool {
return import.JS_IsConstructor(self.context, val.value) != 0;
}
pub fn setConstructorBit(self: Context, func_obj: Value, val: bool) bool {
return import.JS_SetConstructorBit(self.context, func_obj.value, @intFromBool(val)) != 0;
}
pub fn newArray(self: Context) Value {
return .{ .value = import.JS_NewArray(self.context) };
}
pub fn isArray(self: Context, val: Value) bool {
return import.JS_IsArray(self.context, val.value) != 0;
}
pub fn newDate(self: Context, epoch_ms: f64) Value {
return .{ .value = import.JS_NewDate(self.context, epoch_ms) };
}
pub fn eval(self: Context, input: [:0]const u8, filename: [*:0]const u8, eval_flags: import.JS_EVAL) Value {
return .{ .value = import.JS_Eval(self.context, input.ptr, input.len, filename, eval_flags) };
}
pub fn evalThis(self: Context, this_obj: Value, input: [:0]const u8, filename: [*:0]const u8, eval_flags: import.JS_EVAL) Value {
return .{ .value = import.JS_EvalThis(self.context, this_obj.value, input.ptr, input.len, filename, eval_flags) };
}
};
pub const Value = extern struct {
value: import.JSValue,
pub const @"null": Value = .{ .value = import.JS_NULL };
pub const @"undefined": Value = .{ .value = import.JS_UNDEFINED };
pub const @"false": Value = .{ .value = import.JS_FALSE };
pub const @"true": Value = .{ .value = import.JS_TRUE };
pub const exception: Value = .{ .value = import.JS_EXCEPTION };
pub const uninitialized: Value = .{ .value = import.JS_UNINITIALIZED };
pub fn isNumber(self: Value) bool {
return import.JS_IsNumber(self.value);
}
pub fn isBigInt(self: Value) bool {
return import.JS_IsBigInt(self.value);
}
pub fn isBool(self: Value) bool {
return import.JS_IsBool(self.value);
}
pub fn isNull(self: Value) bool {
return import.JS_IsNull(self.value);
}
pub fn isUndefined(self: Value) bool {
return import.JS_IsUndefined(self.value);
}
pub fn isException(self: Value) bool {
return import.JS_IsException(self.value);
}
pub fn isUninitialized(self: Value) bool {
return import.JS_IsUninitialized(self.value);
}
pub fn isString(self: Value) bool {
return import.JS_IsString(self.value);
}
pub fn isSymbol(self: Value) bool {
return import.JS_IsSymbol(self.value);
}
pub fn isObject(self: Value) bool {
return import.JS_IsObject(self.value);
}
pub fn getClassId(self: Value) ClassId {
return .{ .class_id = import.JS_GetClassID(self) };
}
};
pub const Atom = extern struct {
atom: import.JSAtom,
};
pub const ClassId = extern struct {
class_id: import.JSClassID,
pub const invalid: ClassId = .{ .class_id = 0 };
pub fn new() ClassId {
var class_id: import.JSClassId = 0;
return .{ .class_id = import.JS_NewClassID(&class_id) };
}
};
test "refAllDecls" {
std.testing.refAllDeclsRecursive(import);
std.testing.refAllDeclsRecursive(@This());
}
test "runtime and context - custom allocator" {
const allocator = std.testing.allocator;
var runtime = try Runtime.newRuntimeAllocator(&allocator);
defer runtime.freeRuntime();
var context = try runtime.newContext();
defer context.freeContext();
}
test "eval" {
const allocator = std.testing.allocator;
var runtime = try Runtime.newRuntimeAllocator(&allocator);
defer runtime.freeRuntime();
var context = try runtime.newContext();
defer context.freeContext();
const result = context.eval("1 + 2", "eval.js", .{});
try std.testing.expectEqual(.INT, import.JS_VALUE_GET_TAG(result.value));
try std.testing.expectEqual(3, import.JS_VALUE_GET_INT(result.value));
}