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)); }