diff options
Diffstat (limited to 'src/javascript/jsc/api')
-rw-r--r-- | src/javascript/jsc/api/FFI.h | 252 | ||||
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 3007 | ||||
-rw-r--r-- | src/javascript/jsc/api/ffi.zig | 1436 | ||||
-rw-r--r-- | src/javascript/jsc/api/html_rewriter.zig | 1886 | ||||
-rw-r--r-- | src/javascript/jsc/api/libtcc1.a.macos-aarch64 | bin | 29988 -> 0 bytes | |||
-rw-r--r-- | src/javascript/jsc/api/libtcc1.c | 606 | ||||
-rw-r--r-- | src/javascript/jsc/api/router.zig | 541 | ||||
-rw-r--r-- | src/javascript/jsc/api/server.zig | 1844 | ||||
-rw-r--r-- | src/javascript/jsc/api/transpiler.zig | 1304 |
9 files changed, 0 insertions, 10876 deletions
diff --git a/src/javascript/jsc/api/FFI.h b/src/javascript/jsc/api/FFI.h deleted file mode 100644 index 75ac3171d..000000000 --- a/src/javascript/jsc/api/FFI.h +++ /dev/null @@ -1,252 +0,0 @@ -// This file is part of Bun! -// You can find the original source: -// https://github.com/Jarred-Sumner/bun/blob/main/src/javascript/jsc/api/FFI.h#L2 -// -// clang-format off -// This file is only compatible with 64 bit CPUs -// It must be kept in sync with JSCJSValue.h -// https://github.com/Jarred-Sumner/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458 -#ifdef IS_CALLBACK -#define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call -#endif -#define IS_BIG_ENDIAN 0 -#define USE_JSVALUE64 1 -#define USE_JSVALUE32_64 0 - - -// /* 7.18.1.1 Exact-width integer types */ -typedef unsigned char uint8_t; -typedef signed char int8_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef int int32_t; -typedef unsigned int uint32_t; -typedef long long int64_t; -typedef unsigned long long uint64_t; -typedef unsigned long long size_t; -typedef long intptr_t; -typedef uint64_t uintptr_t; -typedef _Bool bool; - -#define true 1 -#define false 0 - - -#ifdef INJECT_BEFORE -// #include <stdint.h> -#endif -// #include <tcclib.h> - -// This value is 2^49, used to encode doubles such that the encoded value will -// begin with a 15-bit pattern within the range 0x0002..0xFFFC. -#define DoubleEncodeOffsetBit 49 -#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit) -#define OtherTag 0x2 -#define BoolTag 0x4 -#define UndefinedTag 0x8 -#define TagValueFalse (OtherTag | BoolTag | false) -#define TagValueTrue (OtherTag | BoolTag | true) -#define TagValueUndefined (OtherTag | UndefinedTag) -#define TagValueNull (OtherTag) -#define NotCellMask NumberTag | OtherTag - -#define MAX_INT32 2147483648 -#define MAX_INT52 9007199254740991 - -// If all bits in the mask are set, this indicates an integer number, -// if any but not all are set this value is a double precision number. -#define NumberTag 0xfffe000000000000ll - -typedef void* JSCell; - -typedef union EncodedJSValue { - int64_t asInt64; - -#if USE_JSVALUE64 - JSCell *ptr; -#endif - -#if IS_BIG_ENDIAN - struct { - int32_t tag; - int32_t payload; - } asBits; -#else - struct { - int32_t payload; - int32_t tag; - } asBits; -#endif - - void* asPtr; - double asDouble; -} EncodedJSValue; - -EncodedJSValue ValueUndefined = { TagValueUndefined }; -EncodedJSValue ValueTrue = { TagValueTrue }; - -typedef void* JSContext; - -// Bun_FFI_PointerOffsetToArgumentsList is injected into the build -// The value is generated in `make sizegen` -// The value is 6. -// On ARM64_32, the value is something else but it really doesn't matter for our case -// However, I don't want this to subtly break amidst future upgrades to JavaScriptCore -#define LOAD_ARGUMENTS_FROM_CALL_FRAME \ - int64_t *argsPtr = (int64_t*)((size_t*)callFrame + Bun_FFI_PointerOffsetToArgumentsList) - - -#ifdef IS_CALLBACK -extern int64_t bun_call(JSContext, void* func, void* thisValue, size_t len, const EncodedJSValue args[], void* exception); -JSContext cachedJSContext; -void* cachedCallbackFunction; -#endif - -static bool JSVALUE_IS_CELL(EncodedJSValue val) __attribute__((__always_inline__)); -static bool JSVALUE_IS_INT32(EncodedJSValue val) __attribute__((__always_inline__)); -static bool JSVALUE_IS_NUMBER(EncodedJSValue val) __attribute__((__always_inline__)); - -static uint64_t JSVALUE_TO_UINT64(void* globalObject, EncodedJSValue value) __attribute__((__always_inline__)); -static int64_t JSVALUE_TO_INT64(EncodedJSValue value) __attribute__((__always_inline__)); -uint64_t JSVALUE_TO_UINT64_SLOW(void* globalObject, EncodedJSValue value); -int64_t JSVALUE_TO_INT64_SLOW(EncodedJSValue value); - -EncodedJSValue UINT64_TO_JSVALUE_SLOW(void* globalObject, uint64_t val); -EncodedJSValue INT64_TO_JSVALUE_SLOW(void* globalObject, int64_t val); -static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) __attribute__((__always_inline__)); -static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) __attribute__((__always_inline__)); - - -static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__)); -static EncodedJSValue DOUBLE_TO_JSVALUE(double val) __attribute__((__always_inline__)); -static EncodedJSValue FLOAT_TO_JSVALUE(float val) __attribute__((__always_inline__)); -static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) __attribute__((__always_inline__)); -static EncodedJSValue PTR_TO_JSVALUE(void* ptr) __attribute__((__always_inline__)); - -static void* JSVALUE_TO_PTR(EncodedJSValue val) __attribute__((__always_inline__)); -static int32_t JSVALUE_TO_INT32(EncodedJSValue val) __attribute__((__always_inline__)); -static float JSVALUE_TO_FLOAT(EncodedJSValue val) __attribute__((__always_inline__)); -static double JSVALUE_TO_DOUBLE(EncodedJSValue val) __attribute__((__always_inline__)); -static bool JSVALUE_TO_BOOL(EncodedJSValue val) __attribute__((__always_inline__)); - -static bool JSVALUE_IS_CELL(EncodedJSValue val) { - return !(val.asInt64 & NotCellMask); -} - -static bool JSVALUE_IS_INT32(EncodedJSValue val) { - return (val.asInt64 & NumberTag) == NumberTag; -} - -static bool JSVALUE_IS_NUMBER(EncodedJSValue val) { - return val.asInt64 & NumberTag; -} - - -static void* JSVALUE_TO_PTR(EncodedJSValue val) { - // must be a double - return (void*)(val.asInt64 - DoubleEncodeOffset); -} - -static EncodedJSValue PTR_TO_JSVALUE(void* ptr) { - EncodedJSValue val; - val.asInt64 = (int64_t)ptr + DoubleEncodeOffset; - return val; -} - -static int32_t JSVALUE_TO_INT32(EncodedJSValue val) { - return val.asInt64; -} - -static EncodedJSValue INT32_TO_JSVALUE(int32_t val) { - EncodedJSValue res; - res.asInt64 = NumberTag | (uint32_t)val; - return res; -} - - -static EncodedJSValue DOUBLE_TO_JSVALUE(double val) { - EncodedJSValue res; - res.asDouble = val; - res.asInt64 += DoubleEncodeOffset; - return res; -} - -static EncodedJSValue FLOAT_TO_JSVALUE(float val) { - return DOUBLE_TO_JSVALUE((double)val); -} - -static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) { - EncodedJSValue res; - res.asInt64 = val ? TagValueTrue : TagValueFalse; - return res; -} - - -static double JSVALUE_TO_DOUBLE(EncodedJSValue val) { - val.asInt64 -= DoubleEncodeOffset; - return val.asDouble; -} - -static float JSVALUE_TO_FLOAT(EncodedJSValue val) { - return (float)JSVALUE_TO_DOUBLE(val); -} - -static bool JSVALUE_TO_BOOL(EncodedJSValue val) { - return val.asInt64 == TagValueTrue; -} - - -static uint64_t JSVALUE_TO_UINT64(void* globalObject, EncodedJSValue value) { - if (JSVALUE_IS_INT32(value)) { - return (uint64_t)JSVALUE_TO_INT32(value); - } - - if (JSVALUE_IS_NUMBER(value)) { - return (uint64_t)JSVALUE_TO_DOUBLE(value); - } - - return JSVALUE_TO_UINT64_SLOW(globalObject, value); -} -static int64_t JSVALUE_TO_INT64(EncodedJSValue value) { - if (JSVALUE_IS_INT32(value)) { - return (int64_t)JSVALUE_TO_INT32(value); - } - - if (JSVALUE_IS_NUMBER(value)) { - return (int64_t)JSVALUE_TO_DOUBLE(value); - } - - return JSVALUE_TO_INT64_SLOW(value); -} - -static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) { - if (val < MAX_INT32) { - return INT32_TO_JSVALUE((int32_t)val); - } - - if (val < MAX_INT52) { - return DOUBLE_TO_JSVALUE((double)val); - } - - return UINT64_TO_JSVALUE_SLOW(globalObject, val); -} - -static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) { - if (val >= -MAX_INT32 && val <= MAX_INT32) { - return INT32_TO_JSVALUE((int32_t)val); - } - - if (val >= -MAX_INT52 && val <= MAX_INT52) { - return DOUBLE_TO_JSVALUE((double)val); - } - - return INT64_TO_JSVALUE_SLOW(globalObject, val); -} - -#ifndef IS_CALLBACK -void* JSFunctionCall(void* globalObject, void* callFrame); - -#endif - - -// --- Generated Code --- diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig deleted file mode 100644 index f062a5130..000000000 --- a/src/javascript/jsc/api/bun.zig +++ /dev/null @@ -1,3007 +0,0 @@ -const Bun = @This(); -const default_allocator = @import("../../../global.zig").default_allocator; -const bun = @import("../../../global.zig"); -const Environment = bun.Environment; -const NetworkThread = @import("http").NetworkThread; -const Global = bun.Global; -const strings = bun.strings; -const string = bun.string; -const Output = @import("../../../global.zig").Output; -const MutableString = @import("../../../global.zig").MutableString; -const std = @import("std"); -const Allocator = std.mem.Allocator; -const IdentityContext = @import("../../../identity_context.zig").IdentityContext; -const Fs = @import("../../../fs.zig"); -const Resolver = @import("../../../resolver/resolver.zig"); -const ast = @import("../../../import_record.zig"); -const NodeModuleBundle = @import("../../../node_module_bundle.zig").NodeModuleBundle; -const MacroEntryPoint = @import("../../../bundler.zig").MacroEntryPoint; -const logger = @import("../../../logger.zig"); -const Api = @import("../../../api/schema.zig").Api; -const options = @import("../../../options.zig"); -const Bundler = @import("../../../bundler.zig").Bundler; -const ServerEntryPoint = @import("../../../bundler.zig").ServerEntryPoint; -const js_printer = @import("../../../js_printer.zig"); -const js_parser = @import("../../../js_parser.zig"); -const js_ast = @import("../../../js_ast.zig"); -const hash_map = @import("../../../hash_map.zig"); -const http = @import("../../../http.zig"); -const NodeFallbackModules = @import("../../../node_fallbacks.zig"); -const ImportKind = ast.ImportKind; -const Analytics = @import("../../../analytics/analytics_thread.zig"); -const ZigString = @import("../../../jsc.zig").ZigString; -const Runtime = @import("../../../runtime.zig"); -const Router = @import("./router.zig"); -const ImportRecord = ast.ImportRecord; -const DotEnv = @import("../../../env_loader.zig"); -const ParseResult = @import("../../../bundler.zig").ParseResult; -const PackageJSON = @import("../../../resolver/package_json.zig").PackageJSON; -const MacroRemap = @import("../../../resolver/package_json.zig").MacroMap; -const WebCore = @import("../../../jsc.zig").WebCore; -const Request = WebCore.Request; -const Response = WebCore.Response; -const Headers = WebCore.Headers; -const Fetch = WebCore.Fetch; -const FetchEvent = WebCore.FetchEvent; -const js = @import("../../../jsc.zig").C; -const JSC = @import("../../../jsc.zig"); -const JSError = @import("../base.zig").JSError; -const d = @import("../base.zig").d; -const MarkedArrayBuffer = @import("../base.zig").MarkedArrayBuffer; -const getAllocator = @import("../base.zig").getAllocator; -const JSValue = @import("../../../jsc.zig").JSValue; -const NewClass = @import("../base.zig").NewClass; -const Microtask = @import("../../../jsc.zig").Microtask; -const JSGlobalObject = @import("../../../jsc.zig").JSGlobalObject; -const ExceptionValueRef = @import("../../../jsc.zig").ExceptionValueRef; -const JSPrivateDataPtr = @import("../../../jsc.zig").JSPrivateDataPtr; -const ZigConsoleClient = @import("../../../jsc.zig").ZigConsoleClient; -const Node = @import("../../../jsc.zig").Node; -const ZigException = @import("../../../jsc.zig").ZigException; -const ZigStackTrace = @import("../../../jsc.zig").ZigStackTrace; -const ErrorableResolvedSource = @import("../../../jsc.zig").ErrorableResolvedSource; -const ResolvedSource = @import("../../../jsc.zig").ResolvedSource; -const JSPromise = @import("../../../jsc.zig").JSPromise; -const JSInternalPromise = @import("../../../jsc.zig").JSInternalPromise; -const JSModuleLoader = @import("../../../jsc.zig").JSModuleLoader; -const JSPromiseRejectionOperation = @import("../../../jsc.zig").JSPromiseRejectionOperation; -const Exception = @import("../../../jsc.zig").Exception; -const ErrorableZigString = @import("../../../jsc.zig").ErrorableZigString; -const ZigGlobalObject = @import("../../../jsc.zig").ZigGlobalObject; -const VM = @import("../../../jsc.zig").VM; -const JSFunction = @import("../../../jsc.zig").JSFunction; -const Config = @import("../config.zig"); -const URL = @import("../../../url.zig").URL; -const Transpiler = @import("./transpiler.zig"); -const VirtualMachine = @import("../javascript.zig").VirtualMachine; -const IOTask = JSC.IOTask; -const zlib = @import("../../../zlib.zig"); - -const is_bindgen = JSC.is_bindgen; -const max_addressible_memory = std.math.maxInt(u56); - -threadlocal var css_imports_list_strings: [512]ZigString = undefined; -threadlocal var css_imports_list: [512]Api.StringPointer = undefined; -threadlocal var css_imports_list_tail: u16 = 0; -threadlocal var css_imports_buf: std.ArrayList(u8) = undefined; -threadlocal var css_imports_buf_loaded: bool = false; - -threadlocal var routes_list_strings: [1024]ZigString = undefined; - -pub fn onImportCSS( - resolve_result: *const Resolver.Result, - import_record: *ImportRecord, - origin: URL, -) void { - if (!css_imports_buf_loaded) { - css_imports_buf = std.ArrayList(u8).initCapacity( - VirtualMachine.vm.allocator, - import_record.path.text.len, - ) catch unreachable; - css_imports_buf_loaded = true; - } - - var writer = css_imports_buf.writer(); - const offset = css_imports_buf.items.len; - css_imports_list[css_imports_list_tail] = .{ - .offset = @truncate(u32, offset), - .length = 0, - }; - getPublicPath(resolve_result.path_pair.primary.text, origin, @TypeOf(writer), writer); - const length = css_imports_buf.items.len - offset; - css_imports_list[css_imports_list_tail].length = @truncate(u32, length); - css_imports_list_tail += 1; -} - -pub fn flushCSSImports() void { - if (css_imports_buf_loaded) { - css_imports_buf.clearRetainingCapacity(); - css_imports_list_tail = 0; - } -} - -pub fn getCSSImports() []ZigString { - var i: u16 = 0; - const tail = css_imports_list_tail; - while (i < tail) : (i += 1) { - ZigString.fromStringPointer(css_imports_list[i], css_imports_buf.items, &css_imports_list_strings[i]); - } - return css_imports_list_strings[0..tail]; -} - -pub fn inspect( - // this - _: void, - ctx: js.JSContextRef, - // function - _: js.JSObjectRef, - // thisObject - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - if (arguments.len == 0) - return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); - - for (arguments) |arg| { - JSC.C.JSValueProtect(ctx, arg); - } - defer { - for (arguments) |arg| { - JSC.C.JSValueUnprotect(ctx, arg); - } - } - - // very stable memory address - var array = MutableString.init(getAllocator(ctx), 0) catch unreachable; - var buffered_writer_ = MutableString.BufferedWriter{ .context = &array }; - var buffered_writer = &buffered_writer_; - - var writer = buffered_writer.writer(); - const Writer = @TypeOf(writer); - // we buffer this because it'll almost always be < 4096 - // when it's under 4096, we want to avoid the dynamic allocation - ZigConsoleClient.format( - .Debug, - ctx.ptr(), - @ptrCast([*]const JSValue, arguments.ptr), - arguments.len, - Writer, - Writer, - writer, - false, - false, - false, - ); - buffered_writer.flush() catch { - return JSC.C.JSValueMakeUndefined(ctx); - }; - - // we are going to always clone to keep things simple for now - // the common case here will be stack-allocated, so it should be fine - var out = ZigString.init(array.toOwnedSliceLeaky()).withEncoding(); - const ret = out.toValueGC(ctx); - array.deinit(); - return ret.asObjectRef(); - - // // when it's a small thing, rely on GC to manage the memory - // if (writer.context.pos < 2048 and array.list.items.len == 0) { - // var slice = writer.context.buffer[0..writer.context.pos]; - // if (slice.len == 0) { - // return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); - // } - - // var zig_str = - // return zig_str.toValueGC(ctx.ptr()).asObjectRef(); - // } - - // // when it's a big thing, we will manage it - // { - // writer.context.flush() catch {}; - // var slice = writer.context.context.toOwnedSlice(); - - // var zig_str = ZigString.init(slice).withEncoding(); - // if (!zig_str.isUTF8()) { - // return zig_str.toExternalValue(ctx.ptr()).asObjectRef(); - // } else { - // return zig_str.toValueGC(ctx.ptr()).asObjectRef(); - // } - // } -} - -pub fn registerMacro( - // this - _: void, - ctx: js.JSContextRef, - // function - _: js.JSObjectRef, - // thisObject - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - if (arguments.len != 2 or !js.JSValueIsNumber(ctx, arguments[0])) { - JSError(getAllocator(ctx), "Internal error registering macros: invalid args", .{}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - // TODO: make this faster - const id = @truncate(i32, @floatToInt(i64, js.JSValueToNumber(ctx, arguments[0], exception))); - if (id == -1 or id == 0) { - JSError(getAllocator(ctx), "Internal error registering macros: invalid id", .{}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - if (!js.JSValueIsObject(ctx, arguments[1]) or !js.JSObjectIsFunction(ctx, arguments[1])) { - JSError(getAllocator(ctx), "Macro must be a function. Received: {s}", .{@tagName(js.JSValueGetType(ctx, arguments[1]))}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - var get_or_put_result = VirtualMachine.vm.macros.getOrPut(id) catch unreachable; - if (get_or_put_result.found_existing) { - js.JSValueUnprotect(ctx, get_or_put_result.value_ptr.*); - } - - js.JSValueProtect(ctx, arguments[1]); - get_or_put_result.value_ptr.* = arguments[1]; - - return js.JSValueMakeUndefined(ctx); -} - -pub fn getCWD( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.bundler.fs.top_level_dir).toValue(ctx.ptr()).asRef(); -} - -pub fn getOrigin( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.origin.origin).toValue(ctx.ptr()).asRef(); -} - -pub fn getStdin( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("BunSTDIN")); - if (existing.isEmpty()) { - var rare_data = JSC.VirtualMachine.vm.rareData(); - var store = rare_data.stdin(); - var blob = bun.default_allocator.create(JSC.WebCore.Blob) catch unreachable; - blob.* = JSC.WebCore.Blob.initWithStore(store, ctx.ptr()); - - return ctx.ptr().putCachedObject( - &ZigString.init("BunSTDIN"), - JSC.JSValue.fromRef(JSC.WebCore.Blob.Class.make(ctx, blob)), - ).asObjectRef(); - } - - return existing.asObjectRef(); -} - -pub fn getStderr( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("BunSTDERR")); - if (existing.isEmpty()) { - var rare_data = JSC.VirtualMachine.vm.rareData(); - var store = rare_data.stderr(); - var blob = bun.default_allocator.create(JSC.WebCore.Blob) catch unreachable; - blob.* = JSC.WebCore.Blob.initWithStore(store, ctx.ptr()); - - return ctx.ptr().putCachedObject( - &ZigString.init("BunSTDERR"), - JSC.JSValue.fromRef(JSC.WebCore.Blob.Class.make(ctx, blob)), - ).asObjectRef(); - } - - return existing.asObjectRef(); -} - -pub fn getStdout( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("BunSTDOUT")); - if (existing.isEmpty()) { - var rare_data = JSC.VirtualMachine.vm.rareData(); - var store = rare_data.stdout(); - var blob = bun.default_allocator.create(JSC.WebCore.Blob) catch unreachable; - blob.* = JSC.WebCore.Blob.initWithStore(store, ctx.ptr()); - - return ctx.ptr().putCachedObject( - &ZigString.init("BunSTDOUT"), - JSC.JSValue.fromRef(JSC.WebCore.Blob.Class.make(ctx, blob)), - ).asObjectRef(); - } - - return existing.asObjectRef(); -} - -pub fn enableANSIColors( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return js.JSValueMakeBoolean(ctx, Output.enable_ansi_colors); -} -pub fn getMain( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.main).toValue(ctx.ptr()).asRef(); -} - -pub fn getAssetPrefix( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.bundler.options.routes.asset_prefix_path).toValue(ctx.ptr()).asRef(); -} - -pub fn getArgv( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - if (comptime Environment.isWindows) { - @compileError("argv not supported on windows"); - } - - var argv_list = std.heap.stackFallback(128, getAllocator(ctx)); - var allocator = argv_list.get(); - var argv = allocator.alloc(ZigString, std.os.argv.len) catch unreachable; - defer if (argv.len > 128) allocator.free(argv); - for (std.os.argv) |arg, i| { - argv[i] = ZigString.init(std.mem.span(arg)); - } - - return JSValue.createStringArray(ctx.ptr(), argv.ptr, argv.len, true).asObjectRef(); -} - -pub fn getRoutesDir( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - if (!VirtualMachine.vm.bundler.options.routes.routes_enabled or VirtualMachine.vm.bundler.options.routes.dir.len == 0) { - return js.JSValueMakeUndefined(ctx); - } - - return ZigString.init(VirtualMachine.vm.bundler.options.routes.dir).toValue(ctx.ptr()).asRef(); -} - -pub fn getFilePath(ctx: js.JSContextRef, arguments: []const js.JSValueRef, buf: []u8, exception: js.ExceptionRef) ?string { - if (arguments.len != 1) { - JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); - return null; - } - - const value = arguments[0]; - if (js.JSValueIsString(ctx, value)) { - var out = ZigString.Empty; - JSValue.toZigString(JSValue.fromRef(value), &out, ctx.ptr()); - var out_slice = out.slice(); - - // The dots are kind of unnecessary. They'll be normalized. - if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) { - JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); - return null; - } - - var parts = [_]string{out_slice}; - // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice)) - var res = VirtualMachine.vm.bundler.fs.absBuf(&parts, buf); - - return res; - } else if (js.JSValueIsArray(ctx, value)) { - var temp_strings_list: [32]string = undefined; - var temp_strings_list_len: u8 = 0; - defer { - for (temp_strings_list[0..temp_strings_list_len]) |_, i| { - temp_strings_list[i] = ""; - } - } - - var iter = JSValue.fromRef(value).arrayIterator(ctx.ptr()); - while (iter.next()) |item| { - if (temp_strings_list_len >= temp_strings_list.len) { - break; - } - - if (!item.isString()) { - JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); - return null; - } - - var out = ZigString.Empty; - JSValue.toZigString(item, &out, ctx.ptr()); - const out_slice = out.slice(); - - temp_strings_list[temp_strings_list_len] = out_slice; - // The dots are kind of unnecessary. They'll be normalized. - if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) { - JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); - return null; - } - temp_strings_list_len += 1; - } - - if (temp_strings_list_len == 0) { - JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); - return null; - } - - return VirtualMachine.vm.bundler.fs.absBuf(temp_strings_list[0..temp_strings_list_len], buf); - } else { - JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); - return null; - } -} - -pub fn getImportedStyles( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - defer flushCSSImports(); - const styles = getCSSImports(); - if (styles.len == 0) { - return js.JSObjectMakeArray(ctx, 0, null, null); - } - - return JSValue.createStringArray(ctx.ptr(), styles.ptr, styles.len, true).asRef(); -} - -pub fn newPath( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - args: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - const is_windows = args.len == 1 and JSValue.fromRef(args[0]).toBoolean(); - return Node.Path.create(ctx.ptr(), is_windows).asObjectRef(); -} - -pub fn readFileAsStringCallback( - ctx: js.JSContextRef, - buf_z: [:0]const u8, - exception: js.ExceptionRef, -) js.JSValueRef { - const path = buf_z.ptr[0..buf_z.len]; - var file = std.fs.cwd().openFileZ(buf_z, .{ .mode = .read_only }) catch |err| { - JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - defer file.close(); - - const stat = file.stat() catch |err| { - JSError(getAllocator(ctx), "Getting file size {s} for \"{s}\"", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - if (stat.kind != .File) { - JSError(getAllocator(ctx), "Can't read a {s} as a string (\"{s}\")", .{ @tagName(stat.kind), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - var contents_buf = VirtualMachine.vm.allocator.alloc(u8, stat.size + 2) catch unreachable; // OOM - defer VirtualMachine.vm.allocator.free(contents_buf); - const contents_len = file.readAll(contents_buf) catch |err| { - JSError(getAllocator(ctx), "{s} reading file (\"{s}\")", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - contents_buf[contents_len] = 0; - - // Very slow to do it this way. We're copying the string twice. - // But it's important that this string is garbage collected instead of manually managed. - // We can't really recycle this one. - // TODO: use external string - return js.JSValueMakeString(ctx, js.JSStringCreateWithUTF8CString(contents_buf.ptr)); -} - -pub fn readFileAsBytesCallback( - ctx: js.JSContextRef, - buf_z: [:0]const u8, - exception: js.ExceptionRef, -) js.JSValueRef { - const path = buf_z.ptr[0..buf_z.len]; - - var file = std.fs.cwd().openFileZ(buf_z, .{ .mode = .read_only }) catch |err| { - JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - defer file.close(); - - const stat = file.stat() catch |err| { - JSError(getAllocator(ctx), "Getting file size {s} for \"{s}\"", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - if (stat.kind != .File) { - JSError(getAllocator(ctx), "Can't read a {s} as a string (\"{s}\")", .{ @tagName(stat.kind), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - var contents_buf = VirtualMachine.vm.allocator.alloc(u8, stat.size + 2) catch unreachable; // OOM - errdefer VirtualMachine.vm.allocator.free(contents_buf); - const contents_len = file.readAll(contents_buf) catch |err| { - JSError(getAllocator(ctx), "{s} reading file (\"{s}\")", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - contents_buf[contents_len] = 0; - - var marked_array_buffer = VirtualMachine.vm.allocator.create(MarkedArrayBuffer) catch unreachable; - marked_array_buffer.* = MarkedArrayBuffer.fromBytes( - contents_buf[0..contents_len], - VirtualMachine.vm.allocator, - .Uint8Array, - ); - - return marked_array_buffer.toJSObjectRef(ctx, exception); -} - -pub fn getRouteFiles( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - if (VirtualMachine.vm.bundler.router == null) return js.JSObjectMakeArray(ctx, 0, null, null); - - const router = &VirtualMachine.vm.bundler.router.?; - const list = router.getPublicPaths() catch unreachable; - - for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| { - routes_list_strings[i] = ZigString.init(list[i]); - } - - const ref = JSValue.createStringArray(ctx.ptr(), &routes_list_strings, list.len, true).asRef(); - return ref; -} - -pub fn getRouteNames( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - if (VirtualMachine.vm.bundler.router == null) return js.JSObjectMakeArray(ctx, 0, null, null); - - const router = &VirtualMachine.vm.bundler.router.?; - const list = router.getNames() catch unreachable; - - for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| { - routes_list_strings[i] = ZigString.init(list[i]); - } - - const ref = JSValue.createStringArray(ctx.ptr(), &routes_list_strings, list.len, true).asRef(); - return ref; -} - -const Editor = @import("../../../open.zig").Editor; -pub fn openInEditor( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - args: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - var edit = &VirtualMachine.vm.rareData().editor_context; - - var arguments = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), args); - defer arguments.deinit(); - var path: string = ""; - var editor_choice: ?Editor = null; - var line: ?string = null; - var column: ?string = null; - - if (arguments.nextEat()) |file_path_| { - path = file_path_.toSlice(ctx.ptr(), bun.default_allocator).slice(); - } - - if (arguments.nextEat()) |opts| { - if (!opts.isUndefinedOrNull()) { - if (opts.getTruthy(ctx.ptr(), "editor")) |editor_val| { - var sliced = editor_val.toSlice(ctx.ptr(), bun.default_allocator); - var prev_name = edit.name; - - if (!strings.eqlLong(prev_name, sliced.slice(), true)) { - var prev = edit.*; - edit.name = sliced.slice(); - edit.detectEditor(VirtualMachine.vm.bundler.env); - editor_choice = edit.editor; - if (editor_choice == null) { - edit.* = prev; - JSError(getAllocator(ctx), "Could not find editor \"{s}\"", .{sliced.slice()}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } else if (edit.name.ptr == edit.path.ptr) { - edit.name = bun.default_allocator.dupe(u8, edit.path) catch unreachable; - edit.path = edit.path; - } - } - } - - if (opts.getTruthy(ctx.ptr(), "line")) |line_| { - line = line_.toSlice(ctx.ptr(), bun.default_allocator).slice(); - } - - if (opts.getTruthy(ctx.ptr(), "column")) |column_| { - column = column_.toSlice(ctx.ptr(), bun.default_allocator).slice(); - } - } - } - - const editor = editor_choice orelse edit.editor orelse brk: { - edit.autoDetectEditor(VirtualMachine.vm.bundler.env); - if (edit.editor == null) { - JSC.JSError(bun.default_allocator, "Failed to auto-detect editor", .{}, ctx, exception); - return null; - } - - break :brk edit.editor.?; - }; - - if (path.len == 0) { - JSError(getAllocator(ctx), "No file path specified", .{}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - editor.open(edit.path, path, line, column, bun.default_allocator) catch |err| { - JSC.JSError(bun.default_allocator, "Opening editor failed {s}", .{@errorName(err)}, ctx, exception); - return null; - }; - - return JSC.JSValue.jsUndefined().asObjectRef(); -} - -pub fn readFileAsBytes( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - var buf: [bun.MAX_PATH_BYTES]u8 = undefined; - const path = getFilePath(ctx, arguments, &buf, exception) orelse return null; - buf[path.len] = 0; - - const buf_z: [:0]const u8 = buf[0..path.len :0]; - const result = readFileAsBytesCallback(ctx, buf_z, exception); - return result; -} - -pub fn readFileAsString( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - var buf: [bun.MAX_PATH_BYTES]u8 = undefined; - const path = getFilePath(ctx, arguments, &buf, exception) orelse return null; - buf[path.len] = 0; - - const buf_z: [:0]const u8 = buf[0..path.len :0]; - const result = readFileAsStringCallback(ctx, buf_z, exception); - return result; -} - -pub fn getPublicPath(to: string, origin: URL, comptime Writer: type, writer: Writer) void { - const relative_path = VirtualMachine.vm.bundler.fs.relativeTo(to); - if (origin.isAbsolute()) { - if (strings.hasPrefix(relative_path, "..") or strings.hasPrefix(relative_path, "./")) { - writer.writeAll(origin.origin) catch return; - writer.writeAll("/abs:") catch return; - if (std.fs.path.isAbsolute(to)) { - writer.writeAll(to) catch return; - } else { - writer.writeAll(VirtualMachine.vm.bundler.fs.abs(&[_]string{to})) catch return; - } - } else { - origin.joinWrite( - Writer, - writer, - VirtualMachine.vm.bundler.options.routes.asset_prefix_path, - "", - relative_path, - "", - ) catch return; - } - } else { - writer.writeAll(std.mem.trimLeft(u8, relative_path, "/")) catch unreachable; - } -} - -pub fn sleepSync( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - if (js.JSValueIsNumber(ctx, arguments[0])) { - const seconds = JSValue.fromRef(arguments[0]).asNumber(); - if (seconds > 0 and std.math.isFinite(seconds)) std.time.sleep(@floatToInt(u64, seconds * 1000) * std.time.ns_per_ms); - } - - return js.JSValueMakeUndefined(ctx); -} - -pub fn createNodeFS( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - return Node.NodeFSBindings.make( - ctx, - VirtualMachine.vm.nodeFS(), - ); -} - -pub fn generateHeapSnapshot( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ctx.ptr().generateHeapSnapshot().asObjectRef(); -} - -pub fn runGC( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - // it should only force cleanup on thread exit - - Global.mimalloc_cleanup(false); - - return ctx.ptr().vm().runGC(arguments.len > 0 and JSValue.fromRef(arguments[0]).toBoolean()).asRef(); -} - -pub fn shrink( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - ctx.ptr().vm().shrinkFootprint(); - return JSValue.jsUndefined().asRef(); -} - -fn doResolve( - ctx: js.JSContextRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) ?JSC.JSValue { - var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); - defer args.deinit(); - const specifier = args.protectEatNext() orelse { - JSC.throwInvalidArguments("Expected a specifier and a from path", .{}, ctx, exception); - return null; - }; - - if (specifier.isUndefinedOrNull()) { - JSC.throwInvalidArguments("specifier must be a string", .{}, ctx, exception); - return null; - } - - const from = args.protectEatNext() orelse { - JSC.throwInvalidArguments("Expected a from path", .{}, ctx, exception); - return null; - }; - - if (from.isUndefinedOrNull()) { - JSC.throwInvalidArguments("from must be a string", .{}, ctx, exception); - return null; - } - - return doResolveWithArgs(ctx, specifier.getZigString(ctx.ptr()), from.getZigString(ctx.ptr()), exception, false); -} - -fn doResolveWithArgs( - ctx: js.JSContextRef, - specifier: ZigString, - from: ZigString, - exception: js.ExceptionRef, - comptime is_file_path: bool, -) ?JSC.JSValue { - var errorable: ErrorableZigString = undefined; - - if (comptime is_file_path) { - VirtualMachine.resolveFilePathForAPI( - &errorable, - ctx.ptr(), - specifier, - from, - ); - } else { - VirtualMachine.resolveForAPI( - &errorable, - ctx.ptr(), - specifier, - from, - ); - } - - if (!errorable.success) { - exception.* = bun.cast(JSC.JSValueRef, errorable.result.err.ptr.?); - return null; - } - - return errorable.result.value.toValue(ctx.ptr()); -} - -pub fn resolveSync( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - const value = doResolve(ctx, arguments, exception) orelse return null; - return value.asObjectRef(); -} - -pub fn resolve( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - const value = doResolve(ctx, arguments, exception) orelse { - var exception_value = exception.*.?; - exception.* = null; - return JSC.JSPromise.rejectedPromiseValue(ctx.ptr(), JSC.JSValue.fromRef(exception_value)).asObjectRef(); - }; - return JSC.JSPromise.resolvedPromiseValue(ctx.ptr(), value).asObjectRef(); -} - -export fn Bun__resolve( - global: *JSGlobalObject, - specifier: JSValue, - source: JSValue, -) JSC.JSValue { - var exception_ = [1]JSC.JSValueRef{null}; - var exception = &exception_; - const value = doResolveWithArgs(global.ref(), specifier.getZigString(global), source.getZigString(global), exception, true) orelse { - return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0])); - }; - return JSC.JSPromise.resolvedPromiseValue(global, value); -} - -export fn Bun__resolveSync( - global: *JSGlobalObject, - specifier: JSValue, - source: JSValue, -) JSC.JSValue { - var exception_ = [1]JSC.JSValueRef{null}; - var exception = &exception_; - return doResolveWithArgs(global.ref(), specifier.getZigString(global), source.getZigString(global), exception, true) orelse { - return JSC.JSValue.fromRef(exception[0]); - }; -} - -comptime { - if (!is_bindgen) { - _ = Bun__resolve; - _ = Bun__resolveSync; - } -} - -pub fn readAllStdinSync( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - var stack = std.heap.stackFallback(2048, getAllocator(ctx)); - var allocator = stack.get(); - - var stdin = std.io.getStdIn(); - var result = stdin.readToEndAlloc(allocator, std.math.maxInt(u32)) catch |err| { - JSError(undefined, "{s} reading stdin", .{@errorName(err)}, ctx, exception); - return null; - }; - var out = ZigString.init(result); - out.detectEncoding(); - return out.toValueGC(ctx.ptr()).asObjectRef(); -} - -var public_path_temp_str: [bun.MAX_PATH_BYTES]u8 = undefined; - -pub fn getPublicPathJS( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSValueRef { - var zig_str: ZigString = ZigString.Empty; - JSValue.toZigString(JSValue.fromRef(arguments[0]), &zig_str, ctx.ptr()); - - const to = zig_str.slice(); - - var stream = std.io.fixedBufferStream(&public_path_temp_str); - var writer = stream.writer(); - getPublicPath(to, VirtualMachine.vm.origin, @TypeOf(&writer), &writer); - - return ZigString.init(stream.buffer[0..stream.pos]).toValueGC(ctx.ptr()).asObjectRef(); -} - -pub const Class = NewClass( - void, - .{ - .name = "Bun", - .read_only = true, - }, - .{ - .match = .{ - .rfn = Router.match, - .ts = Router.match_type_definition, - }, - .sleepSync = .{ - .rfn = sleepSync, - }, - .fetch = .{ - .rfn = Fetch.call, - .ts = d.ts{}, - }, - .getImportedStyles = .{ - .rfn = Bun.getImportedStyles, - .ts = d.ts{ - .name = "getImportedStyles", - .@"return" = "string[]", - }, - }, - .inspect = .{ - .rfn = Bun.inspect, - .ts = d.ts{ - .name = "inspect", - .@"return" = "string", - }, - }, - .getRouteFiles = .{ - .rfn = Bun.getRouteFiles, - .ts = d.ts{ - .name = "getRouteFiles", - .@"return" = "string[]", - }, - }, - ._Path = .{ - .rfn = Bun.newPath, - .ts = d.ts{}, - }, - .getRouteNames = .{ - .rfn = Bun.getRouteNames, - .ts = d.ts{ - .name = "getRouteNames", - .@"return" = "string[]", - }, - }, - .readFile = .{ - .rfn = Bun.readFileAsString, - .ts = d.ts{ - .name = "readFile", - .@"return" = "string", - }, - }, - .resolveSync = .{ - .rfn = Bun.resolveSync, - .ts = d.ts{ - .name = "resolveSync", - .@"return" = "string", - }, - }, - .resolve = .{ - .rfn = Bun.resolve, - .ts = d.ts{ - .name = "resolve", - .@"return" = "string", - }, - }, - .readFileBytes = .{ - .rfn = Bun.readFileAsBytes, - .ts = d.ts{ - .name = "readFile", - .@"return" = "Uint8Array", - }, - }, - .getPublicPath = .{ - .rfn = Bun.getPublicPathJS, - .ts = d.ts{ - .name = "getPublicPath", - .@"return" = "string", - }, - }, - .registerMacro = .{ - .rfn = Bun.registerMacro, - .ts = d.ts{ - .name = "registerMacro", - .@"return" = "undefined", - }, - .enumerable = false, - }, - .fs = .{ - .rfn = Bun.createNodeFS, - .ts = d.ts{}, - .enumerable = false, - }, - .jest = .{ - .rfn = @import("../test/jest.zig").Jest.call, - .ts = d.ts{}, - .enumerable = false, - }, - .gc = .{ - .rfn = Bun.runGC, - .ts = d.ts{}, - }, - .allocUnsafe = .{ - .rfn = Bun.allocUnsafe, - .ts = .{}, - }, - .mmap = .{ - .rfn = Bun.mmapFile, - .ts = .{}, - }, - .generateHeapSnapshot = .{ - .rfn = Bun.generateHeapSnapshot, - .ts = d.ts{}, - }, - .shrink = .{ - .rfn = Bun.shrink, - .ts = d.ts{}, - }, - .openInEditor = .{ - .rfn = Bun.openInEditor, - .ts = d.ts{}, - }, - .readAllStdinSync = .{ - .rfn = Bun.readAllStdinSync, - .ts = d.ts{}, - }, - .serve = .{ - .rfn = Bun.serve, - .ts = d.ts{}, - }, - .file = .{ - .rfn = JSC.WebCore.Blob.constructFile, - .ts = d.ts{}, - }, - .write = .{ - .rfn = JSC.WebCore.Blob.writeFile, - .ts = d.ts{}, - }, - .sha = .{ - .rfn = JSC.wrapWithHasContainer(Crypto.SHA512_256, "hash", false, false, true), - }, - .nanoseconds = .{ - .rfn = nanoseconds, - }, - .gzipSync = .{ - .rfn = JSC.wrapWithHasContainer(JSZlib, "gzipSync", false, false, true), - }, - .deflateSync = .{ - .rfn = JSC.wrapWithHasContainer(JSZlib, "deflateSync", false, false, true), - }, - .gunzipSync = .{ - .rfn = JSC.wrapWithHasContainer(JSZlib, "gunzipSync", false, false, true), - }, - .inflateSync = .{ - .rfn = JSC.wrapWithHasContainer(JSZlib, "inflateSync", false, false, true), - }, - }, - .{ - .main = .{ - .get = getMain, - .ts = d.ts{ .name = "main", .@"return" = "string" }, - }, - .cwd = .{ - .get = getCWD, - .ts = d.ts{ .name = "cwd", .@"return" = "string" }, - }, - .origin = .{ - .get = getOrigin, - .ts = d.ts{ .name = "origin", .@"return" = "string" }, - }, - .stdin = .{ - .get = getStdin, - }, - .stdout = .{ - .get = getStdout, - }, - .stderr = .{ - .get = getStderr, - }, - .routesDir = .{ - .get = getRoutesDir, - .ts = d.ts{ .name = "routesDir", .@"return" = "string" }, - }, - .assetPrefix = .{ - .get = getAssetPrefix, - .ts = d.ts{ .name = "assetPrefix", .@"return" = "string" }, - }, - .argv = .{ - .get = getArgv, - .ts = d.ts{ .name = "argv", .@"return" = "string[]" }, - }, - .env = .{ - .get = EnvironmentVariables.getter, - }, - - .enableANSIColors = .{ - .get = enableANSIColors, - }, - .Transpiler = .{ - .get = getTranspilerConstructor, - .ts = d.ts{ .name = "Transpiler", .@"return" = "Transpiler.prototype" }, - }, - .hash = .{ - .get = getHashObject, - }, - .TOML = .{ - .get = getTOMLObject, - .ts = d.ts{ .name = "TOML", .@"return" = "TOML.prototype" }, - }, - .unsafe = .{ - .get = getUnsafe, - }, - - .SHA1 = .{ - .get = Crypto.SHA1.getter, - }, - .MD5 = .{ - .get = Crypto.MD5.getter, - }, - .MD4 = .{ - .get = Crypto.MD4.getter, - }, - .SHA224 = .{ - .get = Crypto.SHA224.getter, - }, - .SHA512 = .{ - .get = Crypto.SHA512.getter, - }, - .SHA384 = .{ - .get = Crypto.SHA384.getter, - }, - .SHA256 = .{ - .get = Crypto.SHA256.getter, - }, - .SHA512_256 = .{ - .get = Crypto.SHA512_256.getter, - }, - .FFI = .{ - .get = FFI.getter, - }, - }, -); - -pub const Crypto = struct { - const Hashers = @import("../../../sha.zig"); - - fn CryptoHasher(comptime Hasher: type, comptime name: [:0]const u8, cached_constructor_name: []const u8) type { - return struct { - hashing: Hasher = Hasher{}, - - pub fn byteLength( - _: void, - _: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)).asObjectRef(); - } - - pub fn byteLength2( - _: *@This(), - _: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)).asObjectRef(); - } - - pub const Constructor = JSC.NewConstructor( - @This(), - .{ - .hash = .{ - .rfn = JSC.wrapWithHasContainer(@This(), "hash", false, false, true), - }, - .constructor = .{ .rfn = constructor }, - }, - .{ - .byteLength = .{ - .get = byteLength, - }, - }, - ); - - pub const Class = JSC.NewClass( - @This(), - .{ - .name = name, - }, - .{ - .update = .{ - .rfn = JSC.wrapSync(@This(), "update"), - }, - .digest = .{ - .rfn = JSC.wrapSync(@This(), "digest"), - }, - .finalize = finalize, - }, - .{ - .byteLength = .{ - .get = byteLength2, - }, - }, - ); - - fn hashToEncoding( - globalThis: *JSGlobalObject, - input: JSC.Node.StringOrBuffer, - encoding: JSC.Node.Encoding, - exception: JSC.C.ExceptionRef, - ) JSC.JSValue { - var output_digest_buf: Hasher.Digest = undefined; - - Hasher.hash(input.slice(), &output_digest_buf, JSC.VirtualMachine.vm.rareData().boringEngine()); - - return encoding.encodeWithSize(globalThis, Hasher.digest, &output_digest_buf, exception); - } - - fn hashToBytes( - globalThis: *JSGlobalObject, - input: JSC.Node.StringOrBuffer, - output: ?JSC.ArrayBuffer, - exception: JSC.C.ExceptionRef, - ) JSC.JSValue { - var output_digest_buf: Hasher.Digest = undefined; - var output_digest_slice: *Hasher.Digest = &output_digest_buf; - if (output) |output_buf| { - var bytes = output_buf.byteSlice(); - if (bytes.len < Hasher.digest) { - JSC.JSError( - bun.default_allocator, - comptime std.fmt.comptimePrint("TypedArray must be at least {d} bytes", .{Hasher.digest}), - .{}, - globalThis.ref(), - exception, - ); - return JSC.JSValue.zero; - } - output_digest_slice = bytes[0..Hasher.digest]; - } - - Hasher.hash(input.slice(), output_digest_slice, JSC.VirtualMachine.vm.rareData().boringEngine()); - - if (output) |output_buf| { - return output_buf.value; - } else { - var array_buffer_out = JSC.ArrayBuffer.fromBytes(bun.default_allocator.dupe(u8, output_digest_slice) catch unreachable, .Uint8Array); - return array_buffer_out.toJSUnchecked(globalThis.ref(), exception); - } - } - - pub fn hash( - globalThis: *JSGlobalObject, - input: JSC.Node.StringOrBuffer, - output: ?JSC.Node.StringOrBuffer, - exception: JSC.C.ExceptionRef, - ) JSC.JSValue { - if (output) |string_or_buffer| { - switch (string_or_buffer) { - .string => |str| { - const encoding = JSC.Node.Encoding.from(str) orelse { - JSC.JSError( - bun.default_allocator, - "Unknown encoding", - .{}, - globalThis.ref(), - exception, - ); - return JSC.JSValue.zero; - }; - - return hashToEncoding(globalThis, input, encoding, exception); - }, - .buffer => |buffer| { - return hashToBytes(globalThis, input, buffer.buffer, exception); - }, - } - } else { - return hashToBytes(globalThis, input, null, exception); - } - } - - pub fn constructor( - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSObjectRef { - var this = bun.default_allocator.create(@This()) catch { - JSC.JSError(bun.default_allocator, "Failed to create new object", .{}, ctx, exception); - return null; - }; - - this.* = .{ .hashing = Hasher.init() }; - return @This().Class.make(ctx, this); - } - - pub fn getter( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init(cached_constructor_name)); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - &ZigString.init(cached_constructor_name), - JSC.JSValue.fromRef(@This().Constructor.constructor(ctx)), - ).asObjectRef(); - } - - return existing.asObjectRef(); - } - - pub fn update(this: *@This(), thisObj: JSC.C.JSObjectRef, buffer: JSC.Node.StringOrBuffer) JSC.JSValue { - this.hashing.update(buffer.slice()); - return JSC.JSValue.c(thisObj); - } - - pub fn digest( - this: *@This(), - globalThis: *JSGlobalObject, - output: ?JSC.Node.StringOrBuffer, - exception: JSC.C.ExceptionRef, - ) JSC.JSValue { - if (output) |string_or_buffer| { - switch (string_or_buffer) { - .string => |str| { - const encoding = JSC.Node.Encoding.from(str) orelse { - JSC.JSError( - bun.default_allocator, - "Unknown encoding", - .{}, - globalThis.ref(), - exception, - ); - return JSC.JSValue.zero; - }; - - return this.digestToEncoding(globalThis, exception, encoding); - }, - .buffer => |buffer| { - return this.digestToBytes( - globalThis, - exception, - buffer.buffer, - ); - }, - } - } else { - return this.digestToBytes(globalThis, exception, null); - } - } - - fn digestToBytes(this: *@This(), globalThis: *JSGlobalObject, exception: JSC.C.ExceptionRef, output: ?JSC.ArrayBuffer) JSC.JSValue { - var output_digest_buf: Hasher.Digest = undefined; - var output_digest_slice: *Hasher.Digest = &output_digest_buf; - if (output) |output_buf| { - var bytes = output_buf.byteSlice(); - if (bytes.len < Hasher.digest) { - JSC.JSError( - bun.default_allocator, - comptime std.fmt.comptimePrint("TypedArray must be at least {d} bytes", .{@as(usize, Hasher.digest)}), - .{}, - globalThis.ref(), - exception, - ); - return JSC.JSValue.zero; - } - output_digest_slice = bytes[0..Hasher.digest]; - } else { - output_digest_buf = comptime brk: { - var bytes: Hasher.Digest = undefined; - var i: usize = 0; - while (i < Hasher.digest) { - bytes[i] = 0; - i += 1; - } - break :brk bytes; - }; - } - - this.hashing.final(output_digest_slice); - - if (output) |output_buf| { - return output_buf.value; - } else { - var array_buffer_out = JSC.ArrayBuffer.fromBytes(bun.default_allocator.dupe(u8, &output_digest_buf) catch unreachable, .Uint8Array); - return array_buffer_out.toJSUnchecked(globalThis.ref(), exception); - } - } - - fn digestToEncoding(this: *@This(), globalThis: *JSGlobalObject, exception: JSC.C.ExceptionRef, encoding: JSC.Node.Encoding) JSC.JSValue { - var output_digest_buf: Hasher.Digest = comptime brk: { - var bytes: Hasher.Digest = undefined; - var i: usize = 0; - while (i < Hasher.digest) { - bytes[i] = 0; - i += 1; - } - break :brk bytes; - }; - - var output_digest_slice: *Hasher.Digest = &output_digest_buf; - - this.hashing.final(output_digest_slice); - - return encoding.encodeWithSize(globalThis, Hasher.digest, output_digest_slice, exception); - } - - pub fn finalize(this: *@This()) void { - VirtualMachine.vm.allocator.destroy(this); - } - }; - } - - pub const SHA1 = CryptoHasher(Hashers.SHA1, "SHA1", "Bun_Crypto_SHA1"); - pub const MD5 = CryptoHasher(Hashers.MD5, "MD5", "Bun_Crypto_MD5"); - pub const MD4 = CryptoHasher(Hashers.MD4, "MD4", "Bun_Crypto_MD4"); - pub const SHA224 = CryptoHasher(Hashers.SHA224, "SHA224", "Bun_Crypto_SHA224"); - pub const SHA512 = CryptoHasher(Hashers.SHA512, "SHA512", "Bun_Crypto_SHA512"); - pub const SHA384 = CryptoHasher(Hashers.SHA384, "SHA384", "Bun_Crypto_SHA384"); - pub const SHA256 = CryptoHasher(Hashers.SHA256, "SHA256", "Bun_Crypto_SHA256"); - pub const SHA512_256 = CryptoHasher(Hashers.SHA512_256, "SHA512_256", "Bun_Crypto_SHA512_256"); - pub const MD5_SHA1 = CryptoHasher(Hashers.MD5_SHA1, "MD5_SHA1", "Bun_Crypto_MD5_SHA1"); -}; - -pub fn nanoseconds( - _: void, - _: JSC.C.JSContextRef, - _: JSC.C.JSObjectRef, - _: JSC.C.JSObjectRef, - _: []const JSC.C.JSValueRef, - _: JSC.C.ExceptionRef, -) JSC.C.JSValueRef { - const ns = JSC.VirtualMachine.vm.origin_timer.read(); - return JSC.JSValue.jsNumberFromUint64(ns).asObjectRef(); -} - -pub fn serve( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); - var config = JSC.API.ServerConfig.fromJS(ctx.ptr(), &args, exception); - if (exception.* != null) { - return null; - } - - // Listen happens on the next tick! - // This is so we can return a Server object - if (config.ssl_config != null) { - if (config.development) { - var server = JSC.API.DebugSSLServer.init(config, ctx.ptr()); - server.listen(); - if (!server.thisObject.isEmpty()) { - exception.* = server.thisObject.asObjectRef(); - server.thisObject = JSC.JSValue.zero; - server.deinit(); - return null; - } - var obj = JSC.API.DebugSSLServer.Class.make(ctx, server); - JSC.C.JSValueProtect(ctx, obj); - server.thisObject = JSValue.c(obj); - return obj; - } else { - var server = JSC.API.SSLServer.init(config, ctx.ptr()); - server.listen(); - if (!server.thisObject.isEmpty()) { - exception.* = server.thisObject.asObjectRef(); - server.thisObject = JSC.JSValue.zero; - server.deinit(); - return null; - } - var obj = JSC.API.SSLServer.Class.make(ctx, server); - JSC.C.JSValueProtect(ctx, obj); - server.thisObject = JSValue.c(obj); - return obj; - } - } else { - if (config.development) { - var server = JSC.API.DebugServer.init(config, ctx.ptr()); - server.listen(); - if (!server.thisObject.isEmpty()) { - exception.* = server.thisObject.asObjectRef(); - server.thisObject = JSC.JSValue.zero; - server.deinit(); - return null; - } - var obj = JSC.API.DebugServer.Class.make(ctx, server); - JSC.C.JSValueProtect(ctx, obj); - server.thisObject = JSValue.c(obj); - return obj; - } else { - var server = JSC.API.Server.init(config, ctx.ptr()); - server.listen(); - if (!server.thisObject.isEmpty()) { - exception.* = server.thisObject.asObjectRef(); - server.thisObject = JSC.JSValue.zero; - server.deinit(); - return null; - } - var obj = JSC.API.Server.Class.make(ctx, server); - JSC.C.JSValueProtect(ctx, obj); - server.thisObject = JSValue.c(obj); - return obj; - } - } - - unreachable; -} - -pub export fn Bun__escapeHTML( - globalObject: *JSGlobalObject, - callframe: *JSC.CallFrame, -) JSC.JSValue { - const arguments = callframe.arguments(); - if (arguments.len < 1) { - return ZigString.Empty.toValue(globalObject); - } - - const input_value = arguments[0]; - const zig_str = input_value.getZigString(globalObject); - if (zig_str.len == 0) - return ZigString.Empty.toValue(globalObject); - - if (zig_str.is16Bit()) { - const input_slice = zig_str.utf16SliceAligned(); - const escaped = strings.escapeHTMLForUTF16Input(globalObject.bunVM().allocator, input_slice) catch { - globalObject.vm().throwError(globalObject, ZigString.init("Out of memory").toValue(globalObject)); - return JSC.JSValue.jsUndefined(); - }; - - switch (escaped) { - .static => |val| { - return ZigString.init(val).toValue(globalObject); - }, - .original => return input_value, - .allocated => |escaped_html| { - if (comptime Environment.allow_assert) { - // assert that re-encoding the string produces the same result - std.debug.assert( - std.mem.eql( - u16, - (strings.toUTF16Alloc(bun.default_allocator, strings.toUTF8Alloc(bun.default_allocator, escaped_html) catch unreachable, false) catch unreachable).?, - escaped_html, - ), - ); - - // assert we do not allocate a new string unnecessarily - std.debug.assert( - !std.mem.eql( - u16, - input_slice, - escaped_html, - ), - ); - - // the output should always be longer than the input - std.debug.assert(escaped_html.len > input_slice.len); - } - - return ZigString.from16(escaped_html.ptr, escaped_html.len).toExternalValue(globalObject); - }, - } - } else { - const input_slice = zig_str.slice(); - const escaped = strings.escapeHTMLForLatin1Input(globalObject.bunVM().allocator, input_slice) catch { - globalObject.vm().throwError(globalObject, ZigString.init("Out of memory").toValue(globalObject)); - return JSC.JSValue.jsUndefined(); - }; - - switch (escaped) { - .static => |val| { - return ZigString.init(val).toValue(globalObject); - }, - .original => return input_value, - .allocated => |escaped_html| { - if (comptime Environment.allow_assert) { - // the output should always be longer than the input - std.debug.assert(escaped_html.len > input_slice.len); - - // assert we do not allocate a new string unnecessarily - std.debug.assert( - !std.mem.eql( - u8, - input_slice, - escaped_html, - ), - ); - } - - return ZigString.init(escaped_html).toExternalValue(globalObject); - }, - } - } -} - -comptime { - if (!JSC.is_bindgen) { - _ = Bun__escapeHTML; - } -} - -pub fn allocUnsafe( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); - - const length = @intCast( - usize, - @minimum( - @maximum(1, (args.nextEat() orelse JSC.JSValue.jsNumber(@as(i32, 1))).toInt32()), - std.math.maxInt(i32), - ), - ); - var bytes = bun.default_allocator.alloc(u8, length) catch { - JSC.JSError(bun.default_allocator, "OOM! Out of memory", .{}, ctx, exception); - return null; - }; - - return JSC.MarkedArrayBuffer.fromBytes( - bytes, - bun.default_allocator, - .Uint8Array, - ).toJSObjectRef(ctx, null); -} - -pub fn mmapFile( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); - - var buf: [bun.MAX_PATH_BYTES]u8 = undefined; - const path = getFilePath(ctx, arguments[0..@minimum(1, arguments.len)], &buf, exception) orelse return null; - args.eat(); - - buf[path.len] = 0; - - const buf_z: [:0]const u8 = buf[0..path.len :0]; - - const sync_flags: u32 = if (@hasDecl(std.os.MAP, "SYNC")) std.os.MAP.SYNC | std.os.MAP.SHARED_VALIDATE else 0; - const file_flags: u32 = if (@hasDecl(std.os.MAP, "FILE")) std.os.MAP.FILE else 0; - - // Conforming applications must specify either MAP_PRIVATE or MAP_SHARED. - var offset: usize = 0; - var flags = file_flags; - var map_size: ?usize = null; - - if (args.nextEat()) |opts| { - const sync = opts.get(ctx.ptr(), "sync") orelse JSC.JSValue.jsBoolean(false); - const shared = opts.get(ctx.ptr(), "shared") orelse JSC.JSValue.jsBoolean(true); - flags |= @as(u32, if (sync.toBoolean()) sync_flags else 0); - flags |= @as(u32, if (shared.toBoolean()) std.os.MAP.SHARED else std.os.MAP.PRIVATE); - - if (opts.get(ctx.ptr(), "size")) |value| { - map_size = @intCast(usize, value.toInt64()); - } - - if (opts.get(ctx.ptr(), "offset")) |value| { - offset = @intCast(usize, value.toInt64()); - offset = std.mem.alignBackwardAnyAlign(offset, std.mem.page_size); - } - } else { - flags |= std.os.MAP.SHARED; - } - - const map = switch (JSC.Node.Syscall.mmapFile(buf_z, flags, map_size, offset)) { - .result => |map| map, - - .err => |err| { - exception.* = err.toJS(ctx); - return null; - }, - }; - - return JSC.C.JSObjectMakeTypedArrayWithBytesNoCopy(ctx, JSC.C.JSTypedArrayType.kJSTypedArrayTypeUint8Array, @ptrCast(?*anyopaque, map.ptr), map.len, struct { - pub fn x(ptr: ?*anyopaque, size: ?*anyopaque) callconv(.C) void { - _ = JSC.Node.Syscall.munmap(@ptrCast([*]align(std.mem.page_size) u8, @alignCast(std.mem.page_size, ptr))[0..@ptrToInt(size)]); - } - }.x, @intToPtr(?*anyopaque, map.len), exception); -} - -pub fn getTranspilerConstructor( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("BunTranspiler")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - &ZigString.init("BunTranspiler"), - JSC.JSValue.fromRef(Transpiler.Constructor.constructor(ctx)), - ).asObjectRef(); - } - - return existing.asObjectRef(); -} - -pub fn getHashObject( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("BunHash")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - &ZigString.init("BunHash"), - JSC.JSValue.fromRef(JSC.C.JSObjectMake(ctx, Hash.Class.get().*, null)), - ).asObjectRef(); - } - - return existing.asObjectRef(); -} - -pub const Hash = struct { - pub const Class = NewClass( - void, - .{ - .name = "Hash", - }, - .{ - .call = .{ - .rfn = call, - }, - .wyhash = .{ - .rfn = hashWrap(std.hash.Wyhash).hash, - }, - .adler32 = .{ - .rfn = hashWrap(std.hash.Adler32).hash, - }, - .crc32 = .{ - .rfn = hashWrap(std.hash.Crc32).hash, - }, - .cityHash32 = .{ - .rfn = hashWrap(std.hash.CityHash32).hash, - }, - .cityHash64 = .{ - .rfn = hashWrap(std.hash.CityHash64).hash, - }, - .murmur32v2 = .{ - .rfn = hashWrap(std.hash.murmur.Murmur2_32).hash, - }, - .murmur32v3 = .{ - .rfn = hashWrap(std.hash.murmur.Murmur3_32).hash, - }, - .murmur64v2 = .{ - .rfn = hashWrap(std.hash.murmur.Murmur2_64).hash, - }, - }, - .{}, - ); - - pub fn call( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSObjectRef { - return hashWrap(std.hash.Wyhash).hash(void{}, ctx, null, null, arguments, exception); - } - fn hashWrap(comptime Hasher: anytype) type { - return struct { - pub fn hash( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - var args = JSC.Node.ArgumentsSlice.from(ctx.bunVM(), arguments); - var input: []const u8 = ""; - var input_slice = ZigString.Slice.empty; - defer input_slice.deinit(); - if (args.nextEat()) |arg| { - if (arg.as(JSC.WebCore.Blob)) |blob| { - // TODO: files - input = blob.sharedView(); - } else { - switch (arg.jsTypeLoose()) { - .ArrayBuffer, .Int8Array, .Uint8Array, .Uint8ClampedArray, .Int16Array, .Uint16Array, .Int32Array, .Uint32Array, .Float32Array, .Float64Array, .BigInt64Array, .BigUint64Array, .DataView => { - var array_buffer = arg.asArrayBuffer(ctx.ptr()) orelse { - JSC.throwInvalidArguments("ArrayBuffer conversion error", .{}, ctx, exception); - return null; - }; - input = array_buffer.slice(); - }, - else => { - input_slice = arg.toSlice(ctx.ptr(), bun.default_allocator); - input = input_slice.slice(); - }, - } - } - } - - // std.hash has inconsistent interfaces - // - const Function = if (@hasDecl(Hasher, "hashWithSeed")) Hasher.hashWithSeed else Hasher.hash; - var function_args: std.meta.ArgsTuple(@TypeOf(Function)) = undefined; - if (comptime std.meta.fields(std.meta.ArgsTuple(@TypeOf(Function))).len == 1) { - return JSC.JSValue.jsNumber(Function(input)).asObjectRef(); - } else { - var seed: u64 = 0; - if (args.nextEat()) |arg| { - if (arg.isNumber()) { - seed = arg.toU32(); - } - } - if (comptime std.meta.trait.isNumber(@TypeOf(function_args[0]))) { - function_args[0] = @intCast(@TypeOf(function_args[0]), seed); - function_args[1] = input; - } else { - function_args[1] = @intCast(@TypeOf(function_args[1]), seed); - function_args[0] = input; - } - - const value = @call(.{}, Function, function_args); - - if (@TypeOf(value) == u32) { - return JSC.JSValue.jsNumber(@bitCast(i32, value)).asObjectRef(); - } - return JSC.JSValue.jsNumber(value).asObjectRef(); - } - } - }; - } -}; - -pub fn getTOMLObject( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("TOML")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - &ZigString.init("TOML"), - JSValue.fromRef(js.JSObjectMake(ctx, TOML.Class.get().?[0], null)), - ).asObjectRef(); - } - - return existing.asObjectRef(); -} - -pub fn getUnsafe( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("Unsafe")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - &ZigString.init("Unsafe"), - JSValue.fromRef(js.JSObjectMake(ctx, Unsafe.Class.get().?[0], null)), - ).asObjectRef(); - } - - return existing.asObjectRef(); -} - -pub const Unsafe = struct { - pub const Class = NewClass( - void, - .{ .name = "Unsafe", .read_only = true }, - .{ - .segfault = .{ - .rfn = __debug__doSegfault, - }, - .arrayBufferToString = .{ - .rfn = arrayBufferToString, - }, - }, - .{}, - ); - - // For testing the segfault handler - pub fn __debug__doSegfault( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - _ = ctx; - const Reporter = @import("../../../report.zig"); - Reporter.globalError(error.SegfaultTest); - } - - pub fn arrayBufferToString( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - args: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - const array_buffer = JSC.ArrayBuffer.fromTypedArray(ctx, JSC.JSValue.fromRef(args[0]), exception); - switch (array_buffer.typed_array_type) { - .Uint16Array, .Int16Array => { - var zig_str = ZigString.init(""); - zig_str.ptr = @ptrCast([*]const u8, @alignCast(@alignOf([*]align(1) const u16), array_buffer.ptr)); - zig_str.len = array_buffer.len; - zig_str.markUTF16(); - // the deinitializer for string causes segfaults - // if we don't clone it - return ZigString.toValueGC(&zig_str, ctx.ptr()).asObjectRef(); - }, - else => { - // the deinitializer for string causes segfaults - // if we don't clone it - return ZigString.init(array_buffer.slice()).toValueGC(ctx.ptr()).asObjectRef(); - }, - } - } -}; - -// pub const Lockfile = struct { -// const BunLockfile = @import("../../../install/install.zig").Lockfile; -// lockfile: *BunLockfile, - -// pub const RefCountedLockfile = bun.RefCount(Lockfile, true); - -// pub const StaticClass = NewClass( -// void, -// .{ -// .name = "Lockfile", -// .read_only = true, -// }, -// .{ -// .load = .{ -// .rfn = BunLockfile.load, -// }, -// }, -// .{}, -// ); - -// pub const Class = NewClass( -// RefCountedLockfile, -// .{ -// .name = "Lockfile", -// .read_only = true, -// }, -// .{ -// .findPackagesByName = .{ -// .rfn = BunLockfile.load, -// }, -// .dependencies = .{ -// .rfn = BunLockfile.load, -// }, -// }, -// .{}, -// ); - -// pub fn deinit(this: *Lockfile) void { -// this.lockfile.deinit(); -// } - -// pub fn load( -// // this -// _: void, -// ctx: js.JSContextRef, -// // function -// _: js.JSObjectRef, -// // thisObject -// _: js.JSObjectRef, -// arguments: []const js.JSValueRef, -// exception: js.ExceptionRef, -// ) js.JSValueRef { -// if (arguments.len == 0) { -// JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); -// return null; -// } - -// var lockfile: *BunLockfile = getAllocator(ctx).create(BunLockfile) catch return JSValue.jsUndefined().asRef(); - -// var log = logger.Log.init(default_allocator); -// var args_slice = @ptrCast([*]const JSValue, arguments.ptr)[0..arguments.len]; - -// var arguments_slice = Node.ArgumentsSlice.init(args_slice); -// var path_or_buffer = Node.PathLike.fromJS(ctx, &arguments_slice, exception) orelse { -// getAllocator(ctx).destroy(lockfile); -// JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); -// return null; -// }; - -// const load_from_disk_result = switch (path_or_buffer) { -// Node.PathLike.Tag.string => lockfile.loadFromDisk(getAllocator(ctx), &log, path_or_buffer.string), -// Node.PathLike.Tag.buffer => lockfile.loadFromBytes(getAllocator(ctx), path_or_buffer.buffer.slice(), &log), -// else => { -// getAllocator(ctx).destroy(lockfile); -// JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); -// return null; -// }, -// }; - -// switch (load_from_disk_result) { -// .err => |cause| { -// defer getAllocator(ctx).destroy(lockfile); -// switch (cause.step) { -// .open_file => { -// JSError(undefined, "error opening lockfile: {s}", .{ -// @errorName(cause.value), -// }, ctx, exception); -// return null; -// }, -// .parse_file => { -// JSError(undefined, "error parsing lockfile: {s}", .{ -// @errorName(cause.value), -// }, ctx, exception); -// return null; -// }, -// .read_file => { -// JSError(undefined, "error reading lockfile: {s}", .{ -// @errorName(cause.value), -// }, ctx, exception); -// return null; -// }, -// } -// }, -// .ok => {}, -// } -// } -// }; - -pub const TOML = struct { - const TOMLParser = @import("../../../toml/toml_parser.zig").TOML; - pub const Class = NewClass( - void, - .{ - .name = "TOML", - .read_only = true, - }, - .{ - .parse = .{ - .rfn = TOML.parse, - }, - }, - .{}, - ); - - pub fn parse( - // this - _: void, - ctx: js.JSContextRef, - // function - _: js.JSObjectRef, - // thisObject - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - var arena = std.heap.ArenaAllocator.init(getAllocator(ctx)); - var allocator = arena.allocator(); - defer arena.deinit(); - var log = logger.Log.init(default_allocator); - var input_str = ZigString.init(""); - JSValue.fromRef(arguments[0]).toZigString(&input_str, ctx.ptr()); - var needs_deinit = false; - var input = input_str.slice(); - if (input_str.is16Bit()) { - input = std.fmt.allocPrint(allocator, "{}", .{input_str}) catch unreachable; - needs_deinit = true; - } - var source = logger.Source.initPathString("input.toml", input); - var parse_result = TOMLParser.parse(&source, &log, allocator) catch { - exception.* = log.toJS(ctx.ptr(), default_allocator, "Failed to parse toml").asObjectRef(); - return null; - }; - - // for now... - var buffer_writer = try js_printer.BufferWriter.init(allocator); - var writer = js_printer.BufferPrinter.init(buffer_writer); - _ = js_printer.printJSON(*js_printer.BufferPrinter, &writer, parse_result, &source) catch { - exception.* = log.toJS(ctx.ptr(), default_allocator, "Failed to print toml").asObjectRef(); - return null; - }; - - var slice = writer.ctx.buffer.toOwnedSliceLeaky(); - var out = ZigString.init(slice); - - const out_value = js.JSValueMakeFromJSONString(ctx, out.toJSStringRef()); - return out_value; - } -}; - -pub const Timer = struct { - last_id: i32 = 0, - warned: bool = false, - active: u32 = 0, - timeouts: TimeoutMap = TimeoutMap{}, - - const TimeoutMap = std.AutoArrayHashMapUnmanaged(i32, *Timeout); - - pub fn getNextID() callconv(.C) i32 { - VirtualMachine.vm.timer.last_id += 1; - return VirtualMachine.vm.timer.last_id; - } - - pub const Timeout = struct { - id: i32 = 0, - callback: JSValue, - interval: i32 = 0, - completion: NetworkThread.Completion = undefined, - repeat: bool = false, - io_task: ?*TimeoutTask = null, - cancelled: bool = false, - - pub const TimeoutTask = IOTask(Timeout); - - pub fn run(this: *Timeout, _task: *TimeoutTask) void { - this.io_task = _task; - NetworkThread.global.pool.io.?.timeout( - *Timeout, - this, - onCallback, - &this.completion, - std.time.ns_per_ms * @intCast( - u63, - @maximum( - this.interval, - 1, - ), - ), - ); - } - - pub fn onCallback(this: *Timeout, _: *NetworkThread.Completion, _: NetworkThread.AsyncIO.TimeoutError!void) void { - this.io_task.?.onFinish(); - } - - pub fn then(this: *Timeout, global: *JSGlobalObject) void { - if (comptime JSC.is_bindgen) - unreachable; - - if (!this.cancelled) { - if (this.repeat) { - this.io_task.?.deinit(); - var task = Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, global, this) catch unreachable; - this.io_task = task; - task.schedule(); - } - - _ = JSC.C.JSObjectCallAsFunction(global.ref(), this.callback.asObjectRef(), null, 0, null, null); - - if (this.repeat) - return; - } - - this.clear(global); - } - - pub fn clear(this: *Timeout, global: *JSGlobalObject) void { - if (comptime JSC.is_bindgen) - unreachable; - - this.cancelled = true; - JSC.C.JSValueUnprotect(global.ref(), this.callback.asObjectRef()); - _ = VirtualMachine.vm.timer.timeouts.swapRemove(this.id); - if (this.io_task) |task| { - task.deinit(); - } - VirtualMachine.vm.allocator.destroy(this); - VirtualMachine.vm.timer.active -|= 1; - VirtualMachine.vm.active_tasks -|= 1; - } - }; - - fn set( - id: i32, - globalThis: *JSGlobalObject, - callback: JSValue, - countdown: JSValue, - repeat: bool, - ) !void { - if (comptime is_bindgen) unreachable; - var timeout = try VirtualMachine.vm.allocator.create(Timeout); - js.JSValueProtect(globalThis.ref(), callback.asObjectRef()); - timeout.* = Timeout{ .id = id, .callback = callback, .interval = countdown.toInt32(), .repeat = repeat }; - var task = try Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, globalThis, timeout); - VirtualMachine.vm.timer.timeouts.put(VirtualMachine.vm.allocator, id, timeout) catch unreachable; - VirtualMachine.vm.timer.active +|= 1; - VirtualMachine.vm.active_tasks +|= 1; - task.schedule(); - } - - pub fn setTimeout( - globalThis: *JSGlobalObject, - callback: JSValue, - countdown: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - const id = VirtualMachine.vm.timer.last_id; - VirtualMachine.vm.timer.last_id +%= 1; - - Timer.set(id, globalThis, callback, countdown, false) catch - return JSValue.jsUndefined(); - - return JSValue.jsNumberWithType(i32, id); - } - pub fn setInterval( - globalThis: *JSGlobalObject, - callback: JSValue, - countdown: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - const id = VirtualMachine.vm.timer.last_id; - VirtualMachine.vm.timer.last_id +%= 1; - - Timer.set(id, globalThis, callback, countdown, true) catch - return JSValue.jsUndefined(); - - return JSValue.jsNumberWithType(i32, id); - } - - pub fn clearTimer(id: JSValue, _: *JSGlobalObject) void { - if (comptime is_bindgen) unreachable; - var timer: *Timeout = VirtualMachine.vm.timer.timeouts.get(id.toInt32()) orelse return; - timer.cancelled = true; - } - - pub fn clearTimeout( - globalThis: *JSGlobalObject, - id: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - Timer.clearTimer(id, globalThis); - return JSValue.jsUndefined(); - } - pub fn clearInterval( - globalThis: *JSGlobalObject, - id: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - Timer.clearTimer(id, globalThis); - return JSValue.jsUndefined(); - } - - const Shimmer = @import("../bindings/shimmer.zig").Shimmer; - - pub const shim = Shimmer("Bun", "Timer", @This()); - pub const name = "Bun__Timer"; - pub const include = ""; - pub const namespace = shim.namespace; - - pub const Export = shim.exportFunctions(.{ - .@"setTimeout" = setTimeout, - .@"setInterval" = setInterval, - .@"clearTimeout" = clearTimeout, - .@"clearInterval" = clearInterval, - .@"getNextID" = getNextID, - }); - - comptime { - if (!JSC.is_bindgen) { - @export(setTimeout, .{ .name = Export[0].symbol_name }); - @export(setInterval, .{ .name = Export[1].symbol_name }); - @export(clearTimeout, .{ .name = Export[2].symbol_name }); - @export(clearInterval, .{ .name = Export[3].symbol_name }); - @export(getNextID, .{ .name = Export[4].symbol_name }); - } - } -}; - -pub const FFI = struct { - pub const Class = NewClass( - void, - .{ - .name = "FFI", - }, - .{ - .viewSource = .{ - .rfn = JSC.wrapWithHasContainer(JSC.FFI, "print", false, false, true), - }, - .dlopen = .{ - .rfn = JSC.wrapWithHasContainer(JSC.FFI, "open", false, false, true), - }, - .callback = .{ - .rfn = JSC.wrapWithHasContainer(JSC.FFI, "callback", false, false, false), - }, - .linkSymbols = .{ - .rfn = JSC.wrapWithHasContainer(JSC.FFI, "linkSymbols", false, false, false), - }, - .ptr = .{ - .rfn = JSC.wrapWithHasContainer(@This(), "ptr", false, false, true), - }, - .toBuffer = .{ - .rfn = JSC.wrapWithHasContainer(@This(), "toBuffer", false, false, true), - }, - .toArrayBuffer = .{ - .rfn = JSC.wrapWithHasContainer(@This(), "toArrayBuffer", false, false, true), - }, - }, - .{ - .CString = .{ - .get = UnsafeCString.getter, - }, - }, - ); - - pub fn ptr( - globalThis: *JSGlobalObject, - value: JSValue, - byteOffset: ?JSValue, - ) JSValue { - if (value.isEmpty()) { - return JSC.JSValue.jsNull(); - } - - const array_buffer = value.asArrayBuffer(globalThis) orelse { - return JSC.toInvalidArguments("Expected ArrayBufferView", .{}, globalThis.ref()); - }; - - if (array_buffer.len == 0) { - return JSC.toInvalidArguments("ArrayBufferView must have a length > 0. A pointer to empty memory doesn't work", .{}, globalThis.ref()); - } - - var addr: usize = @ptrToInt(array_buffer.ptr); - - if (byteOffset) |off| { - if (!off.isEmptyOrUndefinedOrNull()) { - if (!off.isNumber()) { - return JSC.toInvalidArguments("Expected number for byteOffset", .{}, globalThis.ref()); - } - } - - const bytei64 = off.toInt64(); - if (bytei64 < 0) { - addr -|= @intCast(usize, bytei64 * -1); - } else { - addr += @intCast(usize, bytei64); - } - - if (addr > @ptrToInt(array_buffer.ptr) + @as(usize, array_buffer.byte_len)) { - return JSC.toInvalidArguments("byteOffset out of bounds", .{}, globalThis.ref()); - } - } - - if (addr > max_addressible_memory) { - return JSC.toInvalidArguments("Pointer is outside max addressible memory, which usually means a bug in your program.", .{}, globalThis.ref()); - } - - if (addr == 0) { - return JSC.toInvalidArguments("Pointer must not be 0", .{}, globalThis.ref()); - } - - if (addr == 0xDEADBEEF or addr == 0xaaaaaaaa or addr == 0xAAAAAAAA) { - return JSC.toInvalidArguments("ptr to invalid memory, that would segfault Bun :(", .{}, globalThis.ref()); - } - - return JSC.JSValue.fromPtrAddress(addr); - } - - const ValueOrError = union(enum) { - err: JSValue, - slice: []u8, - }; - - pub fn getPtrSlice(globalThis: *JSGlobalObject, value: JSValue, byteOffset: ?JSValue, byteLength: ?JSValue) ValueOrError { - if (!value.isNumber()) { - return .{ .err = JSC.toInvalidArguments("ptr must be a number.", .{}, globalThis.ref()) }; - } - - const num = value.asNumber(); - if (num == 0) { - return .{ .err = JSC.toInvalidArguments("ptr cannot be zero, that would segfault Bun :(", .{}, globalThis.ref()) }; - } - - if (!std.math.isFinite(num)) { - return .{ .err = JSC.toInvalidArguments("ptr must be a finite number.", .{}, globalThis.ref()) }; - } - - var addr = @bitCast(usize, num); - - if (byteOffset) |byte_off| { - if (byte_off.isNumber()) { - const off = byte_off.toInt64(); - if (off < 0) { - addr -|= @intCast(usize, off * -1); - } else { - addr +|= @intCast(usize, off); - } - - if (addr == 0) { - return .{ .err = JSC.toInvalidArguments("ptr cannot be zero, that would segfault Bun :(", .{}, globalThis.ref()) }; - } - - if (!std.math.isFinite(byte_off.asNumber())) { - return .{ .err = JSC.toInvalidArguments("ptr must be a finite number.", .{}, globalThis.ref()) }; - } - } else if (!byte_off.isEmptyOrUndefinedOrNull()) { - // do nothing - } else { - return .{ .err = JSC.toInvalidArguments("Expected number for byteOffset", .{}, globalThis.ref()) }; - } - } - - if (addr == 0xDEADBEEF or addr == 0xaaaaaaaa or addr == 0xAAAAAAAA) { - return .{ .err = JSC.toInvalidArguments("ptr to invalid memory, that would segfault Bun :(", .{}, globalThis.ref()) }; - } - - if (byteLength) |valueLength| { - if (!valueLength.isEmptyOrUndefinedOrNull()) { - if (!valueLength.isNumber()) { - return .{ .err = JSC.toInvalidArguments("length must be a number.", .{}, globalThis.ref()) }; - } - - if (valueLength.asNumber() == 0.0) { - return .{ .err = JSC.toInvalidArguments("length must be > 0. This usually means a bug in your code.", .{}, globalThis.ref()) }; - } - - const length_i = valueLength.toInt64(); - if (length_i < 0) { - return .{ .err = JSC.toInvalidArguments("length must be > 0. This usually means a bug in your code.", .{}, globalThis.ref()) }; - } - - if (length_i > max_addressible_memory) { - return .{ .err = JSC.toInvalidArguments("length exceeds max addressable memory. This usually means a bug in your code.", .{}, globalThis.ref()) }; - } - - const length = @intCast(usize, length_i); - return .{ .slice = @intToPtr([*]u8, addr)[0..length] }; - } - } - - return .{ .slice = bun.span(@intToPtr([*:0]u8, addr)) }; - } - - pub fn toArrayBuffer( - globalThis: *JSGlobalObject, - value: JSValue, - byteOffset: ?JSValue, - valueLength: ?JSValue, - ) JSC.JSValue { - switch (getPtrSlice(globalThis, value, byteOffset, valueLength)) { - .err => |erro| { - return erro; - }, - .slice => |slice| { - return JSC.ArrayBuffer.fromBytes(slice, JSC.JSValue.JSType.ArrayBuffer).toJSWithContext(globalThis.ref(), null, null, null); - }, - } - } - - pub fn toBuffer( - globalThis: *JSGlobalObject, - value: JSValue, - byteOffset: ?JSValue, - valueLength: ?JSValue, - ) JSC.JSValue { - switch (getPtrSlice(globalThis, value, byteOffset, valueLength)) { - .err => |erro| { - return erro; - }, - .slice => |slice| { - return JSC.JSValue.createBuffer(globalThis, slice, null); - }, - } - } - - pub fn toCStringBuffer( - globalThis: *JSGlobalObject, - value: JSValue, - byteOffset: ?JSValue, - valueLength: ?JSValue, - ) JSC.JSValue { - switch (getPtrSlice(globalThis, value, byteOffset, valueLength)) { - .err => |erro| { - return erro; - }, - .slice => |slice| { - return JSC.JSValue.createBuffer(globalThis, slice, null); - }, - } - } - - pub fn getter( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("FFI")); - if (existing.isEmpty()) { - var prototype = JSC.C.JSObjectMake(ctx, FFI.Class.get().?[0], null); - var base = JSC.C.JSObjectMake(ctx, null, null); - JSC.C.JSObjectSetPrototype(ctx, base, prototype); - return ctx.ptr().putCachedObject( - &ZigString.init("FFI"), - JSValue.fromRef(base), - ).asObjectRef(); - } - - return existing.asObjectRef(); - } -}; - -pub const UnsafeCString = struct { - pub fn constructor( - ctx: js.JSContextRef, - _: js.JSObjectRef, - len: usize, - args: [*c]const js.JSValueRef, - exception: js.ExceptionRef, - ) callconv(.C) js.JSObjectRef { - if (len == 0) { - JSC.throwInvalidArguments("Expected a ptr", .{}, ctx, exception); - return null; - } - - return newCString(ctx.ptr(), JSC.JSValue.fromRef(args[0]), if (len > 1) JSC.JSValue.fromRef(args[1]) else null, if (len > 2) JSC.JSValue.fromRef(args[2]) else null).asObjectRef(); - } - - pub fn newCString(globalThis: *JSGlobalObject, value: JSValue, byteOffset: ?JSValue, lengthValue: ?JSValue) JSC.JSValue { - switch (FFI.getPtrSlice(globalThis, value, byteOffset, lengthValue)) { - .err => |err| { - return err; - }, - .slice => |slice| { - return WebCore.Encoder.toString(slice.ptr, slice.len, globalThis, .utf8); - }, - } - } - - pub fn getter( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("UnsafeCString")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - &ZigString.init("UnsafeCString"), - JSValue.fromRef(JSC.C.JSObjectMakeConstructor(ctx, null, constructor)), - ).asObjectRef(); - } - - return existing.asObjectRef(); - } -}; - -/// EnvironmentVariables is runtime defined. -/// Also, you can't iterate over process.env normally since it only exists at build-time otherwise -// This is aliased to Bun.env -pub const EnvironmentVariables = struct { - pub const Class = NewClass( - void, - .{ - .name = "DotEnv", - .read_only = true, - }, - .{ - .getProperty = .{ - .rfn = getProperty, - }, - .setProperty = .{ - .rfn = setProperty, - }, - .deleteProperty = .{ - .rfn = deleteProperty, - }, - .convertToType = .{ .rfn = convertToType }, - .hasProperty = .{ - .rfn = hasProperty, - }, - .getPropertyNames = .{ - .rfn = getPropertyNames, - }, - .toJSON = .{ - .rfn = toJSON, - .name = "toJSON", - }, - }, - .{}, - ); - - pub fn getter( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(&ZigString.init("Bun.env")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - &ZigString.init("Bun.env"), - JSValue.fromRef(js.JSObjectMake(ctx, EnvironmentVariables.Class.get().*, null)), - ).asObjectRef(); - } - - return existing.asObjectRef(); - } - - pub const BooleanString = struct { - pub const @"true": string = "true"; - pub const @"false": string = "false"; - }; - - pub fn getProperty( - ctx: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - _: js.ExceptionRef, - ) callconv(.C) js.JSValueRef { - const len = js.JSStringGetLength(propertyName); - var ptr = js.JSStringGetCharacters8Ptr(propertyName); - var name = ptr[0..len]; - if (VirtualMachine.vm.bundler.env.map.get(name)) |value| { - return ZigString.toRef(value, ctx.ptr()); - } - - if (Output.enable_ansi_colors) { - // https://github.com/chalk/supports-color/blob/main/index.js - if (strings.eqlComptime(name, "FORCE_COLOR")) { - return ZigString.toRef(BooleanString.@"true", ctx.ptr()); - } - } - - return js.JSValueMakeUndefined(ctx); - } - - pub fn toJSON( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var map = VirtualMachine.vm.bundler.env.map.map; - var keys = map.keys(); - var values = map.values(); - const StackFallback = std.heap.StackFallbackAllocator(32 * 2 * @sizeOf(ZigString)); - var stack = StackFallback{ - .buffer = undefined, - .fallback_allocator = bun.default_allocator, - .fixed_buffer_allocator = undefined, - }; - var allocator = stack.get(); - var key_strings_ = allocator.alloc(ZigString, keys.len * 2) catch unreachable; - var key_strings = key_strings_[0..keys.len]; - var value_strings = key_strings_[keys.len..]; - - for (keys) |key, i| { - key_strings[i] = ZigString.init(key); - key_strings[i].detectEncoding(); - value_strings[i] = ZigString.init(values[i]); - value_strings[i].detectEncoding(); - } - - var result = JSValue.fromEntries(ctx.ptr(), key_strings.ptr, value_strings.ptr, keys.len, false).asObjectRef(); - allocator.free(key_strings_); - return result; - // } - // ZigConsoleClient.Formatter.format(this: *Formatter, result: Tag.Result, comptime Writer: type, writer: Writer, value: JSValue, globalThis: *JSGlobalObject, comptime enable_ansi_colors: bool) - } - - pub fn deleteProperty( - _: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - _: js.ExceptionRef, - ) callconv(.C) bool { - const len = js.JSStringGetLength(propertyName); - var ptr = js.JSStringGetCharacters8Ptr(propertyName); - var name = ptr[0..len]; - _ = VirtualMachine.vm.bundler.env.map.map.swapRemove(name); - return true; - } - - pub fn setProperty( - ctx: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - value: js.JSValueRef, - exception: js.ExceptionRef, - ) callconv(.C) bool { - const len = js.JSStringGetLength(propertyName); - var ptr = js.JSStringGetCharacters8Ptr(propertyName); - var name = ptr[0..len]; - var val = ZigString.init(""); - JSValue.fromRef(value).toZigString(&val, ctx.ptr()); - if (exception.* != null) return false; - var result = std.fmt.allocPrint(VirtualMachine.vm.allocator, "{}", .{val}) catch unreachable; - VirtualMachine.vm.bundler.env.map.put(name, result) catch unreachable; - - return true; - } - - pub fn hasProperty( - _: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - ) callconv(.C) bool { - const len = js.JSStringGetLength(propertyName); - const ptr = js.JSStringGetCharacters8Ptr(propertyName); - const name = ptr[0..len]; - return VirtualMachine.vm.bundler.env.map.get(name) != null or (Output.enable_ansi_colors and strings.eqlComptime(name, "FORCE_COLOR")); - } - - pub fn convertToType(ctx: js.JSContextRef, obj: js.JSObjectRef, kind: js.JSType, exception: js.ExceptionRef) callconv(.C) js.JSValueRef { - _ = ctx; - _ = obj; - _ = kind; - _ = exception; - return obj; - } - - pub fn getPropertyNames( - _: js.JSContextRef, - _: js.JSObjectRef, - props: js.JSPropertyNameAccumulatorRef, - ) callconv(.C) void { - var iter = VirtualMachine.vm.bundler.env.map.iter(); - - while (iter.next()) |item| { - const str = item.key_ptr.*; - js.JSPropertyNameAccumulatorAddName(props, js.JSStringCreateStatic(str.ptr, str.len)); - } - } -}; - -export fn Bun__reportError(_: *JSGlobalObject, err: JSC.JSValue) void { - JSC.VirtualMachine.vm.defaultErrorHandler(err, null); -} - -comptime { - if (!is_bindgen) { - _ = Bun__reportError; - } -} - -pub const JSZlib = struct { - export fn reader_deallocator(_: ?*anyopaque, ctx: ?*anyopaque) void { - var reader: *zlib.ZlibReaderArrayList = bun.cast(*zlib.ZlibReaderArrayList, ctx.?); - reader.list.deinit(reader.allocator); - reader.deinit(); - } - - export fn compressor_deallocator(_: ?*anyopaque, ctx: ?*anyopaque) void { - var compressor: *zlib.ZlibCompressorArrayList = bun.cast(*zlib.ZlibCompressorArrayList, ctx.?); - compressor.list.deinit(compressor.allocator); - compressor.deinit(); - } - - pub fn gzipSync( - globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - options_val_: ?JSValue, - ) JSValue { - return gzipOrDeflateSync(globalThis, buffer, options_val_, true); - } - - pub fn deflateSync( - globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - options_val_: ?JSValue, - ) JSValue { - return gzipOrDeflateSync(globalThis, buffer, options_val_, false); - } - - pub fn gzipOrDeflateSync( - globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - options_val_: ?JSValue, - is_gzip: bool, - ) JSValue { - var opts = zlib.Options{ .gzip = is_gzip }; - if (options_val_) |options_val| { - if (options_val.isObject()) { - if (options_val.get(globalThis, "windowBits")) |window| { - opts.windowBits = window.toInt32(); - } - - if (options_val.get(globalThis, "level")) |level| { - opts.level = level.toInt32(); - } - - if (options_val.get(globalThis, "memLevel")) |memLevel| { - opts.memLevel = memLevel.toInt32(); - } - - if (options_val.get(globalThis, "strategy")) |strategy| { - opts.strategy = strategy.toInt32(); - } - } - } - - var compressed = buffer.slice(); - const allocator = JSC.VirtualMachine.vm.allocator; - var list = std.ArrayListUnmanaged(u8).initCapacity(allocator, if (compressed.len > 512) compressed.len else 32) catch unreachable; - var reader = zlib.ZlibCompressorArrayList.init(compressed, &list, allocator, opts) catch |err| { - if (err == error.InvalidArgument) { - return JSC.toInvalidArguments("Invalid buffer", .{}, globalThis.ref()); - } - - return JSC.toInvalidArguments("Unexpected", .{}, globalThis.ref()); - }; - - reader.readAll() catch { - defer reader.deinit(); - if (reader.errorMessage()) |msg| { - return ZigString.init(msg).toErrorInstance(globalThis); - } - return ZigString.init("Zlib returned an error").toErrorInstance(globalThis); - }; - reader.list = .{ .items = reader.list.toOwnedSlice(allocator) }; - reader.list.capacity = reader.list.items.len; - reader.list_ptr = &reader.list; - - var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); - return array_buffer.toJSWithContext(globalThis.ref(), reader, reader_deallocator, null); - } - - pub fn inflateSync( - globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - ) JSValue { - var compressed = buffer.slice(); - const allocator = JSC.VirtualMachine.vm.allocator; - var list = std.ArrayListUnmanaged(u8).initCapacity(allocator, if (compressed.len > 512) compressed.len else 32) catch unreachable; - var reader = zlib.ZlibReaderArrayList.initWithOptions(compressed, &list, allocator, .{ - .windowBits = -15, - }) catch |err| { - if (err == error.InvalidArgument) { - return JSC.toInvalidArguments("Invalid buffer", .{}, globalThis.ref()); - } - - return JSC.toInvalidArguments("Unexpected", .{}, globalThis.ref()); - }; - - reader.readAll() catch { - defer reader.deinit(); - if (reader.errorMessage()) |msg| { - return ZigString.init(msg).toErrorInstance(globalThis); - } - return ZigString.init("Zlib returned an error").toErrorInstance(globalThis); - }; - reader.list = .{ .items = reader.list.toOwnedSlice(allocator) }; - reader.list.capacity = reader.list.items.len; - reader.list_ptr = &reader.list; - - var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); - return array_buffer.toJSWithContext(globalThis.ref(), reader, reader_deallocator, null); - } - - pub fn gunzipSync( - globalThis: *JSGlobalObject, - buffer: JSC.Node.StringOrBuffer, - ) JSValue { - var compressed = buffer.slice(); - const allocator = JSC.VirtualMachine.vm.allocator; - var list = std.ArrayListUnmanaged(u8).initCapacity(allocator, if (compressed.len > 512) compressed.len else 32) catch unreachable; - var reader = zlib.ZlibReaderArrayList.init(compressed, &list, allocator) catch |err| { - if (err == error.InvalidArgument) { - return JSC.toInvalidArguments("Invalid buffer", .{}, globalThis.ref()); - } - - return JSC.toInvalidArguments("Unexpected", .{}, globalThis.ref()); - }; - - reader.readAll() catch { - defer reader.deinit(); - if (reader.errorMessage()) |msg| { - return ZigString.init(msg).toErrorInstance(globalThis); - } - return ZigString.init("Zlib returned an error").toErrorInstance(globalThis); - }; - reader.list = .{ .items = reader.list.toOwnedSlice(allocator) }; - reader.list.capacity = reader.list.items.len; - reader.list_ptr = &reader.list; - - var array_buffer = JSC.ArrayBuffer.fromBytes(reader.list.items, .Uint8Array); - return array_buffer.toJSWithContext(globalThis.ref(), reader, reader_deallocator, null); - } -}; diff --git a/src/javascript/jsc/api/ffi.zig b/src/javascript/jsc/api/ffi.zig deleted file mode 100644 index 864a5d889..000000000 --- a/src/javascript/jsc/api/ffi.zig +++ /dev/null @@ -1,1436 +0,0 @@ -const Bun = @This(); -const default_allocator = @import("../../../global.zig").default_allocator; -const bun = @import("../../../global.zig"); -const Environment = bun.Environment; -const NetworkThread = @import("http").NetworkThread; -const Global = bun.Global; -const strings = bun.strings; -const string = bun.string; -const Output = @import("../../../global.zig").Output; -const MutableString = @import("../../../global.zig").MutableString; -const std = @import("std"); -const Allocator = std.mem.Allocator; -const IdentityContext = @import("../../../identity_context.zig").IdentityContext; -const Fs = @import("../../../fs.zig"); -const Resolver = @import("../../../resolver/resolver.zig"); -const ast = @import("../../../import_record.zig"); -const NodeModuleBundle = @import("../../../node_module_bundle.zig").NodeModuleBundle; -const MacroEntryPoint = @import("../../../bundler.zig").MacroEntryPoint; -const logger = @import("../../../logger.zig"); -const Api = @import("../../../api/schema.zig").Api; -const options = @import("../../../options.zig"); -const Bundler = @import("../../../bundler.zig").Bundler; -const ServerEntryPoint = @import("../../../bundler.zig").ServerEntryPoint; -const js_printer = @import("../../../js_printer.zig"); -const js_parser = @import("../../../js_parser.zig"); -const js_ast = @import("../../../js_ast.zig"); -const hash_map = @import("../../../hash_map.zig"); -const http = @import("../../../http.zig"); -const NodeFallbackModules = @import("../../../node_fallbacks.zig"); -const ImportKind = ast.ImportKind; -const Analytics = @import("../../../analytics/analytics_thread.zig"); -const ZigString = @import("../../../jsc.zig").ZigString; -const Runtime = @import("../../../runtime.zig"); -const Router = @import("./router.zig"); -const ImportRecord = ast.ImportRecord; -const DotEnv = @import("../../../env_loader.zig"); -const ParseResult = @import("../../../bundler.zig").ParseResult; -const PackageJSON = @import("../../../resolver/package_json.zig").PackageJSON; -const MacroRemap = @import("../../../resolver/package_json.zig").MacroMap; -const WebCore = @import("../../../jsc.zig").WebCore; -const Request = WebCore.Request; -const Response = WebCore.Response; -const Headers = WebCore.Headers; -const Fetch = WebCore.Fetch; -const FetchEvent = WebCore.FetchEvent; -const js = @import("../../../jsc.zig").C; -const JSC = @import("../../../jsc.zig"); -const JSError = @import("../base.zig").JSError; -const d = @import("../base.zig").d; -const MarkedArrayBuffer = @import("../base.zig").MarkedArrayBuffer; -const getAllocator = @import("../base.zig").getAllocator; -const JSValue = @import("../../../jsc.zig").JSValue; -const NewClass = @import("../base.zig").NewClass; -const Microtask = @import("../../../jsc.zig").Microtask; -const JSGlobalObject = @import("../../../jsc.zig").JSGlobalObject; -const ExceptionValueRef = @import("../../../jsc.zig").ExceptionValueRef; -const JSPrivateDataPtr = @import("../../../jsc.zig").JSPrivateDataPtr; -const ZigConsoleClient = @import("../../../jsc.zig").ZigConsoleClient; -const Node = @import("../../../jsc.zig").Node; -const ZigException = @import("../../../jsc.zig").ZigException; -const ZigStackTrace = @import("../../../jsc.zig").ZigStackTrace; -const ErrorableResolvedSource = @import("../../../jsc.zig").ErrorableResolvedSource; -const ResolvedSource = @import("../../../jsc.zig").ResolvedSource; -const JSPromise = @import("../../../jsc.zig").JSPromise; -const JSInternalPromise = @import("../../../jsc.zig").JSInternalPromise; -const JSModuleLoader = @import("../../../jsc.zig").JSModuleLoader; -const JSPromiseRejectionOperation = @import("../../../jsc.zig").JSPromiseRejectionOperation; -const Exception = @import("../../../jsc.zig").Exception; -const ErrorableZigString = @import("../../../jsc.zig").ErrorableZigString; -const ZigGlobalObject = @import("../../../jsc.zig").ZigGlobalObject; -const VM = @import("../../../jsc.zig").VM; -const JSFunction = @import("../../../jsc.zig").JSFunction; -const Config = @import("../config.zig"); -const URL = @import("../../../url.zig").URL; -const Transpiler = @import("./transpiler.zig"); -const VirtualMachine = @import("../javascript.zig").VirtualMachine; -const IOTask = JSC.IOTask; -const ComptimeStringMap = @import("../../../comptime_string_map.zig").ComptimeStringMap; - -const TCC = @import("../../../tcc.zig"); - -pub const FFI = struct { - dylib: ?std.DynLib = null, - functions: std.StringArrayHashMapUnmanaged(Function) = .{}, - closed: bool = false, - - pub const Class = JSC.NewClass( - FFI, - .{ .name = "class" }, - .{ .call = JSC.wrapWithHasContainer(FFI, "close", false, true, true) }, - .{}, - ); - - pub fn callback(globalThis: *JSGlobalObject, interface: JSC.JSValue, js_callback: JSC.JSValue) JSValue { - if (!interface.isObject()) { - return JSC.toInvalidArguments("Expected object", .{}, globalThis.ref()); - } - - if (js_callback.isEmptyOrUndefinedOrNull() or !js_callback.isCallable(globalThis.vm())) { - return JSC.toInvalidArguments("Expected callback function", .{}, globalThis.ref()); - } - - const allocator = VirtualMachine.vm.allocator; - var function: Function = .{}; - var func = &function; - - if (generateSymbolForFunction(globalThis, allocator, interface, func) catch ZigString.init("Out of memory").toErrorInstance(globalThis)) |val| { - return val; - } - - // TODO: WeakRefHandle that automatically frees it? - JSC.C.JSValueProtect(globalThis.ref(), js_callback.asObjectRef()); - func.base_name = ""; - - func.compileCallback(allocator, globalThis, js_callback.asObjectRef().?) catch return ZigString.init("Out of memory").toErrorInstance(globalThis); - switch (func.step) { - .failed => |err| { - JSC.C.JSValueUnprotect(globalThis.ref(), js_callback.asObjectRef()); - const message = ZigString.init(err.msg).toErrorInstance(globalThis); - - func.deinit(allocator); - - return message; - }, - .pending => { - JSC.C.JSValueUnprotect(globalThis.ref(), js_callback.asObjectRef()); - func.deinit(allocator); - return ZigString.init("Failed to compile, but not sure why. Please report this bug").toErrorInstance(globalThis); - }, - .compiled => { - var function_ = bun.default_allocator.create(Function) catch unreachable; - function_.* = func.*; - return JSC.JSValue.jsNumber(@bitCast(f64, @as(usize, @ptrToInt(function_.step.compiled.ptr)))); - }, - } - } - - pub fn close(this: *FFI) JSValue { - if (this.closed) { - return JSC.JSValue.jsUndefined(); - } - this.closed = true; - if (this.dylib) |*dylib| { - dylib.close(); - this.dylib = null; - } - - const allocator = VirtualMachine.vm.allocator; - - for (this.functions.values()) |*val| { - val.deinit(allocator); - } - this.functions.deinit(allocator); - - return JSC.JSValue.jsUndefined(); - } - - pub fn printCallback(global: *JSGlobalObject, object: JSC.JSValue) JSValue { - const allocator = VirtualMachine.vm.allocator; - - if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { - return JSC.toInvalidArguments("Expected an object", .{}, global.ref()); - } - - var function: Function = .{}; - if (generateSymbolForFunction(global, allocator, object, &function) catch ZigString.init("Out of memory").toErrorInstance(global)) |val| { - return val; - } - - var arraylist = std.ArrayList(u8).init(allocator); - defer arraylist.deinit(); - var writer = arraylist.writer(); - - function.base_name = "my_callback_function"; - - function.printCallbackSourceCode(&writer) catch { - return ZigString.init("Error while printing code").toErrorInstance(global); - }; - return ZigString.init(arraylist.items).toValueGC(global); - } - - pub fn print(global: *JSGlobalObject, object: JSC.JSValue, is_callback_val: ?JSC.JSValue) JSValue { - const allocator = VirtualMachine.vm.allocator; - if (is_callback_val) |is_callback| { - if (is_callback.toBoolean()) { - return printCallback(global, object); - } - } - - if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { - return JSC.toInvalidArguments("Expected an options object with symbol names", .{}, global.ref()); - } - - var symbols = std.StringArrayHashMapUnmanaged(Function){}; - if (generateSymbols(global, &symbols, object) catch JSC.JSValue.zero) |val| { - // an error while validating symbols - for (symbols.keys()) |key| { - allocator.free(bun.constStrToU8(key)); - } - symbols.clearAndFree(allocator); - return val; - } - - var zig_strings = allocator.alloc(ZigString, symbols.count()) catch unreachable; - for (symbols.values()) |*function, i| { - var arraylist = std.ArrayList(u8).init(allocator); - var writer = arraylist.writer(); - function.printSourceCode(&writer) catch { - // an error while generating source code - for (symbols.keys()) |key| { - allocator.free(bun.constStrToU8(key)); - } - for (zig_strings) |zig_string| { - allocator.free(bun.constStrToU8(zig_string.slice())); - } - for (symbols.values()) |*function_| { - function_.arg_types.deinit(allocator); - } - - symbols.clearAndFree(allocator); - return ZigString.init("Error while printing code").toErrorInstance(global); - }; - zig_strings[i] = ZigString.init(arraylist.items); - } - - const ret = JSC.JSValue.createStringArray(global, zig_strings.ptr, zig_strings.len, true); - - for (symbols.keys()) |key| { - allocator.free(bun.constStrToU8(key)); - } - for (zig_strings) |zig_string| { - allocator.free(bun.constStrToU8(zig_string.slice())); - } - for (symbols.values()) |*function_| { - function_.arg_types.deinit(allocator); - if (function_.step == .compiled) { - allocator.free(function_.step.compiled.buf); - } - } - symbols.clearAndFree(allocator); - - return ret; - } - - // pub fn dlcompile(global: *JSGlobalObject, object: JSC.JSValue) JSValue { - // const allocator = VirtualMachine.vm.allocator; - - // if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { - // return JSC.toInvalidArguments("Expected an options object with symbol names", .{}, global.ref()); - // } - - // var symbols = std.StringArrayHashMapUnmanaged(Function){}; - // if (generateSymbols(global, &symbols, object) catch JSC.JSValue.zero) |val| { - // // an error while validating symbols - // for (symbols.keys()) |key| { - // allocator.free(bun.constStrToU8(key)); - // } - // symbols.clearAndFree(allocator); - // return val; - // } - - // } - - pub fn open(global: *JSGlobalObject, name_str: ZigString, object: JSC.JSValue) JSC.JSValue { - const allocator = VirtualMachine.vm.allocator; - var name_slice = name_str.toSlice(allocator); - defer name_slice.deinit(); - - if (name_slice.len == 0) { - return JSC.toInvalidArguments("Invalid library name", .{}, global.ref()); - } - - if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { - return JSC.toInvalidArguments("Expected an options object with symbol names", .{}, global.ref()); - } - - const name = name_slice.sliceZ(); - var symbols = std.StringArrayHashMapUnmanaged(Function){}; - if (generateSymbols(global, &symbols, object) catch JSC.JSValue.zero) |val| { - // an error while validating symbols - for (symbols.keys()) |key| { - allocator.free(bun.constStrToU8(key)); - } - symbols.clearAndFree(allocator); - return val; - } - if (symbols.count() == 0) { - return JSC.toInvalidArguments("Expected at least one symbol", .{}, global.ref()); - } - - var dylib = std.DynLib.open(name) catch { - return JSC.toInvalidArguments("Failed to open library", .{}, global.ref()); - }; - - var obj = JSC.JSValue.c(JSC.C.JSObjectMake(global.ref(), null, null)); - JSC.C.JSValueProtect(global.ref(), obj.asObjectRef()); - defer JSC.C.JSValueUnprotect(global.ref(), obj.asObjectRef()); - for (symbols.values()) |*function| { - const function_name = function.base_name.?; - - // optional if the user passed "ptr" - if (function.symbol_from_dynamic_library == null) { - var resolved_symbol = dylib.lookup(*anyopaque, function_name) orelse { - const ret = JSC.toInvalidArguments("Symbol \"{s}\" not found in \"{s}\"", .{ std.mem.span(function_name), name_slice.slice() }, global.ref()); - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - symbols.clearAndFree(allocator); - dylib.close(); - return ret; - }; - - function.symbol_from_dynamic_library = resolved_symbol; - } - - function.compile(allocator) catch |err| { - const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\" in \"{s}\"", .{ - std.mem.span(@errorName(err)), - std.mem.span(function_name), - name_slice.slice(), - }, global.ref()); - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - symbols.clearAndFree(allocator); - dylib.close(); - return ret; - }; - switch (function.step) { - .failed => |err| { - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - - const res = ZigString.init(err.msg).toErrorInstance(global); - function.deinit(allocator); - symbols.clearAndFree(allocator); - dylib.close(); - return res; - }, - .pending => { - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - symbols.clearAndFree(allocator); - dylib.close(); - return ZigString.init("Failed to compile (nothing happend!)").toErrorInstance(global); - }, - .compiled => |compiled| { - var cb = JSC.NewFunctionPtr( - global, - &ZigString.init(std.mem.span(function_name)), - @intCast(u32, function.arg_types.items.len), - compiled.ptr, - ); - - obj.put(global, &ZigString.init(std.mem.span(function_name)), JSC.JSValue.cast(cb)); - }, - } - } - - var lib = allocator.create(FFI) catch unreachable; - lib.* = .{ - .dylib = dylib, - .functions = symbols, - }; - - var close_object = JSC.JSValue.c(Class.make(global.ref(), lib)); - - return JSC.JSValue.createObject2(global, &ZigString.init("close"), &ZigString.init("symbols"), close_object, obj); - } - - pub fn linkSymbols(global: *JSGlobalObject, object: JSC.JSValue) JSC.JSValue { - const allocator = VirtualMachine.vm.allocator; - - if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { - return JSC.toInvalidArguments("Expected an options object with symbol names", .{}, global.ref()); - } - - var symbols = std.StringArrayHashMapUnmanaged(Function){}; - if (generateSymbols(global, &symbols, object) catch JSC.JSValue.zero) |val| { - // an error while validating symbols - for (symbols.keys()) |key| { - allocator.free(bun.constStrToU8(key)); - } - symbols.clearAndFree(allocator); - return val; - } - if (symbols.count() == 0) { - return JSC.toInvalidArguments("Expected at least one symbol", .{}, global.ref()); - } - - var obj = JSC.JSValue.c(JSC.C.JSObjectMake(global.ref(), null, null)); - JSC.C.JSValueProtect(global.ref(), obj.asObjectRef()); - defer JSC.C.JSValueUnprotect(global.ref(), obj.asObjectRef()); - for (symbols.values()) |*function| { - const function_name = function.base_name.?; - - if (function.symbol_from_dynamic_library == null) { - const ret = JSC.toInvalidArguments("Symbol for \"{s}\" not found", .{std.mem.span(function_name)}, global.ref()); - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - symbols.clearAndFree(allocator); - return ret; - } - - function.compile(allocator) catch |err| { - const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\"", .{ - std.mem.span(@errorName(err)), - std.mem.span(function_name), - }, global.ref()); - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - symbols.clearAndFree(allocator); - return ret; - }; - switch (function.step) { - .failed => |err| { - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - - const res = ZigString.init(err.msg).toErrorInstance(global); - function.deinit(allocator); - symbols.clearAndFree(allocator); - return res; - }, - .pending => { - for (symbols.values()) |*value| { - allocator.free(bun.constStrToU8(std.mem.span(value.base_name.?))); - value.arg_types.clearAndFree(allocator); - } - symbols.clearAndFree(allocator); - return ZigString.init("Failed to compile (nothing happend!)").toErrorInstance(global); - }, - .compiled => |compiled| { - var cb = JSC.NewFunctionPtr( - global, - &ZigString.init(std.mem.span(function_name)), - @intCast(u32, function.arg_types.items.len), - compiled.ptr, - ); - - obj.put(global, &ZigString.init(std.mem.span(function_name)), JSC.JSValue.cast(cb)); - }, - } - } - - var lib = allocator.create(FFI) catch unreachable; - lib.* = .{ - .dylib = null, - .functions = symbols, - }; - - var close_object = JSC.JSValue.c(Class.make(global.ref(), lib)); - - return JSC.JSValue.createObject2(global, &ZigString.init("close"), &ZigString.init("symbols"), close_object, obj); - } - pub fn generateSymbolForFunction(global: *JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, function: *Function) !?JSValue { - var abi_types = std.ArrayListUnmanaged(ABIType){}; - - if (value.get(global, "args")) |args| { - if (args.isEmptyOrUndefinedOrNull() or !args.jsType().isArray()) { - return ZigString.init("Expected an object with \"args\" as an array").toErrorInstance(global); - } - - var array = args.arrayIterator(global); - - try abi_types.ensureTotalCapacityPrecise(allocator, array.len); - while (array.next()) |val| { - if (val.isEmptyOrUndefinedOrNull()) { - abi_types.clearAndFree(allocator); - return ZigString.init("param must be a string (type name) or number").toErrorInstance(global); - } - - if (val.isAnyInt()) { - const int = val.toInt32(); - switch (int) { - 0...14 => { - abi_types.appendAssumeCapacity(@intToEnum(ABIType, int)); - continue; - }, - else => { - abi_types.clearAndFree(allocator); - return ZigString.init("invalid ABI type").toErrorInstance(global); - }, - } - } - - if (!val.jsType().isStringLike()) { - abi_types.clearAndFree(allocator); - return ZigString.init("param must be a string (type name) or number").toErrorInstance(global); - } - - var type_name = val.toSlice(global, allocator); - defer type_name.deinit(); - abi_types.appendAssumeCapacity(ABIType.label.get(type_name.slice()) orelse { - abi_types.clearAndFree(allocator); - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown type {s}", .{type_name.slice()}, global.ref()); - }); - } - } - // var function - var return_type = ABIType.@"void"; - - if (value.get(global, "returns")) |ret_value| brk: { - if (ret_value.isAnyInt()) { - const int = ret_value.toInt32(); - switch (int) { - 0...14 => { - return_type = @intToEnum(ABIType, int); - break :brk; - }, - else => { - abi_types.clearAndFree(allocator); - return ZigString.init("invalid ABI type").toErrorInstance(global); - }, - } - } - - var ret_slice = ret_value.toSlice(global, allocator); - defer ret_slice.deinit(); - return_type = ABIType.label.get(ret_slice.slice()) orelse { - abi_types.clearAndFree(allocator); - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown return type {s}", .{ret_slice.slice()}, global.ref()); - }; - } - - function.* = Function{ - .base_name = null, - .arg_types = abi_types, - .return_type = return_type, - }; - - if (value.get(global, "ptr")) |ptr| { - if (ptr.isNumber()) { - const num = @bitCast(usize, ptr.asNumber()); - if (num > 0) - function.symbol_from_dynamic_library = @intToPtr(*anyopaque, num); - } else { - const num = ptr.toUInt64NoTruncate(); - if (num > 0) { - function.symbol_from_dynamic_library = @intToPtr(*anyopaque, num); - } - } - } - - return null; - } - pub fn generateSymbols(global: *JSGlobalObject, symbols: *std.StringArrayHashMapUnmanaged(Function), object: JSC.JSValue) !?JSValue { - const allocator = VirtualMachine.vm.allocator; - - var keys = JSC.C.JSObjectCopyPropertyNames(global.ref(), object.asObjectRef()); - defer JSC.C.JSPropertyNameArrayRelease(keys); - const count = JSC.C.JSPropertyNameArrayGetCount(keys); - - try symbols.ensureTotalCapacity(allocator, count); - - var i: usize = 0; - while (i < count) : (i += 1) { - var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(keys, i); - defer JSC.C.JSStringRelease(property_name_ref); - const len = JSC.C.JSStringGetLength(property_name_ref); - if (len == 0) continue; - var prop = JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..len]; - - var value = JSC.JSValue.c(JSC.C.JSObjectGetProperty(global.ref(), object.asObjectRef(), property_name_ref, null)); - if (value.isEmptyOrUndefinedOrNull()) { - return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Expected an object for key \"{s}\"", .{prop}, global.ref()); - } - - var function: Function = .{}; - if (try generateSymbolForFunction(global, allocator, value, &function)) |val| { - return val; - } - function.base_name = try allocator.dupeZ(u8, prop); - - symbols.putAssumeCapacity(std.mem.span(function.base_name.?), function); - } - - return null; - } - - pub const Function = struct { - symbol_from_dynamic_library: ?*anyopaque = null, - base_name: ?[:0]const u8 = null, - state: ?*TCC.TCCState = null, - - return_type: ABIType = ABIType.@"void", - arg_types: std.ArrayListUnmanaged(ABIType) = .{}, - step: Step = Step{ .pending = {} }, - - pub var lib_dirZ: [*:0]const u8 = ""; - - pub fn deinit(val: *Function, allocator: std.mem.Allocator) void { - if (val.base_name) |base_name| { - if (std.mem.span(base_name).len > 0) { - allocator.free(bun.constStrToU8(std.mem.span(base_name))); - } - } - - val.arg_types.clearAndFree(allocator); - - if (val.state) |state| { - TCC.tcc_delete(state); - val.state = null; - } - - if (val.step == .compiled) { - // allocator.free(val.step.compiled.buf); - if (val.step.compiled.js_function) |js_function| { - JSC.C.JSValueUnprotect(@ptrCast(JSC.C.JSContextRef, val.step.compiled.js_context.?), @ptrCast(JSC.C.JSObjectRef, js_function)); - } - } - - if (val.step == .failed and val.step.failed.allocated) { - allocator.free(val.step.failed.msg); - } - } - - pub const Step = union(enum) { - pending: void, - compiled: struct { - ptr: *anyopaque, - buf: []u8, - js_function: ?*anyopaque = null, - js_context: ?*anyopaque = null, - }, - failed: struct { - msg: []const u8, - allocated: bool = false, - }, - }; - - const FFI_HEADER: string = @embedFile("./FFI.h"); - pub inline fn ffiHeader() string { - if (comptime Environment.isDebug) { - var dirpath = std.fs.path.dirname(@src().file).?; - var env = std.process.getEnvMap(default_allocator) catch unreachable; - - const dir = std.mem.replaceOwned( - u8, - default_allocator, - dirpath, - "jarred", - env.get("USER").?, - ) catch unreachable; - var runtime_path = std.fs.path.join(default_allocator, &[_]string{ dir, "FFI.h" }) catch unreachable; - const file = std.fs.openFileAbsolute(runtime_path, .{}) catch @panic("Missing bun/src/javascript/jsc/api/FFI.h."); - defer file.close(); - return file.readToEndAlloc(default_allocator, (file.stat() catch unreachable).size) catch unreachable; - } else { - return FFI_HEADER; - } - } - - pub fn handleTCCError(ctx: ?*anyopaque, message: [*c]const u8) callconv(.C) void { - var this = bun.cast(*Function, ctx.?); - var msg = std.mem.span(message); - if (msg.len > 0) { - var offset: usize = 0; - // the message we get from TCC sometimes has garbage in it - // i think because we're doing in-memory compilation - while (offset < msg.len) : (offset += 1) { - if (msg[offset] > 0x20 and msg[offset] < 0x7f) break; - } - msg = msg[offset..]; - } - - this.step = .{ .failed = .{ .msg = VirtualMachine.vm.allocator.dupe(u8, msg) catch unreachable, .allocated = true } }; - } - - extern fn pthread_jit_write_protect_np(enable: bool) callconv(.C) void; - - const MyFunctionSStructWorkAround = struct { - JSVALUE_TO_INT64: fn (JSValue0: JSC.JSValue) callconv(.C) i64, - JSVALUE_TO_UINT64: fn (JSValue0: JSC.JSValue) callconv(.C) u64, - INT64_TO_JSVALUE: fn (arg0: [*c]JSC.JSGlobalObject, arg1: i64) callconv(.C) JSC.JSValue, - UINT64_TO_JSVALUE: fn (arg0: [*c]JSC.JSGlobalObject, arg1: u64) callconv(.C) JSC.JSValue, - bun_call: *const @TypeOf(JSC.C.JSObjectCallAsFunction), - }; - const headers = @import("../bindings/headers.zig"); - - var workaround: MyFunctionSStructWorkAround = .{ - .JSVALUE_TO_INT64 = headers.JSC__JSValue__toInt64, - .JSVALUE_TO_UINT64 = headers.JSC__JSValue__toUInt64NoTruncate, - .INT64_TO_JSVALUE = headers.JSC__JSValue__fromInt64NoTruncate, - .UINT64_TO_JSVALUE = headers.JSC__JSValue__fromUInt64NoTruncate, - .bun_call = &JSC.C.JSObjectCallAsFunction, - }; - - const tcc_options = "-std=c11 -nostdlib -Wl,--export-all-symbols" ++ if (Environment.isDebug) " -g" else ""; - - pub fn compile( - this: *Function, - allocator: std.mem.Allocator, - ) !void { - var source_code = std.ArrayList(u8).init(allocator); - var source_code_writer = source_code.writer(); - try this.printSourceCode(&source_code_writer); - - try source_code.append(0); - defer source_code.deinit(); - - var state = TCC.tcc_new() orelse return error.TCCMissing; - TCC.tcc_set_options(state, tcc_options); - // addSharedLibPaths(state); - TCC.tcc_set_error_func(state, this, handleTCCError); - this.state = state; - defer { - if (this.step == .failed) { - TCC.tcc_delete(state); - this.state = null; - } - } - - _ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY); - const Sizes = @import("../bindings/sizes.zig"); - - var symbol_buf: [256]u8 = undefined; - TCC.tcc_define_symbol( - state, - "Bun_FFI_PointerOffsetToArgumentsList", - std.fmt.bufPrintZ(&symbol_buf, "{d}", .{Sizes.Bun_FFI_PointerOffsetToArgumentsList}) catch unreachable, - ); - CompilerRT.define(state); - - // TCC.tcc_define_symbol( - // state, - // "Bun_FFI_PointerOffsetToArgumentsCount", - // std.fmt.bufPrintZ(symbol_buf[8..], "{d}", .{Bun_FFI_PointerOffsetToArgumentsCount}) catch unreachable, - // ); - - const compilation_result = TCC.tcc_compile_string( - state, - source_code.items.ptr, - ); - // did tcc report an error? - if (this.step == .failed) { - return; - } - - // did tcc report failure but never called the error callback? - if (compilation_result == -1) { - this.step = .{ .failed = .{ .msg = "tcc returned -1, which means it failed" } }; - return; - } - CompilerRT.inject(state); - _ = TCC.tcc_add_symbol(state, this.base_name.?, this.symbol_from_dynamic_library.?); - - if (this.step == .failed) { - return; - } - - var relocation_size = TCC.tcc_relocate(state, null); - if (this.step == .failed) { - return; - } - - if (relocation_size < 0) { - this.step = .{ .failed = .{ .msg = "tcc_relocate returned a negative value" } }; - return; - } - - var bytes: []u8 = try allocator.rawAlloc(@intCast(usize, relocation_size), 16, 16, 0); - defer { - if (this.step == .failed) { - allocator.free(bytes); - } - } - - if (comptime Environment.isAarch64 and Environment.isMac) { - pthread_jit_write_protect_np(false); - } - _ = TCC.tcc_relocate(state, bytes.ptr); - if (comptime Environment.isAarch64 and Environment.isMac) { - pthread_jit_write_protect_np(true); - } - - var symbol = TCC.tcc_get_symbol(state, "JSFunctionCall") orelse { - this.step = .{ .failed = .{ .msg = "missing generated symbol in source code" } }; - - return; - }; - - this.step = .{ - .compiled = .{ - .ptr = symbol, - .buf = bytes, - }, - }; - return; - } - const CompilerRT = struct { - noinline fn memset( - dest: [*]u8, - c: u8, - byte_count: usize, - ) callconv(.C) void { - @memset(dest, c, byte_count); - } - - noinline fn memcpy( - noalias dest: [*]u8, - noalias source: [*]const u8, - byte_count: usize, - ) callconv(.C) void { - @memcpy(dest, source, byte_count); - } - - pub fn define(state: *TCC.TCCState) void { - if (comptime Environment.isX64) { - _ = TCC.tcc_define_symbol(state, "NEEDS_COMPILER_RT_FUNCTIONS", "1"); - // there - _ = TCC.tcc_compile_string(state, @embedFile(("libtcc1.c"))); - } - } - - pub fn inject(state: *TCC.TCCState) void { - _ = TCC.tcc_add_symbol(state, "memset", &memset); - _ = TCC.tcc_add_symbol(state, "memcpy", &memcpy); - - _ = TCC.tcc_add_symbol( - state, - "JSVALUE_TO_INT64_SLOW", - workaround.JSVALUE_TO_INT64, - ); - _ = TCC.tcc_add_symbol( - state, - "JSVALUE_TO_UINT64_SLOW", - workaround.JSVALUE_TO_UINT64, - ); - std.mem.doNotOptimizeAway(headers.JSC__JSValue__toUInt64NoTruncate); - std.mem.doNotOptimizeAway(headers.JSC__JSValue__toInt64); - std.mem.doNotOptimizeAway(headers.JSC__JSValue__fromInt64NoTruncate); - std.mem.doNotOptimizeAway(headers.JSC__JSValue__fromUInt64NoTruncate); - _ = TCC.tcc_add_symbol( - state, - "INT64_TO_JSVALUE_SLOW", - workaround.INT64_TO_JSVALUE, - ); - _ = TCC.tcc_add_symbol( - state, - "UINT64_TO_JSVALUE_SLOW", - workaround.UINT64_TO_JSVALUE, - ); - } - }; - - pub fn compileCallback( - this: *Function, - allocator: std.mem.Allocator, - js_context: *anyopaque, - js_function: *anyopaque, - ) !void { - Output.debug("welcome", .{}); - var source_code = std.ArrayList(u8).init(allocator); - var source_code_writer = source_code.writer(); - try this.printCallbackSourceCode(&source_code_writer); - Output.debug("helllooo", .{}); - try source_code.append(0); - // defer source_code.deinit(); - var state = TCC.tcc_new() orelse return error.TCCMissing; - TCC.tcc_set_options(state, tcc_options); - TCC.tcc_set_error_func(state, this, handleTCCError); - this.state = state; - defer { - if (this.step == .failed) { - TCC.tcc_delete(state); - this.state = null; - } - } - - _ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY); - - CompilerRT.define(state); - - const compilation_result = TCC.tcc_compile_string( - state, - source_code.items.ptr, - ); - Output.debug("compile", .{}); - // did tcc report an error? - if (this.step == .failed) { - return; - } - - // did tcc report failure but never called the error callback? - if (compilation_result == -1) { - this.step = .{ .failed = .{ .msg = "tcc returned -1, which means it failed" } }; - - return; - } - - CompilerRT.inject(state); - Output.debug("here", .{}); - _ = TCC.tcc_add_symbol(state, "bun_call", workaround.bun_call.*); - _ = TCC.tcc_add_symbol(state, "cachedJSContext", js_context); - _ = TCC.tcc_add_symbol(state, "cachedCallbackFunction", js_function); - - var relocation_size = TCC.tcc_relocate(state, null); - if (relocation_size == 0) return; - var bytes: []u8 = try allocator.rawAlloc(@intCast(usize, relocation_size), 16, 16, 0); - defer { - if (this.step == .failed) { - allocator.free(bytes); - } - } - - if (comptime Environment.isAarch64 and Environment.isMac) { - pthread_jit_write_protect_np(false); - } - _ = TCC.tcc_relocate(state, bytes.ptr); - if (comptime Environment.isAarch64 and Environment.isMac) { - pthread_jit_write_protect_np(true); - } - - var symbol = TCC.tcc_get_symbol(state, "my_callback_function") orelse { - this.step = .{ .failed = .{ .msg = "missing generated symbol in source code" } }; - - return; - }; - - this.step = .{ - .compiled = .{ - .ptr = symbol, - .buf = bytes, - .js_function = js_function, - .js_context = js_context, - }, - }; - } - - pub fn printSourceCode( - this: *Function, - writer: anytype, - ) !void { - if (this.arg_types.items.len > 0) { - try writer.writeAll("#define HAS_ARGUMENTS\n"); - } - - brk: { - if (this.return_type.isFloatingPoint()) { - try writer.writeAll("#define USES_FLOAT 1\n"); - break :brk; - } - - for (this.arg_types.items) |arg| { - // conditionally include math.h - if (arg.isFloatingPoint()) { - try writer.writeAll("#define USES_FLOAT 1\n"); - break; - } - } - } - - if (comptime Environment.isRelease) { - try writer.writeAll(std.mem.span(FFI_HEADER)); - } else { - try writer.writeAll(ffiHeader()); - } - - // -- Generate the FFI function symbol - try writer.writeAll("/* --- The Function To Call */\n"); - try this.return_type.typename(writer); - try writer.writeAll(" "); - try writer.writeAll(std.mem.span(this.base_name.?)); - try writer.writeAll("("); - var first = true; - for (this.arg_types.items) |arg, i| { - if (!first) { - try writer.writeAll(", "); - } - first = false; - try arg.typename(writer); - try writer.print(" arg{d}", .{i}); - } - try writer.writeAll( - \\); - \\ - \\ - \\/* ---- Your Wrapper Function ---- */ - \\void* JSFunctionCall(void* globalObject, void* callFrame) { - \\ - ); - - if (this.arg_types.items.len > 0) { - try writer.writeAll( - \\ LOAD_ARGUMENTS_FROM_CALL_FRAME; - \\ - ); - for (this.arg_types.items) |arg, i| { - if (arg.needsACastInC()) { - if (i < this.arg_types.items.len - 1) { - try writer.print( - \\ EncodedJSValue arg{d}; - \\ arg{d}.asInt64 = *argsPtr++; - \\ - , - .{ - i, - i, - }, - ); - } else { - try writer.print( - \\ EncodedJSValue arg{d}; - \\ arg{d}.asInt64 = *argsPtr; - \\ - , - .{ - i, - i, - }, - ); - } - } else { - if (i < this.arg_types.items.len - 1) { - try writer.print( - \\ int64_t arg{d} = *argsPtr++; - \\ - , - .{ - i, - }, - ); - } else { - try writer.print( - \\ int64_t arg{d} = *argsPtr; - \\ - , - .{ - i, - }, - ); - } - } - } - } - - // try writer.writeAll( - // "(JSContext ctx, void* function, void* thisObject, size_t argumentCount, const EncodedJSValue arguments[], void* exception);\n\n", - // ); - - var arg_buf: [512]u8 = undefined; - - try writer.writeAll(" "); - if (!(this.return_type == .void)) { - try this.return_type.typename(writer); - try writer.writeAll(" return_value = "); - } - try writer.print("{s}(", .{std.mem.span(this.base_name.?)}); - first = true; - arg_buf[0..3].* = "arg".*; - for (this.arg_types.items) |arg, i| { - if (!first) { - try writer.writeAll(", "); - } - first = false; - - try writer.writeAll(" "); - const lengthBuf = std.fmt.bufPrintIntToSlice(arg_buf["arg".len..], i, 10, .lower, .{}); - const argName = arg_buf[0 .. 3 + lengthBuf.len]; - if (arg.needsACastInC()) { - try writer.print("{}", .{arg.toC(argName)}); - } else { - try writer.writeAll(argName); - } - } - try writer.writeAll(");\n"); - - if (!first) try writer.writeAll("\n"); - - try writer.writeAll(" "); - - try writer.writeAll("return "); - - if (!(this.return_type == .void)) { - try writer.print("{}.asPtr", .{this.return_type.toJS("return_value")}); - } else { - try writer.writeAll("ValueUndefined.asPtr"); - } - - try writer.writeAll(";\n}\n\n"); - } - - pub fn printCallbackSourceCode( - this: *Function, - writer: anytype, - ) !void { - try writer.writeAll("#define IS_CALLBACK 1\n"); - - brk: { - if (this.return_type.isFloatingPoint()) { - try writer.writeAll("#define USES_FLOAT 1\n"); - break :brk; - } - - for (this.arg_types.items) |arg| { - // conditionally include math.h - if (arg.isFloatingPoint()) { - try writer.writeAll("#define USES_FLOAT 1\n"); - break; - } - } - } - - if (comptime Environment.isRelease) { - try writer.writeAll(std.mem.span(FFI_HEADER)); - } else { - try writer.writeAll(ffiHeader()); - } - - // -- Generate the FFI function symbol - try writer.writeAll("\n \n/* --- The Callback Function */\n"); - try writer.writeAll("/* --- The Callback Function */\n"); - try this.return_type.typename(writer); - try writer.writeAll(" my_callback_function"); - try writer.writeAll("("); - var first = true; - for (this.arg_types.items) |arg, i| { - if (!first) { - try writer.writeAll(", "); - } - first = false; - try arg.typename(writer); - try writer.print(" arg{d}", .{i}); - } - try writer.writeAll(");\n\n"); - - first = true; - try this.return_type.typename(writer); - - try writer.writeAll(" my_callback_function"); - try writer.writeAll("("); - for (this.arg_types.items) |arg, i| { - if (!first) { - try writer.writeAll(", "); - } - first = false; - try arg.typename(writer); - try writer.print(" arg{d}", .{i}); - } - try writer.writeAll(") {\n"); - - if (comptime Environment.isDebug) { - try writer.writeAll("#ifdef INJECT_BEFORE\n"); - try writer.writeAll("INJECT_BEFORE;\n"); - try writer.writeAll("#endif\n"); - } - - first = true; - - if (this.arg_types.items.len > 0) { - try writer.print(" EncodedJSValue arguments[{d}] = {{\n", .{this.arg_types.items.len}); - - var arg_buf: [512]u8 = undefined; - arg_buf[0.."arg".len].* = "arg".*; - for (this.arg_types.items) |arg, i| { - const printed = std.fmt.bufPrintIntToSlice(arg_buf["arg".len..], i, 10, .lower, .{}); - const arg_name = arg_buf[0 .. "arg".len + printed.len]; - try writer.print(" {}", .{arg.toJS(arg_name)}); - if (i < this.arg_types.items.len - 1) { - try writer.writeAll(",\n"); - } - } - try writer.writeAll("\n };\n"); - } else { - try writer.writeAll(" EncodedJSValue arguments[1] = {{0}};\n"); - } - - try writer.writeAll(" "); - if (!(this.return_type == .void)) { - try writer.writeAll("EncodedJSValue return_value = {"); - } - // JSC.C.JSObjectCallAsFunction( - // ctx, - // object, - // thisObject, - // argumentCount, - // arguments, - // exception, - // ); - try writer.writeAll("bun_call(cachedJSContext, cachedCallbackFunction, (void*)0, "); - if (this.arg_types.items.len > 0) { - try writer.print("{d}, &arguments[0], (void*)0)", .{this.arg_types.items.len}); - } else { - try writer.writeAll("0, &arguments[0], (void*)0)"); - } - - if (this.return_type != .void) { - try writer.print("}};\n return {}", .{this.return_type.toC("return_value")}); - } - - try writer.writeAll(";\n}\n\n"); - } - }; - - pub const ABIType = enum(i32) { - char = 0, - - int8_t = 1, - uint8_t = 2, - - int16_t = 3, - uint16_t = 4, - - int32_t = 5, - uint32_t = 6, - - int64_t = 7, - uint64_t = 8, - - double = 9, - float = 10, - - bool = 11, - - ptr = 12, - - @"void" = 13, - - cstring = 14, - - i64_fast = 15, - u64_fast = 16, - - /// Types that we can directly pass through as an `int64_t` - pub fn needsACastInC(this: ABIType) bool { - return switch (this) { - .char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => false, - else => true, - }; - } - - const map = .{ - .{ "bool", ABIType.bool }, - .{ "c_int", ABIType.int32_t }, - .{ "c_uint", ABIType.uint32_t }, - .{ "char", ABIType.char }, - .{ "char*", ABIType.ptr }, - .{ "double", ABIType.double }, - .{ "f32", ABIType.float }, - .{ "f64", ABIType.double }, - .{ "float", ABIType.float }, - .{ "i16", ABIType.int16_t }, - .{ "i32", ABIType.int32_t }, - .{ "i64", ABIType.int64_t }, - .{ "i8", ABIType.int8_t }, - .{ "int", ABIType.int32_t }, - .{ "int16_t", ABIType.int16_t }, - .{ "int32_t", ABIType.int32_t }, - .{ "int64_t", ABIType.int64_t }, - .{ "int8_t", ABIType.int8_t }, - .{ "isize", ABIType.int64_t }, - .{ "u16", ABIType.uint16_t }, - .{ "u32", ABIType.uint32_t }, - .{ "u64", ABIType.uint64_t }, - .{ "u8", ABIType.uint8_t }, - .{ "uint16_t", ABIType.uint16_t }, - .{ "uint32_t", ABIType.uint32_t }, - .{ "uint64_t", ABIType.uint64_t }, - .{ "uint8_t", ABIType.uint8_t }, - .{ "usize", ABIType.uint64_t }, - .{ "void*", ABIType.ptr }, - .{ "ptr", ABIType.ptr }, - .{ "pointer", ABIType.ptr }, - .{ "void", ABIType.@"void" }, - .{ "cstring", ABIType.@"cstring" }, - .{ "i64_fast", ABIType.i64_fast }, - .{ "u64_fast", ABIType.u64_fast }, - }; - pub const label = ComptimeStringMap(ABIType, map); - const EnumMapFormatter = struct { - name: []const u8, - entry: ABIType, - pub fn format(self: EnumMapFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - try writer.writeAll("['"); - // these are not all valid identifiers - try writer.writeAll(self.name); - try writer.writeAll("']:"); - try std.fmt.formatInt(@enumToInt(self.entry), 10, .lower, .{}, writer); - try writer.writeAll(",'"); - try std.fmt.formatInt(@enumToInt(self.entry), 10, .lower, .{}, writer); - try writer.writeAll("':"); - try std.fmt.formatInt(@enumToInt(self.entry), 10, .lower, .{}, writer); - } - }; - pub const map_to_js_object = brk: { - var count: usize = 2; - for (map) |item, i| { - var fmt = EnumMapFormatter{ .name = item.@"0", .entry = item.@"1" }; - count += std.fmt.count("{}", .{fmt}); - count += @boolToInt(i > 0); - } - - var buf: [count]u8 = undefined; - buf[0] = '{'; - buf[buf.len - 1] = '}'; - var end: usize = 1; - for (map) |item, i| { - var fmt = EnumMapFormatter{ .name = item.@"0", .entry = item.@"1" }; - if (i > 0) { - buf[end] = ','; - end += 1; - } - end += (std.fmt.bufPrint(buf[end..], "{}", .{fmt}) catch unreachable).len; - } - - break :brk buf; - }; - - pub fn isFloatingPoint(this: ABIType) bool { - return switch (this) { - .double, .float => true, - else => false, - }; - } - - const ToCFormatter = struct { - symbol: string, - tag: ABIType, - - pub fn format(self: ToCFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - switch (self.tag) { - .void => {}, - .bool => { - try writer.print("JSVALUE_TO_BOOL({s})", .{self.symbol}); - }, - .char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => { - try writer.print("JSVALUE_TO_INT32({s})", .{self.symbol}); - }, - .i64_fast, .int64_t => { - try writer.print("JSVALUE_TO_INT64({s})", .{self.symbol}); - }, - .u64_fast, .uint64_t => { - try writer.print("JSVALUE_TO_UINT64(globalObject, {s})", .{self.symbol}); - }, - .cstring, .ptr => { - try writer.print("JSVALUE_TO_PTR({s})", .{self.symbol}); - }, - .double => { - try writer.print("JSVALUE_TO_DOUBLE({s})", .{self.symbol}); - }, - .float => { - try writer.print("JSVALUE_TO_FLOAT({s})", .{self.symbol}); - }, - } - } - }; - - const ToJSFormatter = struct { - symbol: []const u8, - tag: ABIType, - - pub fn format(self: ToJSFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { - switch (self.tag) { - .void => {}, - .bool => { - try writer.print("BOOLEAN_TO_JSVALUE({s})", .{self.symbol}); - }, - .char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => { - try writer.print("INT32_TO_JSVALUE({s})", .{self.symbol}); - }, - .i64_fast => { - try writer.print("INT64_TO_JSVALUE(globalObject, {s})", .{self.symbol}); - }, - .int64_t => { - try writer.print("INT64_TO_JSVALUE_SLOW(globalObject, {s})", .{self.symbol}); - }, - .u64_fast => { - try writer.print("UINT64_TO_JSVALUE(globalObject, {s})", .{self.symbol}); - }, - .uint64_t => { - try writer.print("UINT64_TO_JSVALUE_SLOW(globalObject, {s})", .{self.symbol}); - }, - .cstring, .ptr => { - try writer.print("PTR_TO_JSVALUE({s})", .{self.symbol}); - }, - .double => { - try writer.print("DOUBLE_TO_JSVALUE({s})", .{self.symbol}); - }, - .float => { - try writer.print("FLOAT_TO_JSVALUE({s})", .{self.symbol}); - }, - } - } - }; - - pub fn toC(this: ABIType, symbol: string) ToCFormatter { - return ToCFormatter{ .tag = this, .symbol = symbol }; - } - - pub fn toJS( - this: ABIType, - symbol: string, - ) ToJSFormatter { - return ToJSFormatter{ - .tag = this, - .symbol = symbol, - }; - } - - pub fn typename(this: ABIType, writer: anytype) !void { - try writer.writeAll(this.typenameLabel()); - } - - pub fn typenameLabel(this: ABIType) []const u8 { - return switch (this) { - .cstring, .ptr => "void*", - .bool => "bool", - .int8_t => "int8_t", - .uint8_t => "uint8_t", - .int16_t => "int16_t", - .uint16_t => "uint16_t", - .int32_t => "int32_t", - .uint32_t => "uint32_t", - .i64_fast, .int64_t => "int64_t", - .u64_fast, .uint64_t => "uint64_t", - .double => "double", - .float => "float", - .char => "char", - .void => "void", - }; - } - }; -}; diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig deleted file mode 100644 index 302af6aac..000000000 --- a/src/javascript/jsc/api/html_rewriter.zig +++ /dev/null @@ -1,1886 +0,0 @@ -const std = @import("std"); -const Api = @import("../../../api/schema.zig").Api; -const FilesystemRouter = @import("../../../router.zig"); -const http = @import("../../../http.zig"); -const JavaScript = @import("../javascript.zig"); -const QueryStringMap = @import("../../../url.zig").QueryStringMap; -const CombinedScanner = @import("../../../url.zig").CombinedScanner; -const bun = @import("../../../global.zig"); -const string = bun.string; -const JSC = @import("../../../jsc.zig"); -const js = JSC.C; -const WebCore = @import("../webcore/response.zig"); -const Router = @This(); -const Bundler = @import("../../../bundler.zig"); -const VirtualMachine = JavaScript.VirtualMachine; -const ScriptSrcStream = std.io.FixedBufferStream([]u8); -const ZigString = JSC.ZigString; -const Fs = @import("../../../fs.zig"); -const Base = @import("../base.zig"); -const getAllocator = Base.getAllocator; -const JSObject = JSC.JSObject; -const JSError = Base.JSError; -const JSValue = JSC.JSValue; -const JSGlobalObject = JSC.JSGlobalObject; -const strings = @import("strings"); -const NewClass = Base.NewClass; -const To = Base.To; -const Request = WebCore.Request; -const d = Base.d; -const FetchEvent = WebCore.FetchEvent; -const Response = WebCore.Response; -const LOLHTML = @import("lolhtml"); - -const SelectorMap = std.ArrayListUnmanaged(*LOLHTML.HTMLSelector); -pub const LOLHTMLContext = struct { - selectors: SelectorMap = .{}, - element_handlers: std.ArrayListUnmanaged(*ElementHandler) = .{}, - document_handlers: std.ArrayListUnmanaged(*DocumentHandler) = .{}, - - pub fn deinit(this: *LOLHTMLContext, allocator: std.mem.Allocator) void { - for (this.selectors.items) |selector| { - selector.deinit(); - } - this.selectors.deinit(allocator); - this.selectors = .{}; - - for (this.element_handlers.items) |handler| { - handler.deinit(); - } - this.element_handlers.deinit(allocator); - this.element_handlers = .{}; - - for (this.document_handlers.items) |handler| { - handler.deinit(); - } - this.document_handlers.deinit(allocator); - this.document_handlers = .{}; - } -}; -pub const HTMLRewriter = struct { - builder: *LOLHTML.HTMLRewriter.Builder, - context: LOLHTMLContext, - - pub const Constructor = JSC.NewConstructor(HTMLRewriter, .{ .constructor = constructor }, .{}); - - pub const Class = NewClass( - HTMLRewriter, - .{ .name = "HTMLRewriter" }, - .{ - .finalize = finalize, - .on = .{ - .rfn = wrap(HTMLRewriter, "on"), - }, - .onDocument = .{ - .rfn = wrap(HTMLRewriter, "onDocument"), - }, - .transform = .{ - .rfn = wrap(HTMLRewriter, "transform"), - }, - }, - .{}, - ); - - pub fn constructor( - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSObjectRef { - var rewriter = bun.default_allocator.create(HTMLRewriter) catch unreachable; - rewriter.* = HTMLRewriter{ - .builder = LOLHTML.HTMLRewriter.Builder.init(), - .context = .{}, - }; - return HTMLRewriter.Class.make(ctx, rewriter); - } - - pub fn on( - this: *HTMLRewriter, - global: *JSGlobalObject, - selector_name: ZigString, - thisObject: JSC.C.JSObjectRef, - listener: JSValue, - exception: JSC.C.ExceptionRef, - ) JSValue { - var selector_slice = std.fmt.allocPrint(bun.default_allocator, "{}", .{selector_name}) catch unreachable; - - var selector = LOLHTML.HTMLSelector.parse(selector_slice) catch - return throwLOLHTMLError(global); - var handler_ = ElementHandler.init(global, listener, exception); - if (exception.* != null) { - selector.deinit(); - return JSValue.fromRef(exception.*); - } - var handler = getAllocator(global.ref()).create(ElementHandler) catch unreachable; - handler.* = handler_; - - this.builder.addElementContentHandlers( - selector, - - ElementHandler, - ElementHandler.onElement, - if (handler.onElementCallback != null) - handler - else - null, - - ElementHandler, - ElementHandler.onComment, - if (handler.onCommentCallback != null) - handler - else - null, - - ElementHandler, - ElementHandler.onText, - if (handler.onTextCallback != null) - handler - else - null, - ) catch { - selector.deinit(); - return throwLOLHTMLError(global); - }; - - this.context.selectors.append(bun.default_allocator, selector) catch unreachable; - this.context.element_handlers.append(bun.default_allocator, handler) catch unreachable; - return JSValue.fromRef(thisObject); - } - - pub fn onDocument( - this: *HTMLRewriter, - global: *JSGlobalObject, - listener: JSValue, - thisObject: JSC.C.JSObjectRef, - exception: JSC.C.ExceptionRef, - ) JSValue { - var handler_ = DocumentHandler.init(global, listener, exception); - if (exception.* != null) { - return JSValue.fromRef(exception.*); - } - - var handler = getAllocator(global.ref()).create(DocumentHandler) catch unreachable; - handler.* = handler_; - - this.builder.addDocumentContentHandlers( - DocumentHandler, - DocumentHandler.onDocType, - if (handler.onDocTypeCallback != null) - handler - else - null, - - DocumentHandler, - DocumentHandler.onComment, - if (handler.onCommentCallback != null) - handler - else - null, - - DocumentHandler, - DocumentHandler.onText, - if (handler.onTextCallback != null) - handler - else - null, - - DocumentHandler, - DocumentHandler.onEnd, - if (handler.onEndCallback != null) - handler - else - null, - ) catch { - return throwLOLHTMLError(global); - }; - - this.context.document_handlers.append(bun.default_allocator, handler) catch unreachable; - return JSValue.fromRef(thisObject); - } - - pub fn finalize(this: *HTMLRewriter) void { - this.finalizeWithoutDestroy(); - bun.default_allocator.destroy(this); - } - - pub fn finalizeWithoutDestroy(this: *HTMLRewriter) void { - this.context.deinit(bun.default_allocator); - } - - pub fn beginTransform(this: *HTMLRewriter, global: *JSGlobalObject, response: *Response) JSValue { - const new_context = this.context; - this.context = .{}; - return BufferOutputSink.init(new_context, global, response, this.builder); - } - - pub fn returnEmptyResponse(this: *HTMLRewriter, global: *JSGlobalObject, response: *Response) JSValue { - var result = bun.default_allocator.create(Response) catch unreachable; - - response.cloneInto(result, getAllocator(global.ref()), global); - this.finalizeWithoutDestroy(); - return JSValue.fromRef(Response.makeMaybePooled(global.ref(), result)); - } - - pub fn transform(this: *HTMLRewriter, global: *JSGlobalObject, response: *Response) JSValue { - var input = response.body.slice(); - - if (input.len == 0 and !(response.body.value == .Blob and response.body.value.Blob.needsToReadFile())) { - return this.returnEmptyResponse(global, response); - } - - return this.beginTransform(global, response); - } - - pub const HTMLRewriterLoader = struct { - rewriter: *LOLHTML.HTMLRewriter, - finalized: bool = false, - context: LOLHTMLContext, - chunk_size: usize = 0, - failed: bool = false, - output: JSC.WebCore.Sink, - signal: JSC.WebCore.Signal = .{}, - backpressure: std.fifo.LinearFifo(u8, .Dynamic) = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator), - - pub fn finalize(this: *HTMLRewriterLoader) void { - if (this.finalized) return; - this.rewriter.deinit(); - this.backpressure.deinit(); - this.backpressure = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator); - this.finalized = true; - } - - pub fn fail(this: *HTMLRewriterLoader, err: JSC.Node.Syscall.Error) void { - this.signal.close(err); - this.output.end(err); - this.failed = true; - this.finalize(); - } - - pub fn connect(this: *HTMLRewriterLoader, signal: JSC.WebCore.Signal) void { - this.signal = signal; - } - - pub fn writeToDestination(this: *HTMLRewriterLoader, bytes: []const u8) void { - if (this.backpressure.count > 0) { - this.backpressure.write(bytes) catch { - this.fail(JSC.Node.Syscall.Error.oom); - this.finalize(); - }; - return; - } - - const write_result = this.output.write(.{ .temporary = bun.ByteList.init(bytes) }); - - switch (write_result) { - .err => |err| { - this.fail(err); - }, - .owned_and_done, .temporary_and_done, .into_array_and_done => { - this.done(); - }, - .pending => |pending| { - pending.applyBackpressure(bun.default_allocator, &this.output, pending, bytes); - }, - .into_array, .owned, .temporary => { - this.signal.ready(if (this.chunk_size > 0) this.chunk_size else null, null); - }, - } - } - - pub fn done( - this: *HTMLRewriterLoader, - ) void { - this.output.end(null); - this.signal.close(null); - this.finalize(); - } - - pub fn setup( - this: *HTMLRewriterLoader, - builder: *LOLHTML.HTMLRewriter.Builder, - context: LOLHTMLContext, - size_hint: ?usize, - output: JSC.WebCore.Sink, - ) ?[]const u8 { - for (context.document_handlers.items) |doc| { - doc.ctx = this; - } - for (context.element_handlers.items) |doc| { - doc.ctx = this; - } - - const chunk_size = @maximum(size_hint orelse 16384, 1024); - this.rewriter = builder.build( - .UTF8, - .{ - .preallocated_parsing_buffer_size = chunk_size, - .max_allowed_memory_usage = std.math.maxInt(u32), - }, - false, - HTMLRewriterLoader, - this, - HTMLRewriterLoader.writeToDestination, - HTMLRewriterLoader.done, - ) catch { - output.end(); - return LOLHTML.HTMLString.lastError().slice(); - }; - - this.chunk_size = chunk_size; - this.context = context; - this.output = output; - - return null; - } - - pub fn sink(this: *HTMLRewriterLoader) JSC.WebCore.Sink { - return JSC.WebCore.Sink.init(this); - } - - fn writeBytes(this: *HTMLRewriterLoader, bytes: bun.ByteList, comptime deinit_: bool) ?JSC.Node.Syscall.Error { - this.rewriter.write(bytes.slice()) catch { - return JSC.Node.Syscall.Error{ - .errno = 1, - // TODO: make this a union - .path = bun.default_allocator.dupe(u8, LOLHTML.HTMLString.lastError().slice()) catch unreachable, - }; - }; - if (comptime deinit_) bytes.listManaged(bun.default_allocator).deinit(); - return null; - } - - pub fn write(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable { - switch (data) { - .owned => |bytes| { - if (this.writeBytes(bytes, true)) |err| { - return .{ .err = err }; - } - return .{ .owned = bytes.len }; - }, - .owned_and_done => |bytes| { - if (this.writeBytes(bytes, true)) |err| { - return .{ .err = err }; - } - return .{ .owned_and_done = bytes.len }; - }, - .temporary_and_done => |bytes| { - if (this.writeBytes(bytes, false)) |err| { - return .{ .err = err }; - } - return .{ .temporary_and_done = bytes.len }; - }, - .temporary => |bytes| { - if (this.writeBytes(bytes, false)) |err| { - return .{ .err = err }; - } - return .{ .temporary = bytes.len }; - }, - else => unreachable, - } - } - - pub fn writeUTF16(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable { - return JSC.WebCore.Sink.UTF8Fallback.writeUTF16(HTMLRewriterLoader, this, data, write); - } - - pub fn writeLatin1(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable { - return JSC.WebCore.Sink.UTF8Fallback.writeLatin1(HTMLRewriterLoader, this, data, write); - } - }; - - pub const BufferOutputSink = struct { - global: *JSGlobalObject, - bytes: bun.MutableString, - rewriter: *LOLHTML.HTMLRewriter, - context: LOLHTMLContext, - response: *Response, - input: JSC.WebCore.Blob = undefined, - pub fn init(context: LOLHTMLContext, global: *JSGlobalObject, original: *Response, builder: *LOLHTML.HTMLRewriter.Builder) JSValue { - var result = bun.default_allocator.create(Response) catch unreachable; - var sink = bun.default_allocator.create(BufferOutputSink) catch unreachable; - sink.* = BufferOutputSink{ - .global = global, - .bytes = bun.MutableString.initEmpty(bun.default_allocator), - .rewriter = undefined, - .context = context, - .response = result, - }; - - for (sink.context.document_handlers.items) |doc| { - doc.ctx = sink; - } - for (sink.context.element_handlers.items) |doc| { - doc.ctx = sink; - } - - sink.rewriter = builder.build( - .UTF8, - .{ - .preallocated_parsing_buffer_size = @maximum(original.body.len(), 1024), - .max_allowed_memory_usage = std.math.maxInt(u32), - }, - false, - BufferOutputSink, - sink, - BufferOutputSink.write, - BufferOutputSink.done, - ) catch { - sink.deinit(); - bun.default_allocator.destroy(result); - - return throwLOLHTMLError(global); - }; - - result.* = Response{ - .allocator = bun.default_allocator, - .body = .{ - .init = .{ - .status_code = 200, - }, - .value = .{ - .Locked = .{ - .global = global, - .task = sink, - }, - }, - }, - }; - - result.body.init.headers = original.body.init.headers; - result.body.init.method = original.body.init.method; - result.body.init.status_code = original.body.init.status_code; - - result.url = bun.default_allocator.dupe(u8, original.url) catch unreachable; - result.status_text = bun.default_allocator.dupe(u8, original.status_text) catch unreachable; - - var input: JSC.WebCore.Blob = original.body.value.use(); - - const is_pending = input.needsToReadFile(); - defer if (!is_pending) input.detach(); - - if (is_pending) { - input.doReadFileInternal(*BufferOutputSink, sink, onFinishedLoading, global); - } else if (sink.runOutputSink(input.sharedView(), false, false)) |error_value| { - return error_value; - } - - // Hold off on cloning until we're actually done. - - return JSC.JSValue.fromRef( - Response.makeMaybePooled(sink.global.ref(), sink.response), - ); - } - - pub fn onFinishedLoading(sink: *BufferOutputSink, bytes: JSC.WebCore.Blob.Store.ReadFile.ResultType) void { - switch (bytes) { - .err => |err| { - if (sink.response.body.value == .Locked and @ptrToInt(sink.response.body.value.Locked.task) == @ptrToInt(sink) and - sink.response.body.value.Locked.promise == null) - { - sink.response.body.value = .{ .Empty = .{} }; - // is there a pending promise? - // we will need to reject it - } else if (sink.response.body.value == .Locked and @ptrToInt(sink.response.body.value.Locked.task) == @ptrToInt(sink) and - sink.response.body.value.Locked.promise != null) - { - sink.response.body.value.Locked.callback = null; - sink.response.body.value.Locked.task = null; - } - - sink.response.body.value.toErrorInstance(err.toErrorInstance(sink.global), sink.global); - sink.rewriter.end() catch {}; - sink.deinit(); - return; - }, - .result => |data| { - _ = sink.runOutputSink(data.buf, true, data.is_temporary); - }, - } - } - - pub fn runOutputSink( - sink: *BufferOutputSink, - bytes: []const u8, - is_async: bool, - free_bytes_on_end: bool, - ) ?JSValue { - defer if (free_bytes_on_end) - bun.default_allocator.free(bun.constStrToU8(bytes)); - - sink.bytes.growBy(bytes.len) catch unreachable; - var global = sink.global; - var response = sink.response; - - sink.rewriter.write(bytes) catch { - sink.deinit(); - bun.default_allocator.destroy(sink); - - if (is_async) { - response.body.value.toErrorInstance(throwLOLHTMLError(global), global); - - return null; - } else { - return throwLOLHTMLError(global); - } - }; - - sink.rewriter.end() catch { - if (!is_async) response.finalize(); - sink.response = undefined; - sink.deinit(); - - if (is_async) { - response.body.value.toErrorInstance(throwLOLHTMLError(global), global); - return null; - } else { - return throwLOLHTMLError(global); - } - }; - - return null; - } - - pub const Sync = enum { suspended, pending, done }; - - pub fn done(this: *BufferOutputSink) void { - var prev_value = this.response.body.value; - var bytes = this.bytes.toOwnedSliceLeaky(); - this.response.body.value = .{ - .Blob = JSC.WebCore.Blob.init(bytes, this.bytes.allocator, this.global), - }; - prev_value.resolve( - &this.response.body.value, - this.global, - ); - } - - pub fn write(this: *BufferOutputSink, bytes: []const u8) void { - this.bytes.append(bytes) catch unreachable; - } - - pub fn deinit(this: *BufferOutputSink) void { - this.bytes.deinit(); - - this.context.deinit(bun.default_allocator); - } - }; - - // pub const StreamOutputSink = struct { - // global: *JSGlobalObject, - // rewriter: *LOLHTML.HTMLRewriter, - // context: LOLHTMLContext, - // response: *Response, - // input: JSC.WebCore.Blob = undefined, - // pub fn init(context: LOLHTMLContext, global: *JSGlobalObject, original: *Response, builder: *LOLHTML.HTMLRewriter.Builder) JSValue { - // var result = bun.default_allocator.create(Response) catch unreachable; - // var sink = bun.default_allocator.create(StreamOutputSink) catch unreachable; - // sink.* = StreamOutputSink{ - // .global = global, - // .rewriter = undefined, - // .context = context, - // .response = result, - // }; - - // for (sink.context.document_handlers.items) |doc| { - // doc.ctx = sink; - // } - // for (sink.context.element_handlers.items) |doc| { - // doc.ctx = sink; - // } - - // sink.rewriter = builder.build( - // .UTF8, - // .{ - // .preallocated_parsing_buffer_size = @maximum(original.body.len(), 1024), - // .max_allowed_memory_usage = std.math.maxInt(u32), - // }, - // false, - // StreamOutputSink, - // sink, - // StreamOutputSink.write, - // StreamOutputSink.done, - // ) catch { - // sink.deinit(); - // bun.default_allocator.destroy(result); - - // return throwLOLHTMLError(global); - // }; - - // result.* = Response{ - // .allocator = bun.default_allocator, - // .body = .{ - // .init = .{ - // .status_code = 200, - // }, - // .value = .{ - // .Locked = .{ - // .global = global, - // .task = sink, - // }, - // }, - // }, - // }; - - // result.body.init.headers = original.body.init.headers; - // result.body.init.method = original.body.init.method; - // result.body.init.status_code = original.body.init.status_code; - - // result.url = bun.default_allocator.dupe(u8, original.url) catch unreachable; - // result.status_text = bun.default_allocator.dupe(u8, original.status_text) catch unreachable; - - // var input: JSC.WebCore.Blob = original.body.value.use(); - - // const is_pending = input.needsToReadFile(); - // defer if (!is_pending) input.detach(); - - // if (is_pending) { - // input.doReadFileInternal(*StreamOutputSink, sink, onFinishedLoading, global); - // } else if (sink.runOutputSink(input.sharedView(), false, false)) |error_value| { - // return error_value; - // } - - // // Hold off on cloning until we're actually done. - - // return JSC.JSValue.fromRef( - // Response.makeMaybePooled(sink.global.ref(), sink.response), - // ); - // } - - // pub fn runOutputSink( - // sink: *StreamOutputSink, - // bytes: []const u8, - // is_async: bool, - // free_bytes_on_end: bool, - // ) ?JSValue { - // defer if (free_bytes_on_end) - // bun.default_allocator.free(bun.constStrToU8(bytes)); - - // return null; - // } - - // pub const Sync = enum { suspended, pending, done }; - - // pub fn done(this: *StreamOutputSink) void { - // var prev_value = this.response.body.value; - // var bytes = this.bytes.toOwnedSliceLeaky(); - // this.response.body.value = .{ - // .Blob = JSC.WebCore.Blob.init(bytes, this.bytes.allocator, this.global), - // }; - // prev_value.resolve( - // &this.response.body.value, - // this.global, - // ); - // } - - // pub fn write(this: *StreamOutputSink, bytes: []const u8) void { - // this.bytes.append(bytes) catch unreachable; - // } - - // pub fn deinit(this: *StreamOutputSink) void { - // this.bytes.deinit(); - - // this.context.deinit(bun.default_allocator); - // } - // }; -}; - -const DocumentHandler = struct { - onDocTypeCallback: ?JSValue = null, - onCommentCallback: ?JSValue = null, - onTextCallback: ?JSValue = null, - onEndCallback: ?JSValue = null, - thisObject: JSValue, - global: *JSGlobalObject, - ctx: ?*HTMLRewriter.BufferOutputSink = null, - - pub const onDocType = HandlerCallback( - DocumentHandler, - DocType, - LOLHTML.DocType, - "doctype", - "onDocTypeCallback", - ); - pub const onComment = HandlerCallback( - DocumentHandler, - Comment, - LOLHTML.Comment, - "comment", - "onCommentCallback", - ); - pub const onText = HandlerCallback( - DocumentHandler, - TextChunk, - LOLHTML.TextChunk, - "text_chunk", - "onTextCallback", - ); - pub const onEnd = HandlerCallback( - DocumentHandler, - DocEnd, - LOLHTML.DocEnd, - "doc_end", - "onEndCallback", - ); - - pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) DocumentHandler { - var handler = DocumentHandler{ - .thisObject = thisObject, - .global = global, - }; - - switch (thisObject.jsType()) { - .Object, .ProxyObject, .Cell, .FinalObject => {}, - else => |kind| { - JSC.throwInvalidArguments( - "Expected object but received {s}", - .{std.mem.span(@tagName(kind))}, - global.ref(), - exception, - ); - return undefined; - }, - } - - if (thisObject.get(global, "doctype")) |val| { - if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) { - JSC.throwInvalidArguments("doctype must be a function", .{}, global.ref(), exception); - return undefined; - } - JSC.C.JSValueProtect(global.ref(), val.asObjectRef()); - handler.onDocTypeCallback = val; - } - - if (thisObject.get(global, "comments")) |val| { - if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) { - JSC.throwInvalidArguments("comments must be a function", .{}, global.ref(), exception); - return undefined; - } - JSC.C.JSValueProtect(global.ref(), val.asObjectRef()); - handler.onCommentCallback = val; - } - - if (thisObject.get(global, "text")) |val| { - if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) { - JSC.throwInvalidArguments("text must be a function", .{}, global.ref(), exception); - return undefined; - } - JSC.C.JSValueProtect(global.ref(), val.asObjectRef()); - handler.onTextCallback = val; - } - - if (thisObject.get(global, "end")) |val| { - if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) { - JSC.throwInvalidArguments("end must be a function", .{}, global.ref(), exception); - return undefined; - } - JSC.C.JSValueProtect(global.ref(), val.asObjectRef()); - handler.onEndCallback = val; - } - - JSC.C.JSValueProtect(global.ref(), thisObject.asObjectRef()); - return handler; - } - - pub fn deinit(this: *DocumentHandler) void { - if (this.onDocTypeCallback) |cb| { - JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef()); - this.onDocTypeCallback = null; - } - - if (this.onCommentCallback) |cb| { - JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef()); - this.onCommentCallback = null; - } - - if (this.onTextCallback) |cb| { - JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef()); - this.onTextCallback = null; - } - - if (this.onEndCallback) |cb| { - JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef()); - this.onEndCallback = null; - } - - JSC.C.JSValueUnprotect(this.global.ref(), this.thisObject.asObjectRef()); - } -}; - -fn HandlerCallback( - comptime HandlerType: type, - comptime ZigType: type, - comptime LOLHTMLType: type, - comptime field_name: string, - comptime callback_name: string, -) (fn (*HandlerType, *LOLHTMLType) bool) { - return struct { - pub fn callback(this: *HandlerType, value: *LOLHTMLType) bool { - if (comptime JSC.is_bindgen) - unreachable; - var zig_element = bun.default_allocator.create(ZigType) catch unreachable; - @field(zig_element, field_name) = value; - // At the end of this scope, the value is no longer valid - var args = [1]JSC.C.JSObjectRef{ - ZigType.Class.make(this.global.ref(), zig_element), - }; - var result = JSC.C.JSObjectCallAsFunctionReturnValue( - this.global.ref(), - @field(this, callback_name).?.asObjectRef(), - if (comptime @hasField(HandlerType, "thisObject")) - @field(this, "thisObject").asObjectRef() - else - null, - 1, - &args, - ); - var promise_: ?*JSC.JSInternalPromise = null; - while (!result.isUndefinedOrNull()) { - if (result.isError() or result.isAggregateError(this.global)) { - @field(zig_element, field_name) = null; - return true; - } - - var promise = promise_ orelse JSC.JSInternalPromise.resolvedPromise(this.global, result); - promise_ = promise; - JavaScript.VirtualMachine.vm.event_loop.waitForPromise(promise); - - switch (promise.status(this.global.vm())) { - JSC.JSPromise.Status.Pending => unreachable, - JSC.JSPromise.Status.Rejected => { - JavaScript.VirtualMachine.vm.defaultErrorHandler(promise.result(this.global.vm()), null); - @field(zig_element, field_name) = null; - return false; - }, - JSC.JSPromise.Status.Fulfilled => { - result = promise.result(this.global.vm()); - break; - }, - } - - break; - } - @field(zig_element, field_name) = null; - return false; - } - }.callback; -} - -const ElementHandler = struct { - onElementCallback: ?JSValue = null, - onCommentCallback: ?JSValue = null, - onTextCallback: ?JSValue = null, - thisObject: JSValue, - global: *JSGlobalObject, - ctx: ?*HTMLRewriter.BufferOutputSink = null, - - pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) ElementHandler { - var handler = ElementHandler{ - .thisObject = thisObject, - .global = global, - }; - - switch (thisObject.jsType()) { - .Object, .ProxyObject, .Cell, .FinalObject => {}, - else => |kind| { - JSC.throwInvalidArguments( - "Expected object but received {s}", - .{std.mem.span(@tagName(kind))}, - global.ref(), - exception, - ); - return undefined; - }, - } - - if (thisObject.get(global, "element")) |val| { - if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) { - JSC.throwInvalidArguments("element must be a function", .{}, global.ref(), exception); - return undefined; - } - JSC.C.JSValueProtect(global.ref(), val.asObjectRef()); - handler.onElementCallback = val; - } - - if (thisObject.get(global, "comments")) |val| { - if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) { - JSC.throwInvalidArguments("comments must be a function", .{}, global.ref(), exception); - return undefined; - } - JSC.C.JSValueProtect(global.ref(), val.asObjectRef()); - handler.onCommentCallback = val; - } - - if (thisObject.get(global, "text")) |val| { - if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) { - JSC.throwInvalidArguments("text must be a function", .{}, global.ref(), exception); - return undefined; - } - JSC.C.JSValueProtect(global.ref(), val.asObjectRef()); - handler.onTextCallback = val; - } - - JSC.C.JSValueProtect(global.ref(), thisObject.asObjectRef()); - return handler; - } - - pub fn deinit(this: *ElementHandler) void { - if (this.onElementCallback) |cb| { - JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef()); - this.onElementCallback = null; - } - - if (this.onCommentCallback) |cb| { - JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef()); - this.onCommentCallback = null; - } - - if (this.onTextCallback) |cb| { - JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef()); - this.onTextCallback = null; - } - - JSC.C.JSValueUnprotect(this.global.ref(), this.thisObject.asObjectRef()); - } - - pub fn onElement(this: *ElementHandler, value: *LOLHTML.Element) bool { - return HandlerCallback( - ElementHandler, - Element, - LOLHTML.Element, - "element", - "onElementCallback", - )(this, value); - } - - pub const onComment = HandlerCallback( - ElementHandler, - Comment, - LOLHTML.Comment, - "comment", - "onCommentCallback", - ); - - pub const onText = HandlerCallback( - ElementHandler, - TextChunk, - LOLHTML.TextChunk, - "text_chunk", - "onTextCallback", - ); -}; - -pub const ContentOptions = struct { - html: bool = false, -}; - -const getterWrap = JSC.getterWrap; -const setterWrap = JSC.setterWrap; -const wrap = JSC.wrapAsync; - -pub fn free_html_writer_string(_: ?*anyopaque, ptr: ?*anyopaque, len: usize) callconv(.C) void { - var str = LOLHTML.HTMLString{ .ptr = bun.cast([*]const u8, ptr.?), .len = len }; - str.deinit(); -} - -fn throwLOLHTMLError(global: *JSGlobalObject) JSValue { - var err = LOLHTML.HTMLString.lastError(); - return ZigString.init(err.slice()).toErrorInstance(global); -} - -fn htmlStringValue(input: LOLHTML.HTMLString, globalObject: *JSGlobalObject) JSValue { - var str = ZigString.init( - input.slice(), - ); - str.detectEncoding(); - - return str.toExternalValueWithCallback( - globalObject, - free_html_writer_string, - ); -} - -pub const TextChunk = struct { - text_chunk: ?*LOLHTML.TextChunk = null, - - pub const Class = NewClass( - TextChunk, - .{ .name = "TextChunk" }, - .{ - .before = .{ - .rfn = wrap(TextChunk, "before"), - }, - .after = .{ - .rfn = wrap(TextChunk, "after"), - }, - - .replace = .{ - .rfn = wrap(TextChunk, "replace"), - }, - - .remove = .{ - .rfn = wrap(TextChunk, "remove"), - }, - .finalize = finalize, - }, - .{ - .removed = .{ - .get = getterWrap(TextChunk, "removed"), - }, - .text = .{ - .get = getterWrap(TextChunk, "getText"), - }, - }, - ); - - fn contentHandler(this: *TextChunk, comptime Callback: (fn (*LOLHTML.TextChunk, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - if (this.text_chunk == null) - return JSC.JSValue.jsUndefined(); - var content_slice = content.toSlice(bun.default_allocator); - defer content_slice.deinit(); - - Callback( - this.text_chunk.?, - content_slice.slice(), - contentOptions != null and contentOptions.?.html, - ) catch return throwLOLHTMLError(globalObject); - - return JSValue.fromRef(thisObject); - } - - pub fn before( - this: *TextChunk, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.TextChunk.before, thisObject, globalObject, content, contentOptions); - } - - pub fn after( - this: *TextChunk, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.TextChunk.after, thisObject, globalObject, content, contentOptions); - } - - pub fn replace( - this: *TextChunk, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.TextChunk.replace, thisObject, globalObject, content, contentOptions); - } - - pub fn remove(this: *TextChunk, thisObject: js.JSObjectRef) JSValue { - if (this.text_chunk == null) - return JSC.JSValue.jsUndefined(); - this.text_chunk.?.remove(); - return JSValue.fromRef(thisObject); - } - - pub fn getText(this: *TextChunk, global: *JSGlobalObject) JSValue { - if (this.text_chunk == null) - return JSC.JSValue.jsUndefined(); - return ZigString.init(this.text_chunk.?.getContent().slice()).withEncoding().toValue(global); - } - - pub fn removed(this: *TextChunk, _: *JSGlobalObject) JSValue { - return JSC.JSValue.jsBoolean(this.text_chunk.?.isRemoved()); - } - - pub fn finalize(this: *TextChunk) void { - this.text_chunk = null; - bun.default_allocator.destroy(this); - } -}; - -pub const DocType = struct { - doctype: ?*LOLHTML.DocType = null, - - pub fn finalize(this: *DocType) void { - this.doctype = null; - bun.default_allocator.destroy(this); - } - - pub const Class = NewClass( - DocType, - .{ - .name = "DocType", - }, - .{ - .finalize = finalize, - }, - .{ - .name = .{ - .get = getterWrap(DocType, "name"), - }, - .systemId = .{ - .get = getterWrap(DocType, "systemId"), - }, - - .publicId = .{ - .get = getterWrap(DocType, "publicId"), - }, - }, - ); - - /// The doctype name. - pub fn name(this: *DocType, global: *JSGlobalObject) JSValue { - if (this.doctype == null) - return JSC.JSValue.jsUndefined(); - const str = this.doctype.?.getName().slice(); - if (str.len == 0) - return JSValue.jsNull(); - return ZigString.init(str).toValue(global); - } - - pub fn systemId(this: *DocType, global: *JSGlobalObject) JSValue { - if (this.doctype == null) - return JSC.JSValue.jsUndefined(); - - const str = this.doctype.?.getSystemId().slice(); - if (str.len == 0) - return JSValue.jsNull(); - return ZigString.init(str).toValue(global); - } - - pub fn publicId(this: *DocType, global: *JSGlobalObject) JSValue { - if (this.doctype == null) - return JSC.JSValue.jsUndefined(); - - const str = this.doctype.?.getPublicId().slice(); - if (str.len == 0) - return JSValue.jsNull(); - return ZigString.init(str).toValue(global); - } -}; - -pub const DocEnd = struct { - doc_end: ?*LOLHTML.DocEnd, - - pub fn finalize(this: *DocEnd) void { - this.doc_end = null; - bun.default_allocator.destroy(this); - } - - pub const Class = NewClass( - DocEnd, - .{ .name = "DocEnd" }, - .{ - .finalize = finalize, - .append = .{ - .rfn = wrap(DocEnd, "append"), - }, - }, - .{}, - ); - - fn contentHandler(this: *DocEnd, comptime Callback: (fn (*LOLHTML.DocEnd, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - if (this.doc_end == null) - return JSValue.jsNull(); - - var content_slice = content.toSlice(bun.default_allocator); - defer content_slice.deinit(); - - Callback( - this.doc_end.?, - content_slice.slice(), - contentOptions != null and contentOptions.?.html, - ) catch return throwLOLHTMLError(globalObject); - - return JSValue.fromRef(thisObject); - } - - pub fn append( - this: *DocEnd, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.DocEnd.append, thisObject, globalObject, content, contentOptions); - } -}; - -pub const Comment = struct { - comment: ?*LOLHTML.Comment = null, - - pub fn finalize(this: *Comment) void { - this.comment = null; - bun.default_allocator.destroy(this); - } - - pub const Class = NewClass( - Comment, - .{ .name = "Comment" }, - .{ - .before = .{ - .rfn = wrap(Comment, "before"), - }, - .after = .{ - .rfn = wrap(Comment, "after"), - }, - - .replace = .{ - .rfn = wrap(Comment, "replace"), - }, - - .remove = .{ - .rfn = wrap(Comment, "remove"), - }, - .finalize = finalize, - }, - .{ - .removed = .{ - .get = getterWrap(Comment, "removed"), - }, - .text = .{ - .get = getterWrap(Comment, "getText"), - .set = setterWrap(Comment, "setText"), - }, - }, - ); - - fn contentHandler(this: *Comment, comptime Callback: (fn (*LOLHTML.Comment, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - if (this.comment == null) - return JSValue.jsNull(); - var content_slice = content.toSlice(bun.default_allocator); - defer content_slice.deinit(); - - Callback( - this.comment.?, - content_slice.slice(), - contentOptions != null and contentOptions.?.html, - ) catch return throwLOLHTMLError(globalObject); - - return JSValue.fromRef(thisObject); - } - - pub fn before( - this: *Comment, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.Comment.before, thisObject, globalObject, content, contentOptions); - } - - pub fn after( - this: *Comment, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.Comment.after, thisObject, globalObject, content, contentOptions); - } - - pub fn replace( - this: *Comment, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.Comment.replace, thisObject, globalObject, content, contentOptions); - } - - pub fn remove(this: *Comment, thisObject: js.JSObjectRef) JSValue { - if (this.comment == null) - return JSValue.jsNull(); - this.comment.?.remove(); - return JSValue.fromRef(thisObject); - } - - pub fn getText(this: *Comment, global: *JSGlobalObject) JSValue { - if (this.comment == null) - return JSValue.jsNull(); - return ZigString.init(this.comment.?.getText().slice()).withEncoding().toValue(global); - } - - pub fn setText( - this: *Comment, - value: JSValue, - exception: JSC.C.ExceptionRef, - global: *JSGlobalObject, - ) void { - if (this.comment == null) - return; - var text = value.toSlice(global, bun.default_allocator); - defer text.deinit(); - this.comment.?.setText(text.slice()) catch { - exception.* = throwLOLHTMLError(global).asObjectRef(); - }; - } - - pub fn removed(this: *Comment, _: *JSGlobalObject) JSValue { - if (this.comment == null) - return JSC.JSValue.jsUndefined(); - return JSC.JSValue.jsBoolean(this.comment.?.isRemoved()); - } -}; - -pub const EndTag = struct { - end_tag: ?*LOLHTML.EndTag, - - pub fn finalize(this: *EndTag) void { - this.end_tag = null; - bun.default_allocator.destroy(this); - } - - pub const Handler = struct { - callback: ?JSC.JSValue, - global: *JSGlobalObject, - - pub const onEndTag = HandlerCallback( - Handler, - EndTag, - LOLHTML.EndTag, - "end_tag", - "callback", - ); - - pub const onEndTagHandler = LOLHTML.DirectiveHandler(LOLHTML.EndTag, Handler, onEndTag); - }; - - pub const Class = NewClass( - EndTag, - .{ .name = "EndTag" }, - .{ - .before = .{ - .rfn = wrap(EndTag, "before"), - }, - .after = .{ - .rfn = wrap(EndTag, "after"), - }, - - .remove = .{ - .rfn = wrap(EndTag, "remove"), - }, - .finalize = finalize, - }, - .{ - .name = .{ - .get = getterWrap(EndTag, "getName"), - .set = setterWrap(EndTag, "setName"), - }, - }, - ); - - fn contentHandler(this: *EndTag, comptime Callback: (fn (*LOLHTML.EndTag, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - if (this.end_tag == null) - return JSValue.jsNull(); - - var content_slice = content.toSlice(bun.default_allocator); - defer content_slice.deinit(); - - Callback( - this.end_tag.?, - content_slice.slice(), - contentOptions != null and contentOptions.?.html, - ) catch return throwLOLHTMLError(globalObject); - - return JSValue.fromRef(thisObject); - } - - pub fn before( - this: *EndTag, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.EndTag.before, thisObject, globalObject, content, contentOptions); - } - - pub fn after( - this: *EndTag, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.EndTag.after, thisObject, globalObject, content, contentOptions); - } - - pub fn replace( - this: *EndTag, - thisObject: js.JSObjectRef, - globalObject: *JSGlobalObject, - content: ZigString, - contentOptions: ?ContentOptions, - ) JSValue { - return this.contentHandler(LOLHTML.EndTag.replace, thisObject, globalObject, content, contentOptions); - } - - pub fn remove(this: *EndTag, thisObject: js.JSObjectRef) JSValue { - if (this.end_tag == null) - return JSC.JSValue.jsUndefined(); - - this.end_tag.?.remove(); - return JSValue.fromRef(thisObject); - } - - pub fn getName(this: *EndTag, global: *JSGlobalObject) JSValue { - if (this.end_tag == null) - return JSC.JSValue.jsUndefined(); - - return ZigString.init(this.end_tag.?.getName().slice()).withEncoding().toValue(global); - } - - pub fn setName( - this: *EndTag, - value: JSValue, - exception: JSC.C.ExceptionRef, - global: *JSGlobalObject, - ) void { - if (this.end_tag == null) - return; - var text = value.toSlice(global, bun.default_allocator); - defer text.deinit(); - this.end_tag.?.setName(text.slice()) catch { - exception.* = throwLOLHTMLError(global).asObjectRef(); - }; - } -}; - -pub const AttributeIterator = struct { - iterator: ?*LOLHTML.Attribute.Iterator = null, - - const attribute_iterator_path: string = "file:///bun-vfs/lolhtml/AttributeIterator.js"; - const attribute_iterator_code: string = - \\"use strict"; - \\ - \\class AttributeIterator { - \\ constructor(internal) { - \\ this.#iterator = internal; - \\ } - \\ - \\ #iterator; - \\ - \\ [Symbol.iterator]() { - \\ return this; - \\ } - \\ - \\ next() { - \\ if (this.#iterator === null) - \\ return {done: true}; - \\ var value = this.#iterator.next(); - \\ if (!value) { - \\ this.#iterator = null; - \\ return {done: true}; - \\ } - \\ return {done: false, value: value}; - \\ } - \\} - \\ - \\return new AttributeIterator(internal1); - ; - threadlocal var attribute_iterator_class: JSC.C.JSObjectRef = undefined; - threadlocal var attribute_iterator_loaded: bool = false; - - pub fn getAttributeIteratorJSClass(global: *JSGlobalObject) JSValue { - if (attribute_iterator_loaded) - return JSC.JSValue.fromRef(attribute_iterator_class); - attribute_iterator_loaded = true; - var exception_ptr: ?[*]JSC.JSValueRef = null; - var name = JSC.C.JSStringCreateStatic("AttributeIteratorGetter", "AttributeIteratorGetter".len); - var param_name = JSC.C.JSStringCreateStatic("internal1", "internal1".len); - var attribute_iterator_class_ = JSC.C.JSObjectMakeFunction( - global.ref(), - name, - 1, - &[_]JSC.C.JSStringRef{param_name}, - JSC.C.JSStringCreateStatic(attribute_iterator_code.ptr, attribute_iterator_code.len), - JSC.C.JSStringCreateStatic(attribute_iterator_path.ptr, attribute_iterator_path.len), - 0, - exception_ptr, - ); - JSC.C.JSValueProtect(global.ref(), attribute_iterator_class_); - attribute_iterator_class = attribute_iterator_class_; - return JSC.JSValue.fromRef(attribute_iterator_class); - } - - pub fn finalize(this: *AttributeIterator) void { - if (this.iterator) |iter| { - iter.deinit(); - this.iterator = null; - } - bun.default_allocator.destroy(this); - } - - pub const Class = NewClass( - AttributeIterator, - .{ .name = "AttributeIterator" }, - .{ - .next = .{ - .rfn = wrap(AttributeIterator, "next"), - }, - .finalize = finalize, - }, - .{}, - ); - - const value_ = ZigString.init("value"); - const done_ = ZigString.init("done"); - pub fn next( - this: *AttributeIterator, - globalObject: *JSGlobalObject, - ) JSValue { - if (this.iterator == null) { - return JSC.JSValue.jsNull(); - } - - var attribute = this.iterator.?.next() orelse { - this.iterator.?.deinit(); - this.iterator = null; - return JSC.JSValue.jsNull(); - }; - - // TODO: don't clone here - const value = attribute.value(); - const name = attribute.name(); - defer name.deinit(); - defer value.deinit(); - - var strs = [2]ZigString{ - ZigString.init(name.slice()), - ZigString.init(value.slice()), - }; - - var valid_strs: []ZigString = strs[0..2]; - - var array = JSC.JSValue.createStringArray( - globalObject, - valid_strs.ptr, - valid_strs.len, - true, - ); - - return array; - } -}; -pub const Element = struct { - element: ?*LOLHTML.Element = null, - - pub const Class = NewClass( - Element, - .{ .name = "Element" }, - .{ - .getAttribute = .{ - .rfn = wrap(Element, "getAttribute"), - }, - .hasAttribute = .{ - .rfn = wrap(Element, "hasAttribute"), - }, - .setAttribute = .{ - .rfn = wrap(Element, "setAttribute"), - }, - .removeAttribute = .{ - .rfn = wrap(Element, "removeAttribute"), - }, - .before = .{ - .rfn = wrap(Element, "before"), - }, - .after = .{ - .rfn = wrap(Element, "after"), - }, - .prepend = .{ - .rfn = wrap(Element, "prepend"), - }, - .append = .{ - .rfn = wrap(Element, "append"), - }, - .replace = .{ - .rfn = wrap(Element, "replace"), - }, - .setInnerContent = .{ - .rfn = wrap(Element, "setInnerContent"), - }, - .remove = .{ - .rfn = wrap(Element, "remove"), - }, - .removeAndKeepContent = .{ - .rfn = wrap(Element, "removeAndKeepContent"), - }, - .onEndTag = .{ - .rfn = wrap(Element, "onEndTag"), - }, - .finalize = finalize, - }, - .{ - .tagName = .{ - .get = getterWrap(Element, "getTagName"), - .set = setterWrap(Element, "setTagName"), - }, - .removed = .{ - .get = getterWrap(Element, "getRemoved"), - }, - .namespaceURI = .{ - .get = getterWrap(Element, "getNamespaceURI"), - }, - .attributes = .{ - .get = getterWrap(Element, "getAttributes"), - }, - }, - ); - - pub fn finalize(this: *Element) void { - this.element = null; - bun.default_allocator.destroy(this); - } - - pub fn onEndTag( - this: *Element, - globalObject: *JSGlobalObject, - function: JSValue, - thisObject: JSC.C.JSObjectRef, - ) JSValue { - if (this.element == null) - return JSValue.jsNull(); - if (function.isUndefinedOrNull() or !function.isCallable(globalObject.vm())) { - return ZigString.init("Expected a function").withEncoding().toValue(globalObject); - } - - var end_tag_handler = bun.default_allocator.create(EndTag.Handler) catch unreachable; - end_tag_handler.* = .{ .global = globalObject, .callback = function }; - - this.element.?.onEndTag(EndTag.Handler.onEndTagHandler, end_tag_handler) catch { - bun.default_allocator.destroy(end_tag_handler); - return throwLOLHTMLError(globalObject); - }; - - JSC.C.JSValueProtect(globalObject.ref(), function.asObjectRef()); - return JSValue.fromRef(thisObject); - } - - // // fn wrap(comptime name: string) - - /// Returns the value for a given attribute name: ZigString on the element, or null if it is not found. - pub fn getAttribute(this: *Element, globalObject: *JSGlobalObject, name: ZigString) JSValue { - if (this.element == null) - return JSValue.jsNull(); - - var slice = name.toSlice(bun.default_allocator); - defer slice.deinit(); - var attr = this.element.?.getAttribute(slice.slice()).slice(); - - if (attr.len == 0) - return JSC.JSValue.jsNull(); - - var str = ZigString.init( - attr, - ); - - return str.toExternalValueWithCallback( - globalObject, - free_html_writer_string, - ); - } - - /// Returns a boolean indicating whether an attribute exists on the element. - pub fn hasAttribute(this: *Element, global: *JSGlobalObject, name: ZigString) JSValue { - if (this.element == null) - return JSValue.jsBoolean(false); - - var slice = name.toSlice(bun.default_allocator); - defer slice.deinit(); - return JSValue.jsBoolean(this.element.?.hasAttribute(slice.slice()) catch return throwLOLHTMLError(global)); - } - - /// Sets an attribute to a provided value, creating the attribute if it does not exist. - pub fn setAttribute(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, name_: ZigString, value_: ZigString) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - var name_slice = name_.toSlice(bun.default_allocator); - defer name_slice.deinit(); - - var value_slice = value_.toSlice(bun.default_allocator); - defer value_slice.deinit(); - this.element.?.setAttribute(name_slice.slice(), value_slice.slice()) catch return throwLOLHTMLError(globalObject); - return JSValue.fromRef(thisObject); - } - - /// Removes the attribute. - pub fn removeAttribute(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, name: ZigString) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - var name_slice = name.toSlice(bun.default_allocator); - defer name_slice.deinit(); - - this.element.?.removeAttribute( - name_slice.slice(), - ) catch return throwLOLHTMLError(globalObject); - return JSValue.fromRef(thisObject); - } - - fn contentHandler(this: *Element, comptime Callback: (fn (*LOLHTML.Element, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - var content_slice = content.toSlice(bun.default_allocator); - defer content_slice.deinit(); - - Callback( - this.element.?, - content_slice.slice(), - contentOptions != null and contentOptions.?.html, - ) catch return throwLOLHTMLError(globalObject); - - return JSValue.fromRef(thisObject); - } - - /// Inserts content before the element. - pub fn before(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - return contentHandler( - this, - LOLHTML.Element.before, - thisObject, - globalObject, - content, - contentOptions, - ); - } - - /// Inserts content right after the element. - pub fn after(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - return contentHandler( - this, - LOLHTML.Element.after, - thisObject, - globalObject, - content, - contentOptions, - ); - } - - /// Inserts content right after the start tag of the element. - pub fn prepend(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - return contentHandler( - this, - LOLHTML.Element.prepend, - thisObject, - globalObject, - content, - contentOptions, - ); - } - - /// Inserts content right before the end tag of the element. - pub fn append(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - return contentHandler( - this, - LOLHTML.Element.append, - thisObject, - globalObject, - content, - contentOptions, - ); - } - - /// Removes the element and inserts content in place of it. - pub fn replace(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - return contentHandler( - this, - LOLHTML.Element.replace, - thisObject, - globalObject, - content, - contentOptions, - ); - } - - /// Replaces content of the element. - pub fn setInnerContent(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue { - return contentHandler( - this, - LOLHTML.Element.setInnerContent, - thisObject, - globalObject, - content, - contentOptions, - ); - } - - /// Removes the element with all its content. - pub fn remove(this: *Element, thisObject: js.JSObjectRef) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - this.element.?.remove(); - return JSValue.fromRef(thisObject); - } - - /// Removes the start tag and end tag of the element but keeps its inner content intact. - pub fn removeAndKeepContent(this: *Element, thisObject: js.JSObjectRef) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - this.element.?.removeAndKeepContent(); - return JSValue.fromRef(thisObject); - } - - pub fn getTagName(this: *Element, globalObject: *JSGlobalObject) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - return htmlStringValue(this.element.?.tagName(), globalObject); - } - - pub fn setTagName(this: *Element, value: JSValue, exception: JSC.C.ExceptionRef, global: *JSGlobalObject) void { - if (this.element == null) - return; - - var text = value.toSlice(global, bun.default_allocator); - defer text.deinit(); - - this.element.?.setTagName(text.slice()) catch { - exception.* = throwLOLHTMLError(global).asObjectRef(); - }; - } - - pub fn getRemoved(this: *Element, _: *JSGlobalObject) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - return JSC.JSValue.jsBoolean(this.element.?.isRemoved()); - } - - pub fn getNamespaceURI(this: *Element, globalObject: *JSGlobalObject) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - return ZigString.init(std.mem.span(this.element.?.namespaceURI())).toValue(globalObject); - } - - pub fn getAttributes(this: *Element, globalObject: *JSGlobalObject) JSValue { - if (this.element == null) - return JSValue.jsUndefined(); - - var iter = this.element.?.attributes() orelse return throwLOLHTMLError(globalObject); - var attr_iter = bun.default_allocator.create(AttributeIterator) catch unreachable; - attr_iter.* = .{ .iterator = iter }; - var attr = AttributeIterator.Class.make(globalObject.ref(), attr_iter); - JSC.C.JSValueProtect(globalObject.ref(), attr); - defer JSC.C.JSValueUnprotect(globalObject.ref(), attr); - return JSC.JSValue.fromRef( - JSC.C.JSObjectCallAsFunction( - globalObject.ref(), - AttributeIterator.getAttributeIteratorJSClass(globalObject).asObjectRef(), - null, - 1, - @ptrCast([*]JSC.C.JSObjectRef, &attr), - null, - ), - ); - } -}; diff --git a/src/javascript/jsc/api/libtcc1.a.macos-aarch64 b/src/javascript/jsc/api/libtcc1.a.macos-aarch64 Binary files differdeleted file mode 100644 index 60696b611..000000000 --- a/src/javascript/jsc/api/libtcc1.a.macos-aarch64 +++ /dev/null diff --git a/src/javascript/jsc/api/libtcc1.c b/src/javascript/jsc/api/libtcc1.c deleted file mode 100644 index 38750b825..000000000 --- a/src/javascript/jsc/api/libtcc1.c +++ /dev/null @@ -1,606 +0,0 @@ -/* TCC runtime library. - Parts of this code are (c) 2002 Fabrice Bellard - - Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc. - -This file is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -In addition to the permissions in the GNU General Public License, the -Free Software Foundation gives you unlimited permission to link the -compiled version of this file into combinations with other programs, -and to distribute those combinations without any restriction coming -from the use of this file. (The General Public License restrictions -do apply in other respects; for example, they cover modification of -the file, and distribution when not linked into a combine -executable.) - -This file is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#define W_TYPE_SIZE 32 -#define BITS_PER_UNIT 8 - -typedef int Wtype; -typedef unsigned int UWtype; -typedef unsigned int USItype; -typedef long long DWtype; -typedef unsigned long long UDWtype; - -struct DWstruct { - Wtype low, high; -}; - -typedef union -{ - struct DWstruct s; - DWtype ll; -} DWunion; - -typedef long double XFtype; -#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) -#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) - -/* the following deal with IEEE single-precision numbers */ -#define EXCESS 126 -#define SIGNBIT 0x80000000 -#define HIDDEN (1 << 23) -#define SIGN(fp) ((fp) & SIGNBIT) -#define EXP(fp) (((fp) >> 23) & 0xFF) -#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) -#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) - -/* the following deal with IEEE double-precision numbers */ -#define EXCESSD 1022 -#define HIDDEND (1 << 20) -#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) -#define SIGND(fp) ((fp.l.upper) & SIGNBIT) -#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \ - (fp.l.lower >> 22)) -#define HIDDEND_LL ((long long)1 << 52) -#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) -#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) - -/* the following deal with x86 long double-precision numbers */ -#define EXCESSLD 16382 -#define EXPLD(fp) (fp.l.upper & 0x7fff) -#define SIGNLD(fp) ((fp.l.upper) & 0x8000) - -/* only for x86 */ -union ldouble_long { - long double ld; - struct { - unsigned long long lower; - unsigned short upper; - } l; -}; - -union double_long { - double d; -#if 1 - struct { - unsigned int lower; - int upper; - } l; -#else - struct { - int upper; - unsigned int lower; - } l; -#endif - long long ll; -}; - -union float_long { - float f; - long l; -}; - -/* XXX: we don't support several builtin supports for now */ -#ifndef __x86_64__ - -/* XXX: use gcc/tcc intrinsic ? */ -#if defined(__i386__) -#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - __asm__ ("subl %5,%1\n\tsbbl %3,%0" \ - : "=r" ((USItype) (sh)), \ - "=&r" ((USItype) (sl)) \ - : "0" ((USItype) (ah)), \ - "g" ((USItype) (bh)), \ - "1" ((USItype) (al)), \ - "g" ((USItype) (bl))) -#define umul_ppmm(w1, w0, u, v) \ - __asm__ ("mull %3" \ - : "=a" ((USItype) (w0)), \ - "=d" ((USItype) (w1)) \ - : "%0" ((USItype) (u)), \ - "rm" ((USItype) (v))) -#define udiv_qrnnd(q, r, n1, n0, dv) \ - __asm__ ("divl %4" \ - : "=a" ((USItype) (q)), \ - "=d" ((USItype) (r)) \ - : "0" ((USItype) (n0)), \ - "1" ((USItype) (n1)), \ - "rm" ((USItype) (dv))) -#define count_leading_zeros(count, x) \ - do { \ - USItype __cbtmp; \ - __asm__ ("bsrl %1,%0" \ - : "=r" (__cbtmp) : "rm" ((USItype) (x))); \ - (count) = __cbtmp ^ 31; \ - } while (0) -#else -#error unsupported CPU type -#endif - -/* most of this code is taken from libgcc2.c from gcc */ - -static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) -{ - DWunion ww; - DWunion nn, dd; - DWunion rr; - UWtype d0, d1, n0, n1, n2; - UWtype q0, q1; - UWtype b, bm; - - nn.ll = n; - dd.ll = d; - - d0 = dd.s.low; - d1 = dd.s.high; - n0 = nn.s.low; - n1 = nn.s.high; - -#if !UDIV_NEEDS_NORMALIZATION - if (d1 == 0) - { - if (d0 > n1) - { - /* 0q = nn / 0D */ - - udiv_qrnnd (q0, n0, n1, n0, d0); - q1 = 0; - - /* Remainder in n0. */ - } - else - { - /* qq = NN / 0d */ - - if (d0 == 0) - d0 = 1 / d0; /* Divide intentionally by zero. */ - - udiv_qrnnd (q1, n1, 0, n1, d0); - udiv_qrnnd (q0, n0, n1, n0, d0); - - /* Remainder in n0. */ - } - - if (rp != 0) - { - rr.s.low = n0; - rr.s.high = 0; - *rp = rr.ll; - } - } - -#else /* UDIV_NEEDS_NORMALIZATION */ - - if (d1 == 0) - { - if (d0 > n1) - { - /* 0q = nn / 0D */ - - count_leading_zeros (bm, d0); - - if (bm != 0) - { - /* Normalize, i.e. make the most significant bit of the - denominator set. */ - - d0 = d0 << bm; - n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); - n0 = n0 << bm; - } - - udiv_qrnnd (q0, n0, n1, n0, d0); - q1 = 0; - - /* Remainder in n0 >> bm. */ - } - else - { - /* qq = NN / 0d */ - - if (d0 == 0) - d0 = 1 / d0; /* Divide intentionally by zero. */ - - count_leading_zeros (bm, d0); - - if (bm == 0) - { - /* From (n1 >= d0) /\ (the most significant bit of d0 is set), - conclude (the most significant bit of n1 is set) /\ (the - leading quotient digit q1 = 1). - - This special case is necessary, not an optimization. - (Shifts counts of W_TYPE_SIZE are undefined.) */ - - n1 -= d0; - q1 = 1; - } - else - { - /* Normalize. */ - - b = W_TYPE_SIZE - bm; - - d0 = d0 << bm; - n2 = n1 >> b; - n1 = (n1 << bm) | (n0 >> b); - n0 = n0 << bm; - - udiv_qrnnd (q1, n1, n2, n1, d0); - } - - /* n1 != d0... */ - - udiv_qrnnd (q0, n0, n1, n0, d0); - - /* Remainder in n0 >> bm. */ - } - - if (rp != 0) - { - rr.s.low = n0 >> bm; - rr.s.high = 0; - *rp = rr.ll; - } - } -#endif /* UDIV_NEEDS_NORMALIZATION */ - - else - { - if (d1 > n1) - { - /* 00 = nn / DD */ - - q0 = 0; - q1 = 0; - - /* Remainder in n1n0. */ - if (rp != 0) - { - rr.s.low = n0; - rr.s.high = n1; - *rp = rr.ll; - } - } - else - { - /* 0q = NN / dd */ - - count_leading_zeros (bm, d1); - if (bm == 0) - { - /* From (n1 >= d1) /\ (the most significant bit of d1 is set), - conclude (the most significant bit of n1 is set) /\ (the - quotient digit q0 = 0 or 1). - - This special case is necessary, not an optimization. */ - - /* The condition on the next line takes advantage of that - n1 >= d1 (true due to program flow). */ - if (n1 > d1 || n0 >= d0) - { - q0 = 1; - sub_ddmmss (n1, n0, n1, n0, d1, d0); - } - else - q0 = 0; - - q1 = 0; - - if (rp != 0) - { - rr.s.low = n0; - rr.s.high = n1; - *rp = rr.ll; - } - } - else - { - UWtype m1, m0; - /* Normalize. */ - - b = W_TYPE_SIZE - bm; - - d1 = (d1 << bm) | (d0 >> b); - d0 = d0 << bm; - n2 = n1 >> b; - n1 = (n1 << bm) | (n0 >> b); - n0 = n0 << bm; - - udiv_qrnnd (q0, n1, n2, n1, d1); - umul_ppmm (m1, m0, q0, d0); - - if (m1 > n1 || (m1 == n1 && m0 > n0)) - { - q0--; - sub_ddmmss (m1, m0, m1, m0, d1, d0); - } - - q1 = 0; - - /* Remainder in (n1n0 - m1m0) >> bm. */ - if (rp != 0) - { - sub_ddmmss (n1, n0, n1, n0, m1, m0); - rr.s.low = (n1 << b) | (n0 >> bm); - rr.s.high = n1 >> bm; - *rp = rr.ll; - } - } - } - } - - ww.s.low = q0; - ww.s.high = q1; - return ww.ll; -} - -#define __negdi2(a) (-(a)) - -long long __divdi3(long long u, long long v) -{ - int c = 0; - DWunion uu, vv; - DWtype w; - - uu.ll = u; - vv.ll = v; - - if (uu.s.high < 0) { - c = ~c; - uu.ll = __negdi2 (uu.ll); - } - if (vv.s.high < 0) { - c = ~c; - vv.ll = __negdi2 (vv.ll); - } - w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0); - if (c) - w = __negdi2 (w); - return w; -} - -long long __moddi3(long long u, long long v) -{ - int c = 0; - DWunion uu, vv; - DWtype w; - - uu.ll = u; - vv.ll = v; - - if (uu.s.high < 0) { - c = ~c; - uu.ll = __negdi2 (uu.ll); - } - if (vv.s.high < 0) - vv.ll = __negdi2 (vv.ll); - - __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w); - if (c) - w = __negdi2 (w); - return w; -} - -unsigned long long __udivdi3(unsigned long long u, unsigned long long v) -{ - return __udivmoddi4 (u, v, (UDWtype *) 0); -} - -unsigned long long __umoddi3(unsigned long long u, unsigned long long v) -{ - UDWtype w; - - __udivmoddi4 (u, v, &w); - return w; -} - -/* XXX: fix tcc's code generator to do this instead */ -long long __ashrdi3(long long a, int b) -{ -#ifdef __TINYC__ - DWunion u; - u.ll = a; - if (b >= 32) { - u.s.low = u.s.high >> (b - 32); - u.s.high = u.s.high >> 31; - } else if (b != 0) { - u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); - u.s.high = u.s.high >> b; - } - return u.ll; -#else - return a >> b; -#endif -} - -/* XXX: fix tcc's code generator to do this instead */ -unsigned long long __lshrdi3(unsigned long long a, int b) -{ -#ifdef __TINYC__ - DWunion u; - u.ll = a; - if (b >= 32) { - u.s.low = (unsigned)u.s.high >> (b - 32); - u.s.high = 0; - } else if (b != 0) { - u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); - u.s.high = (unsigned)u.s.high >> b; - } - return u.ll; -#else - return a >> b; -#endif -} - -/* XXX: fix tcc's code generator to do this instead */ -long long __ashldi3(long long a, int b) -{ -#ifdef __TINYC__ - DWunion u; - u.ll = a; - if (b >= 32) { - u.s.high = (unsigned)u.s.low << (b - 32); - u.s.low = 0; - } else if (b != 0) { - u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b)); - u.s.low = (unsigned)u.s.low << b; - } - return u.ll; -#else - return a << b; -#endif -} - -#if defined(__i386__) -/* FPU control word for rounding to nearest mode */ -unsigned short __tcc_fpu_control = 0x137f; -/* FPU control word for round to zero mode for int conversion */ -unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00; -#endif - -#endif /* !__x86_64__ */ - -/* XXX: fix tcc's code generator to do this instead */ -float __floatundisf(unsigned long long a) -{ - DWunion uu; - XFtype r; - - uu.ll = a; - if (uu.s.high >= 0) { - return (float)uu.ll; - } else { - r = (XFtype)uu.ll; - r += 18446744073709551616.0; - return (float)r; - } -} - -double __floatundidf(unsigned long long a) -{ - DWunion uu; - XFtype r; - - uu.ll = a; - if (uu.s.high >= 0) { - return (double)uu.ll; - } else { - r = (XFtype)uu.ll; - r += 18446744073709551616.0; - return (double)r; - } -} - -long double __floatundixf(unsigned long long a) -{ - DWunion uu; - XFtype r; - - uu.ll = a; - if (uu.s.high >= 0) { - return (long double)uu.ll; - } else { - r = (XFtype)uu.ll; - r += 18446744073709551616.0; - return (long double)r; - } -} - -unsigned long long __fixunssfdi (float a1) -{ - register union float_long fl1; - register int exp; - register unsigned long l; - - fl1.f = a1; - - if (fl1.l == 0) - return (0); - - exp = EXP (fl1.l) - EXCESS - 24; - - l = MANT(fl1.l); - if (exp >= 41) - return (unsigned long long)-1; - else if (exp >= 0) - return (unsigned long long)l << exp; - else if (exp >= -23) - return l >> -exp; - else - return 0; -} - -unsigned long long __fixunsdfdi (double a1) -{ - register union double_long dl1; - register int exp; - register unsigned long long l; - - dl1.d = a1; - - if (dl1.ll == 0) - return (0); - - exp = EXPD (dl1) - EXCESSD - 53; - - l = MANTD_LL(dl1); - - if (exp >= 12) - return (unsigned long long)-1; - else if (exp >= 0) - return l << exp; - else if (exp >= -52) - return l >> -exp; - else - return 0; -} - -unsigned long long __fixunsxfdi (long double a1) -{ - register union ldouble_long dl1; - register int exp; - register unsigned long long l; - - dl1.ld = a1; - - if (dl1.l.lower == 0 && dl1.l.upper == 0) - return (0); - - exp = EXPLD (dl1) - EXCESSLD - 64; - - l = dl1.l.lower; - - if (exp > 0) - return (unsigned long long)-1; - else if (exp >= -63) - return l >> -exp; - else - return 0; -} diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig deleted file mode 100644 index 8bf1ab7e0..000000000 --- a/src/javascript/jsc/api/router.zig +++ /dev/null @@ -1,541 +0,0 @@ -const std = @import("std"); -const Api = @import("../../../api/schema.zig").Api; -const FilesystemRouter = @import("../../../router.zig"); -const http = @import("../../../http.zig"); -const JavaScript = @import("../javascript.zig"); -const QueryStringMap = @import("../../../url.zig").QueryStringMap; -const CombinedScanner = @import("../../../url.zig").CombinedScanner; -const bun = @import("../../../global.zig"); -const string = bun.string; -const JSC = @import("../../../jsc.zig"); -const js = JSC.C; -const WebCore = @import("../webcore/response.zig"); -const Router = @This(); -const Bundler = @import("../../../bundler.zig"); -const VirtualMachine = JavaScript.VirtualMachine; -const ScriptSrcStream = std.io.FixedBufferStream([]u8); -const ZigString = JSC.ZigString; -const Fs = @import("../../../fs.zig"); -const Base = @import("../base.zig"); -const getAllocator = Base.getAllocator; -const JSObject = JSC.JSObject; -const JSError = Base.JSError; -const JSValue = JSC.JSValue; -const JSGlobalObject = JSC.JSGlobalObject; -const strings = @import("strings"); -const NewClass = Base.NewClass; -const To = Base.To; -const Request = WebCore.Request; -const d = Base.d; -const FetchEvent = WebCore.FetchEvent; -const URLPath = @import("../../../http/url_path.zig"); -const URL = @import("../../../url.zig").URL; -route: *const FilesystemRouter.Match, -route_holder: FilesystemRouter.Match = undefined, -needs_deinit: bool = false, -query_string_map: ?QueryStringMap = null, -param_map: ?QueryStringMap = null, -params_list_holder: FilesystemRouter.Param.List = .{}, - -pub fn importRoute( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, -) js.JSObjectRef { - const prom = JSC.JSModuleLoader.loadAndEvaluateModule(ctx.ptr(), &ZigString.init(this.route.file_path)); - - VirtualMachine.vm.tick(); - - return prom.result(ctx.ptr().vm()).asRef(); -} - -pub fn match( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSObjectRef { - if (arguments.len == 0) { - JSError(getAllocator(ctx), "Expected string, FetchEvent, or Request but there were no arguments", .{}, ctx, exception); - return null; - } - - const arg: JSC.JSValue = brk: { - if (FetchEvent.Class.isLoaded()) { - if (JSValue.as(JSValue.fromRef(arguments[0]), FetchEvent)) |fetch_event| { - if (fetch_event.request_context != null) { - return matchFetchEvent(ctx, fetch_event, exception); - } - - // When disconencted, we still have a copy of the request data in here - break :brk JSC.JSValue.fromRef(fetch_event.getRequest(ctx, null, null, null)); - } - } - break :brk JSC.JSValue.fromRef(arguments[0]); - }; - - var router = JavaScript.VirtualMachine.vm.bundler.router orelse { - JSError(getAllocator(ctx), "Bun.match needs a framework configured with routes", .{}, ctx, exception); - return null; - }; - - var path_: ?ZigString.Slice = null; - var pathname: string = ""; - defer { - if (path_) |path| { - path.deinit(); - } - } - - if (arg.isString()) { - var path_string = arg.getZigString(ctx.ptr()); - path_ = path_string.toSlice(bun.default_allocator); - var url = URL.parse(path_.?.slice()); - pathname = url.pathname; - } else if (arg.as(Request)) |req| { - var path_string = req.url; - path_ = path_string.toSlice(bun.default_allocator); - var url = URL.parse(path_.?.slice()); - pathname = url.pathname; - } - - if (path_ == null) { - JSError(getAllocator(ctx), "Expected string, FetchEvent, or Request", .{}, ctx, exception); - return null; - } - - const url_path = URLPath.parse(path_.?.slice()) catch { - JSError(getAllocator(ctx), "Could not parse URL path", .{}, ctx, exception); - return null; - }; - - var match_params_fallback = std.heap.stackFallback(1024, bun.default_allocator); - var match_params_allocator = match_params_fallback.get(); - var match_params = FilesystemRouter.Param.List{}; - match_params.ensureTotalCapacity(match_params_allocator, 16) catch unreachable; - var prev_allocator = router.routes.allocator; - router.routes.allocator = match_params_allocator; - defer router.routes.allocator = prev_allocator; - if (router.routes.matchPage("", url_path, &match_params)) |matched| { - var match_ = matched; - var params_list = match_.params.clone(bun.default_allocator) catch unreachable; - var instance = getAllocator(ctx).create(Router) catch unreachable; - - instance.* = Router{ - .route_holder = match_, - .route = undefined, - }; - instance.params_list_holder = params_list; - instance.route = &instance.route_holder; - instance.route_holder.params = &instance.params_list_holder; - - return Instance.make(ctx, instance); - } - // router.routes.matchPage - - return JSC.JSValue.jsNull().asObjectRef(); -} - -fn matchRequest( - ctx: js.JSContextRef, - request: *const Request, - _: js.ExceptionRef, -) js.JSObjectRef { - return createRouteObject(ctx, request.request_context); -} - -fn matchFetchEvent( - ctx: js.JSContextRef, - fetch_event: *const FetchEvent, - _: js.ExceptionRef, -) js.JSObjectRef { - return createRouteObject(ctx, fetch_event.request_context.?); -} - -fn createRouteObject(ctx: js.JSContextRef, req: *const http.RequestContext) js.JSValueRef { - const route = &(req.matched_route orelse { - return js.JSValueMakeNull(ctx); - }); - - return createRouteObjectFromMatch(ctx, route); -} - -fn createRouteObjectFromMatch( - ctx: js.JSContextRef, - route: *const FilesystemRouter.Match, -) js.JSValueRef { - var router = getAllocator(ctx).create(Router) catch unreachable; - router.* = Router{ - .route = route, - }; - - return Instance.make(ctx, router); -} - -pub const match_type_definition = &[_]d.ts{ - .{ - .tsdoc = "Match a {@link https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent FetchEvent} to a `Route` from the local filesystem. Returns `null` if there is no match.", - .args = &[_]d.ts.arg{ - .{ - .name = "event", - .@"return" = "FetchEvent", - }, - }, - .@"return" = "Route | null", - }, - .{ - .tsdoc = "Match a `pathname` to a `Route` from the local filesystem. Returns `null` if there is no match.", - .args = &[_]d.ts.arg{ - .{ - .name = "pathname", - .@"return" = "string", - }, - }, - .@"return" = "Route | null", - }, - .{ - .tsdoc = "Match a {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Request} to a `Route` from the local filesystem. Returns `null` if there is no match.", - .args = &[_]d.ts.arg{ - .{ - .name = "request", - .@"return" = "Request", - }, - }, - .@"return" = "Route | null", - }, -}; - -pub const Instance = NewClass( - Router, - .{ - .name = "Route", - .read_only = true, - .ts = .{ - .class = d.ts.class{ - .tsdoc = - \\Route matched from the filesystem. - , - }, - }, - }, - .{ - .finalize = finalize, - .import = .{ - .rfn = importRoute, - .ts = d.ts{ - .@"return" = "Object", - .tsdoc = - \\Synchronously load & evaluate the file corresponding to the route. Returns the exports of the route. This is similar to `await import(route.filepath)`, except it's synchronous. It is recommended to use this function instead of `import`. - , - }, - }, - }, - .{ - .pathname = .{ - .get = getPathname, - .ro = true, - .ts = d.ts{ - .@"return" = "string", - .@"tsdoc" = "URL path as appears in a web browser's address bar", - }, - }, - - .filePath = .{ - .get = getFilePath, - .ro = true, - .ts = d.ts{ - .@"return" = "string", - .tsdoc = - \\Project-relative filesystem path to the route file. - , - }, - }, - .scriptSrc = .{ - .get = getScriptSrc, - .ro = true, - .ts = d.ts{ - .@"return" = "string", - .tsdoc = - \\src attribute of the script tag that loads the route. - , - }, - }, - .kind = .{ - .get = getKind, - .ro = true, - .ts = d.ts{ - .@"return" = "\"exact\" | \"dynamic\" | \"catch-all\" | \"optional-catch-all\"", - }, - }, - .name = .{ - .get = getRoute, - .ro = true, - .ts = d.ts{ - .@"return" = "string", - .tsdoc = - \\Route name - \\@example - \\`"blog/posts/[id]"` - \\`"blog/posts/[id]/[[...slug]]"` - \\`"blog"` - , - }, - }, - .query = .{ - .get = getQuery, - .ro = true, - .ts = d.ts{ - .@"return" = "Record<string, string | string[]>", - .tsdoc = - \\Route parameters & parsed query string values as a key-value object - \\ - \\@example - \\```js - \\console.assert(router.query.id === "123"); - \\console.assert(router.pathname === "/blog/posts/123"); - \\console.assert(router.route === "blog/posts/[id]"); - \\``` - , - }, - }, - .params = .{ - .get = getParams, - .ro = true, - .ts = d.ts{ - .@"return" = "Record<string, string | string[]>", - .tsdoc = - \\Route parameters as a key-value object - \\ - \\@example - \\```js - \\console.assert(router.query.id === "123"); - \\console.assert(router.pathname === "/blog/posts/123"); - \\console.assert(router.route === "blog/posts/[id]"); - \\``` - , - }, - }, - }, -); - -pub fn getFilePath( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ZigString.init(this.route.file_path) - .withEncoding() - .toValueGC(ctx.ptr()).asRef(); -} - -pub fn finalize( - this: *Router, -) void { - if (this.query_string_map) |*map| { - map.deinit(); - } - - if (this.needs_deinit) { - this.params_list_holder.deinit(bun.default_allocator); - this.params_list_holder = .{}; - this.needs_deinit = false; - } - - bun.default_allocator.destroy(this); -} - -pub fn getPathname( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ZigString.init(this.route.pathname) - .withEncoding() - .toValueGC(ctx.ptr()).asRef(); -} - -pub fn getRoute( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return ZigString.init(this.route.name) - .withEncoding() - .toValueGC(ctx.ptr()).asRef(); -} - -const KindEnum = struct { - pub const exact = "exact"; - pub const catch_all = "catch-all"; - pub const optional_catch_all = "optional-catch-all"; - pub const dynamic = "dynamic"; - - // this is kinda stupid it should maybe just store it - pub fn init(name: string) ZigString { - if (strings.contains(name, "[[...")) { - return ZigString.init(optional_catch_all); - } else if (strings.contains(name, "[...")) { - return ZigString.init(catch_all); - } else if (strings.contains(name, "[")) { - return ZigString.init(dynamic); - } else { - return ZigString.init(exact); - } - } -}; - -pub fn getKind( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - return KindEnum.init(this.route.name).toValue(ctx.ptr()).asRef(); -} - -threadlocal var query_string_values_buf: [256]string = undefined; -threadlocal var query_string_value_refs_buf: [256]ZigString = undefined; -pub fn createQueryObject(ctx: js.JSContextRef, map: *QueryStringMap, _: js.ExceptionRef) callconv(.C) js.JSValueRef { - const QueryObjectCreator = struct { - query: *QueryStringMap, - pub fn create(this: *@This(), obj: *JSObject, global: *JSGlobalObject) void { - var iter = this.query.iter(); - var str: ZigString = undefined; - while (iter.next(&query_string_values_buf)) |entry| { - str = ZigString.init(entry.name); - - std.debug.assert(entry.values.len > 0); - if (entry.values.len > 1) { - var values = query_string_value_refs_buf[0..entry.values.len]; - for (entry.values) |value, i| { - values[i] = ZigString.init(value); - } - obj.putRecord(global, &str, values.ptr, values.len); - } else { - query_string_value_refs_buf[0] = ZigString.init(entry.values[0]); - - obj.putRecord(global, &str, &query_string_value_refs_buf, 1); - } - } - } - }; - - var creator = QueryObjectCreator{ .query = map }; - - var value = JSObject.createWithInitializer(QueryObjectCreator, &creator, ctx.ptr(), map.getNameCount()); - - return value.asRef(); -} - -pub fn getScriptSrcString( - comptime Writer: type, - writer: Writer, - file_path: string, - client_framework_enabled: bool, -) void { - var entry_point_tempbuf: [bun.MAX_PATH_BYTES]u8 = undefined; - // We don't store the framework config including the client parts in the server - // instead, we just store a boolean saying whether we should generate this whenever the script is requested - // this is kind of bad. we should consider instead a way to inline the contents of the script. - if (client_framework_enabled) { - JSC.API.Bun.getPublicPath( - Bundler.ClientEntryPoint.generateEntryPointPath( - &entry_point_tempbuf, - Fs.PathName.init(file_path), - ), - VirtualMachine.vm.origin, - Writer, - writer, - ); - } else { - JSC.API.Bun.getPublicPath(file_path, VirtualMachine.vm.origin, Writer, writer); - } -} - -pub fn getScriptSrc( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSStringRef, - _: js.ExceptionRef, -) js.JSValueRef { - var script_src_buffer = std.ArrayList(u8).init(bun.default_allocator); - - var writer = script_src_buffer.writer(); - getScriptSrcString(@TypeOf(&writer), &writer, this.route.file_path, this.route.client_framework_enabled); - - return ZigString.init(script_src_buffer.toOwnedSlice()).toExternalValue(ctx.ptr()).asObjectRef(); -} - -pub fn getParams( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSStringRef, - exception: js.ExceptionRef, -) js.JSValueRef { - if (this.param_map == null) { - if (this.route.params.len > 0) { - if (QueryStringMap.initWithScanner(getAllocator(ctx), CombinedScanner.init( - "", - this.route.pathnameWithoutLeadingSlash(), - this.route.name, - this.route.params, - ))) |map| { - this.param_map = map; - } else |_| {} - } - } - - // If it's still null, there are no params - if (this.param_map) |*map| { - return createQueryObject(ctx, map, exception); - } else { - return JSValue.createEmptyObject(ctx.ptr(), 0).asRef(); - } -} - -pub fn getQuery( - this: *Router, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSStringRef, - exception: js.ExceptionRef, -) js.JSValueRef { - if (this.query_string_map == null) { - if (this.route.params.len > 0) { - if (QueryStringMap.initWithScanner(getAllocator(ctx), CombinedScanner.init( - this.route.query_string, - this.route.pathnameWithoutLeadingSlash(), - this.route.name, - - this.route.params, - ))) |map| { - this.query_string_map = map; - } else |_| {} - } else if (this.route.query_string.len > 0) { - if (QueryStringMap.init(getAllocator(ctx), this.route.query_string)) |map| { - this.query_string_map = map; - } else |_| {} - } - } - - // If it's still null, the query string has no names. - if (this.query_string_map) |*map| { - return createQueryObject(ctx, map, exception); - } else { - return JSValue.createEmptyObject(ctx.ptr(), 0).asRef(); - } -} diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig deleted file mode 100644 index 711329a95..000000000 --- a/src/javascript/jsc/api/server.zig +++ /dev/null @@ -1,1844 +0,0 @@ -const Bun = @This(); -const default_allocator = @import("../../../global.zig").default_allocator; -const bun = @import("../../../global.zig"); -const Environment = bun.Environment; -const NetworkThread = @import("http").NetworkThread; -const Global = bun.Global; -const strings = bun.strings; -const string = bun.string; -const Output = @import("../../../global.zig").Output; -const MutableString = @import("../../../global.zig").MutableString; -const std = @import("std"); -const Allocator = std.mem.Allocator; -const IdentityContext = @import("../../../identity_context.zig").IdentityContext; -const Fs = @import("../../../fs.zig"); -const Resolver = @import("../../../resolver/resolver.zig"); -const ast = @import("../../../import_record.zig"); -const NodeModuleBundle = @import("../../../node_module_bundle.zig").NodeModuleBundle; -const MacroEntryPoint = @import("../../../bundler.zig").MacroEntryPoint; -const logger = @import("../../../logger.zig"); -const Api = @import("../../../api/schema.zig").Api; -const options = @import("../../../options.zig"); -const Bundler = @import("../../../bundler.zig").Bundler; -const ServerEntryPoint = @import("../../../bundler.zig").ServerEntryPoint; -const js_printer = @import("../../../js_printer.zig"); -const js_parser = @import("../../../js_parser.zig"); -const js_ast = @import("../../../js_ast.zig"); -const hash_map = @import("../../../hash_map.zig"); -const http = @import("../../../http.zig"); -const NodeFallbackModules = @import("../../../node_fallbacks.zig"); -const ImportKind = ast.ImportKind; -const Analytics = @import("../../../analytics/analytics_thread.zig"); -const ZigString = @import("../../../jsc.zig").ZigString; -const Runtime = @import("../../../runtime.zig"); -const Router = @import("./router.zig"); -const ImportRecord = ast.ImportRecord; -const DotEnv = @import("../../../env_loader.zig"); -const ParseResult = @import("../../../bundler.zig").ParseResult; -const PackageJSON = @import("../../../resolver/package_json.zig").PackageJSON; -const MacroRemap = @import("../../../resolver/package_json.zig").MacroMap; -const WebCore = @import("../../../jsc.zig").WebCore; -const Request = WebCore.Request; -const Response = WebCore.Response; -const Headers = WebCore.Headers; -const Fetch = WebCore.Fetch; -const HTTP = @import("http"); -const FetchEvent = WebCore.FetchEvent; -const js = @import("../../../jsc.zig").C; -const JSC = @import("../../../jsc.zig"); -const JSError = @import("../base.zig").JSError; -const MarkedArrayBuffer = @import("../base.zig").MarkedArrayBuffer; -const getAllocator = @import("../base.zig").getAllocator; -const JSValue = @import("../../../jsc.zig").JSValue; -const NewClass = @import("../base.zig").NewClass; -const Microtask = @import("../../../jsc.zig").Microtask; -const JSGlobalObject = @import("../../../jsc.zig").JSGlobalObject; -const ExceptionValueRef = @import("../../../jsc.zig").ExceptionValueRef; -const JSPrivateDataPtr = @import("../../../jsc.zig").JSPrivateDataPtr; -const ZigConsoleClient = @import("../../../jsc.zig").ZigConsoleClient; -const Node = @import("../../../jsc.zig").Node; -const ZigException = @import("../../../jsc.zig").ZigException; -const ZigStackTrace = @import("../../../jsc.zig").ZigStackTrace; -const ErrorableResolvedSource = @import("../../../jsc.zig").ErrorableResolvedSource; -const ResolvedSource = @import("../../../jsc.zig").ResolvedSource; -const JSPromise = @import("../../../jsc.zig").JSPromise; -const JSInternalPromise = @import("../../../jsc.zig").JSInternalPromise; -const JSModuleLoader = @import("../../../jsc.zig").JSModuleLoader; -const JSPromiseRejectionOperation = @import("../../../jsc.zig").JSPromiseRejectionOperation; -const Exception = @import("../../../jsc.zig").Exception; -const ErrorableZigString = @import("../../../jsc.zig").ErrorableZigString; -const ZigGlobalObject = @import("../../../jsc.zig").ZigGlobalObject; -const VM = @import("../../../jsc.zig").VM; -const JSFunction = @import("../../../jsc.zig").JSFunction; -const Config = @import("../config.zig"); -const URL = @import("../../../url.zig").URL; -const Transpiler = @import("./transpiler.zig"); -const VirtualMachine = @import("../javascript.zig").VirtualMachine; -const IOTask = JSC.IOTask; -const is_bindgen = JSC.is_bindgen; -const uws = @import("uws"); -const Fallback = Runtime.Fallback; -const MimeType = HTTP.MimeType; -const Blob = JSC.WebCore.Blob; -const BoringSSL = @import("boringssl"); -const Arena = @import("../../../mimalloc_arena.zig").Arena; -const SendfileContext = struct { - fd: i32, - socket_fd: i32 = 0, - remain: Blob.SizeType = 0, - offset: Blob.SizeType = 0, - has_listener: bool = false, - has_set_on_writable: bool = false, - auto_close: bool = false, -}; -const DateTime = @import("datetime"); -const linux = std.os.linux; - -pub const ServerConfig = struct { - port: u16 = 0, - hostname: [*:0]const u8 = "0.0.0.0", - - // TODO: use webkit URL parser instead of bun's - base_url: URL = URL{}, - base_uri: string = "", - - ssl_config: ?SSLConfig = null, - max_request_body_size: usize = 1024 * 1024 * 128, - development: bool = false, - - onError: JSC.JSValue = JSC.JSValue.zero, - onRequest: JSC.JSValue = JSC.JSValue.zero, - - pub const SSLConfig = struct { - server_name: [*c]const u8 = null, - - key_file_name: [*c]const u8 = null, - cert_file_name: [*c]const u8 = null, - - ca_file_name: [*c]const u8 = null, - dh_params_file_name: [*c]const u8 = null, - - passphrase: [*c]const u8 = null, - low_memory_mode: bool = false, - - pub fn deinit(this: *SSLConfig) void { - const fields = .{ - "server_name", - "key_file_name", - "cert_file_name", - "ca_file_name", - "dh_params_file_name", - "passphrase", - }; - - inline for (fields) |field| { - const slice = std.mem.span(@field(this, field)); - if (slice.len > 0) { - bun.default_allocator.free(slice); - } - } - } - - const zero = SSLConfig{}; - - pub fn inJS(global: *JSC.JSGlobalObject, obj: JSC.JSValue, exception: JSC.C.ExceptionRef) ?SSLConfig { - var result = zero; - var any = false; - - // Required - if (obj.getTruthy(global, "keyFile")) |key_file_name| { - var sliced = key_file_name.toSlice(global, bun.default_allocator); - defer sliced.deinit(); - if (sliced.len > 0) { - result.key_file_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable; - if (std.os.system.access(result.key_file_name, std.os.F_OK) != 0) { - JSC.throwInvalidArguments("Unable to access keyFile path", .{}, global.ref(), exception); - result.deinit(); - - return null; - } - any = true; - } - } - if (obj.getTruthy(global, "certFile")) |cert_file_name| { - var sliced = cert_file_name.toSlice(global, bun.default_allocator); - defer sliced.deinit(); - if (sliced.len > 0) { - result.cert_file_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable; - if (std.os.system.access(result.cert_file_name, std.os.F_OK) != 0) { - JSC.throwInvalidArguments("Unable to access certFile path", .{}, global.ref(), exception); - result.deinit(); - return null; - } - any = true; - } - } - - // Optional - if (any) { - if (obj.getTruthy(global, "serverName")) |key_file_name| { - var sliced = key_file_name.toSlice(global, bun.default_allocator); - defer sliced.deinit(); - if (sliced.len > 0) { - result.server_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable; - } - } - - if (obj.getTruthy(global, "caFile")) |ca_file_name| { - var sliced = ca_file_name.toSlice(global, bun.default_allocator); - defer sliced.deinit(); - if (sliced.len > 0) { - result.ca_file_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable; - if (std.os.system.access(result.ca_file_name, std.os.F_OK) != 0) { - JSC.throwInvalidArguments("Invalid caFile path", .{}, global.ref(), exception); - result.deinit(); - return null; - } - } - } - if (obj.getTruthy(global, "dhParamsFile")) |dh_params_file_name| { - var sliced = dh_params_file_name.toSlice(global, bun.default_allocator); - defer sliced.deinit(); - if (sliced.len > 0) { - result.dh_params_file_name = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable; - if (std.os.system.access(result.dh_params_file_name, std.os.F_OK) != 0) { - JSC.throwInvalidArguments("Invalid dhParamsFile path", .{}, global.ref(), exception); - result.deinit(); - return null; - } - } - } - - if (obj.getTruthy(global, "passphrase")) |passphrase| { - var sliced = passphrase.toSlice(global, bun.default_allocator); - defer sliced.deinit(); - if (sliced.len > 0) { - result.passphrase = bun.default_allocator.dupeZ(u8, sliced.slice()) catch unreachable; - } - } - - if (obj.get(global, "lowMemoryMode")) |low_memory_mode| { - result.low_memory_mode = low_memory_mode.toBoolean(); - any = true; - } - } - - if (!any) - return null; - return result; - } - - pub fn fromJS(global: *JSC.JSGlobalObject, arguments: *JSC.Node.ArgumentsSlice, exception: JSC.C.ExceptionRef) ?SSLConfig { - if (arguments.next()) |arg| { - return SSLConfig.inJS(global, arg, exception); - } - - return null; - } - }; - - pub fn fromJS(global: *JSC.JSGlobalObject, arguments: *JSC.Node.ArgumentsSlice, exception: JSC.C.ExceptionRef) ServerConfig { - var env = arguments.vm.bundler.env; - - var args = ServerConfig{ - .port = 3000, - .hostname = "0.0.0.0", - .development = true, - }; - var has_hostname = false; - if (strings.eqlComptime(env.get("NODE_ENV") orelse "", "production")) { - args.development = false; - } - - if (arguments.vm.bundler.options.production) { - args.development = false; - } - - const PORT_ENV = .{ "PORT", "BUN_PORT", "NODE_PORT" }; - - inline for (PORT_ENV) |PORT| { - if (env.get(PORT)) |port| { - if (std.fmt.parseInt(u16, port, 10)) |_port| { - args.port = _port; - } else |_| {} - } - } - - if (arguments.vm.bundler.options.transform_options.port) |port| { - args.port = port; - } - - if (arguments.vm.bundler.options.transform_options.origin) |origin| { - args.base_uri = origin; - } - - if (arguments.next()) |arg| { - if (arg.isUndefinedOrNull() or !arg.isObject()) { - JSC.throwInvalidArguments("Bun.serve expects an object", .{}, global.ref(), exception); - return args; - } - - if (arg.getTruthy(global, "port")) |port_| { - args.port = @intCast(u16, @minimum(@maximum(0, port_.toInt32()), std.math.maxInt(u16))); - } - - if (arg.getTruthy(global, "baseURI")) |baseURI| { - var sliced = baseURI.toSlice(global, bun.default_allocator); - - if (sliced.len > 0) { - defer sliced.deinit(); - args.base_uri = bun.default_allocator.dupe(u8, sliced.slice()) catch unreachable; - } - } - - if (arg.getTruthy(global, "hostname") orelse arg.getTruthy(global, "host")) |host| { - const host_str = host.toSlice( - global, - bun.default_allocator, - ); - if (host_str.len > 0) { - args.hostname = bun.default_allocator.dupeZ(u8, host_str.slice()) catch unreachable; - has_hostname = true; - } - } - - if (arg.get(global, "development")) |dev| { - args.development = dev.toBoolean(); - } - - if (SSLConfig.fromJS(global, arguments, exception)) |ssl_config| { - args.ssl_config = ssl_config; - } - - if (exception.* != null) { - return args; - } - - if (arg.getTruthy(global, "maxRequestBodySize")) |max_request_body_size| { - args.max_request_body_size = @intCast(u64, @maximum(0, max_request_body_size.toInt64())); - } - - if (arg.getTruthy(global, "error")) |onError| { - if (!onError.isCallable(global.vm())) { - JSC.throwInvalidArguments("Expected error to be a function", .{}, global.ref(), exception); - if (args.ssl_config) |*conf| { - conf.deinit(); - } - return args; - } - JSC.C.JSValueProtect(global.ref(), onError.asObjectRef()); - args.onError = onError; - } - - if (arg.getTruthy(global, "fetch")) |onRequest| { - if (!onRequest.isCallable(global.vm())) { - JSC.throwInvalidArguments("Expected fetch() to be a function", .{}, global.ref(), exception); - return args; - } - JSC.C.JSValueProtect(global.ref(), onRequest.asObjectRef()); - args.onRequest = onRequest; - } else { - JSC.throwInvalidArguments("Expected fetch() to be a function", .{}, global.ref(), exception); - if (args.ssl_config) |*conf| { - conf.deinit(); - } - return args; - } - } - - if (args.port == 0) { - JSC.throwInvalidArguments("Invalid port: must be > 0", .{}, global.ref(), exception); - } - - if (args.base_uri.len > 0) { - args.base_url = URL.parse(args.base_uri); - if (args.base_url.hostname.len == 0) { - JSC.throwInvalidArguments("baseURI must have a hostname", .{}, global.ref(), exception); - bun.default_allocator.free(bun.constStrToU8(args.base_uri)); - args.base_uri = ""; - return args; - } - - if (!strings.isAllASCII(args.base_uri)) { - JSC.throwInvalidArguments("Unicode baseURI must already be encoded for now.\nnew URL(baseuRI).toString() should do the trick.", .{}, global.ref(), exception); - bun.default_allocator.free(bun.constStrToU8(args.base_uri)); - args.base_uri = ""; - return args; - } - - if (args.base_url.protocol.len == 0) { - const protocol: string = if (args.ssl_config != null) "https" else "http"; - - args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null)) - std.fmt.allocPrint(bun.default_allocator, "{s}://{s}/{s}", .{ - protocol, - args.base_url.hostname, - strings.trimLeadingChar(args.base_url.pathname, '/'), - }) - else - std.fmt.allocPrint(bun.default_allocator, "{s}://{s}:{d}/{s}", .{ - protocol, - args.base_url.hostname, - args.port, - strings.trimLeadingChar(args.base_url.pathname, '/'), - })) catch unreachable; - - args.base_url = URL.parse(args.base_uri); - } - } else { - const hostname: string = - if (has_hostname and std.mem.span(args.hostname).len > 0) std.mem.span(args.hostname) else "localhost"; - const protocol: string = if (args.ssl_config != null) "https" else "http"; - - args.base_uri = (if ((args.port == 80 and args.ssl_config == null) or (args.port == 443 and args.ssl_config != null)) - std.fmt.allocPrint(bun.default_allocator, "{s}://{s}/", .{ - protocol, - hostname, - }) - else - std.fmt.allocPrint(bun.default_allocator, "{s}://{s}:{d}/", .{ protocol, hostname, args.port })) catch unreachable; - - if (!strings.isAllASCII(hostname)) { - JSC.throwInvalidArguments("Unicode hostnames must already be encoded for now.\nnew URL(input).hostname should do the trick.", .{}, global.ref(), exception); - bun.default_allocator.free(bun.constStrToU8(args.base_uri)); - args.base_uri = ""; - return args; - } - - args.base_url = URL.parse(args.base_uri); - } - - // I don't think there's a case where this can happen - // but let's check anyway, just in case - if (args.base_url.hostname.len == 0) { - JSC.throwInvalidArguments("baseURI must have a hostname", .{}, global.ref(), exception); - bun.default_allocator.free(bun.constStrToU8(args.base_uri)); - args.base_uri = ""; - return args; - } - - if (args.base_url.username.len > 0 or args.base_url.password.len > 0) { - JSC.throwInvalidArguments("baseURI can't have a username or password", .{}, global.ref(), exception); - bun.default_allocator.free(bun.constStrToU8(args.base_uri)); - args.base_uri = ""; - return args; - } - - return args; - } -}; - -pub fn NewRequestContextStackAllocator(comptime RequestContext: type, comptime count: usize) type { - // Pre-allocate up to 2048 requests - // use a bitset to track which ones are used - return struct { - buf: [count]RequestContext = undefined, - unused: Set = undefined, - fallback_allocator: std.mem.Allocator = undefined, - - pub const Set = std.bit_set.ArrayBitSet(usize, count); - - pub fn get(this: *@This()) std.mem.Allocator { - this.unused = Set.initFull(); - return std.mem.Allocator.init(this, alloc, resize, free); - } - - fn alloc(self: *@This(), a: usize, b: u29, c: u29, d: usize) ![]u8 { - if (self.unused.findFirstSet()) |i| { - self.unused.unset(i); - return std.mem.asBytes(&self.buf[i]); - } - - return try self.fallback_allocator.rawAlloc(a, b, c, d); - } - - fn resize( - _: *@This(), - _: []u8, - _: u29, - _: usize, - _: u29, - _: usize, - ) ?usize { - unreachable; - } - - fn sliceContainsSlice(container: []u8, slice: []u8) bool { - return @ptrToInt(slice.ptr) >= @ptrToInt(container.ptr) and - (@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(container.ptr) + container.len); - } - - fn free( - self: *@This(), - buf: []u8, - buf_align: u29, - return_address: usize, - ) void { - _ = buf_align; - _ = return_address; - const bytes = std.mem.asBytes(&self.buf); - if (sliceContainsSlice(bytes, buf)) { - const index = if (bytes[0..buf.len].ptr != buf.ptr) - (@ptrToInt(buf.ptr) - @ptrToInt(bytes)) / @sizeOf(RequestContext) - else - @as(usize, 0); - - if (comptime Environment.allow_assert) { - std.debug.assert(@intToPtr(*RequestContext, @ptrToInt(buf.ptr)) == &self.buf[index]); - std.debug.assert(!self.unused.isSet(index)); - } - - self.unused.set(index); - } else { - self.fallback_allocator.rawFree(buf, buf_align, return_address); - } - } - }; -} - -// This is defined separately partially to work-around an LLVM debugger bug. -fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comptime ThisServer: type) type { - return struct { - const RequestContext = @This(); - const App = uws.NewApp(ssl_enabled); - pub threadlocal var pool: ?*RequestContext.RequestContextStackAllocator = null; - pub threadlocal var pool_allocator: std.mem.Allocator = undefined; - - pub const RequestContextStackAllocator = NewRequestContextStackAllocator(RequestContext, 2048); - pub const name = "HTTPRequestContext" ++ (if (debug_mode) "Debug" else "") ++ (if (ThisServer.ssl_enabled) "TLS" else ""); - pub const shim = JSC.Shimmer("Bun", name, @This()); - - server: *ThisServer, - resp: *App.Response, - /// thread-local default heap allocator - /// this prevents an extra pthread_getspecific() call which shows up in profiling - allocator: std.mem.Allocator, - req: *uws.Request, - url: string, - method: HTTP.Method, - aborted: bool = false, - finalized: bun.DebugOnly(bool) = bun.DebugOnlyDefault(false), - - /// We can only safely free once the request body promise is finalized - /// and the response is rejected - pending_promises_for_abort: u8 = 0, - - has_marked_complete: bool = false, - response_jsvalue: JSC.JSValue = JSC.JSValue.zero, - response_ptr: ?*JSC.WebCore.Response = null, - blob: JSC.WebCore.Blob = JSC.WebCore.Blob{}, - promise: ?*JSC.JSValue = null, - response_headers: ?*JSC.FetchHeaders = null, - has_abort_handler: bool = false, - has_sendfile_ctx: bool = false, - has_called_error_handler: bool = false, - needs_content_length: bool = false, - sendfile: SendfileContext = undefined, - request_js_object: JSC.C.JSObjectRef = null, - request_body_buf: std.ArrayListUnmanaged(u8) = .{}, - - /// Used either for temporary blob data or fallback - /// When the response body is a temporary value - response_buf_owned: std.ArrayListUnmanaged(u8) = .{}, - - // TODO: support builtin compression - const can_sendfile = !ssl_enabled; - - pub const thenables = shim.thenables(.{ - PromiseHandler, - }); - - pub const lazy_static_functions = thenables; - pub const Export = lazy_static_functions; - - const PromiseHandler = JSC.Thenable(RequestContext, onResolve, onReject); - - pub fn setAbortHandler(this: *RequestContext) void { - if (this.has_abort_handler) return; - this.has_abort_handler = true; - this.resp.onAborted(*RequestContext, RequestContext.onAbort, this); - } - - pub fn onResolve( - ctx: *RequestContext, - _: *JSC.JSGlobalObject, - arguments: []const JSC.JSValue, - ) void { - if (ctx.aborted) { - ctx.finalizeForAbort(); - return; - } - - if (arguments.len == 0) { - ctx.renderMissing(); - return; - } - - handleResolve(ctx, arguments[0]); - } - - fn handleResolve(ctx: *RequestContext, value: JSC.JSValue) void { - if (value.isEmptyOrUndefinedOrNull()) { - ctx.renderMissing(); - return; - } - - var response = value.as(JSC.WebCore.Response) orelse { - Output.prettyErrorln("Expected a Response object", .{}); - Output.flush(); - ctx.renderMissing(); - return; - }; - ctx.response_jsvalue = value; - JSC.C.JSValueProtect(ctx.server.globalThis.ref(), value.asObjectRef()); - - ctx.render(response); - } - - pub fn finalizeForAbort(this: *RequestContext) void { - this.pending_promises_for_abort -|= 1; - if (this.pending_promises_for_abort == 0) this.finalize(); - } - - pub fn onReject( - ctx: *RequestContext, - _: *JSC.JSGlobalObject, - arguments: []const JSC.JSValue, - ) void { - if (ctx.aborted) { - ctx.finalizeForAbort(); - return; - } - handleReject(ctx, if (arguments.len > 0) arguments[0] else JSC.JSValue.jsUndefined()); - } - - fn handleReject(ctx: *RequestContext, value: JSC.JSValue) void { - ctx.runErrorHandler( - value, - ); - - if (ctx.aborted) { - ctx.finalizeForAbort(); - return; - } - if (!ctx.resp.hasResponded()) { - ctx.renderMissing(); - } - } - - pub fn renderMissing(ctx: *RequestContext) void { - if (comptime !debug_mode) { - ctx.resp.writeStatus("204 No Content"); - ctx.resp.endWithoutBody(); - ctx.finalize(); - } else { - ctx.resp.writeStatus("200 OK"); - ctx.resp.end("Welcome to Bun! To get started, return a Response object.", false); - ctx.finalize(); - } - } - - pub fn renderDefaultError( - this: *RequestContext, - log: *logger.Log, - err: anyerror, - exceptions: []Api.JsException, - comptime fmt: string, - args: anytype, - ) void { - this.resp.writeStatus("500 Internal Server Error"); - this.resp.writeHeader("content-type", MimeType.html.value); - - const allocator = this.allocator; - - var fallback_container = allocator.create(Api.FallbackMessageContainer) catch unreachable; - defer allocator.destroy(fallback_container); - fallback_container.* = Api.FallbackMessageContainer{ - .message = std.fmt.allocPrint(allocator, comptime Output.prettyFmt(fmt, false), args) catch unreachable, - .router = null, - .reason = .fetch_event_handler, - .cwd = VirtualMachine.vm.bundler.fs.top_level_dir, - .problems = Api.Problems{ - .code = @truncate(u16, @errorToInt(err)), - .name = @errorName(err), - .exceptions = exceptions, - .build = log.toAPI(allocator) catch unreachable, - }, - }; - - if (comptime fmt.len > 0) Output.prettyErrorln(fmt, args); - Output.flush(); - - var bb = std.ArrayList(u8).init(allocator); - var bb_writer = bb.writer(); - - Fallback.renderBackend( - allocator, - fallback_container, - @TypeOf(bb_writer), - bb_writer, - ) catch unreachable; - if (this.resp.tryEnd(bb.items, bb.items.len)) { - bb.clearAndFree(); - this.finalizeWithoutDeinit(); - return; - } - - this.response_buf_owned = std.ArrayListUnmanaged(u8){ .items = bb.items, .capacity = bb.capacity }; - this.renderResponseBuffer(); - } - - pub fn renderResponseBuffer(this: *RequestContext) void { - this.resp.onWritable(*RequestContext, onWritableResponseBuffer, this); - } - - pub fn onWritableResponseBuffer(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool { - std.debug.assert(this.resp == resp); - if (this.aborted) { - this.finalizeForAbort(); - return false; - } - return this.sendWritableBytes(this.response_buf_owned.items, write_offset, resp); - } - - pub fn create(this: *RequestContext, server: *ThisServer, req: *uws.Request, resp: *App.Response) void { - this.* = .{ - .allocator = server.allocator, - .resp = resp, - .req = req, - // this memory is owned by the Request object - .url = strings.append(this.allocator, server.base_url_string_for_joining, req.url()) catch - @panic("Out of memory while joining the URL path?"), - .method = HTTP.Method.which(req.method()) orelse .GET, - .server = server, - }; - } - - pub fn isDeadRequest(this: *RequestContext) bool { - if (this.pending_promises_for_abort > 0) return false; - - if (this.promise != null) { - return false; - } - - if (this.request_js_object) |obj| { - if (obj.value().as(Request)) |req| { - if (req.body == .Locked) { - return false; - } - } - } - - return true; - } - - pub fn onAbort(this: *RequestContext, resp: *App.Response) void { - std.debug.assert(this.resp == resp); - std.debug.assert(!this.aborted); - this.aborted = true; - - // if we can, free the request now. - if (this.isDeadRequest()) { - this.finalizeWithoutDeinit(); - this.markComplete(); - this.deinit(); - } else { - this.pending_promises_for_abort = 0; - - // if we cannot, we have to reject pending promises - // first, we reject the request body promise - if (this.request_js_object != null) { - var request_js = this.request_js_object.?.value(); - request_js.ensureStillAlive(); - - this.request_js_object = null; - defer request_js.ensureStillAlive(); - defer JSC.C.JSValueUnprotect(this.server.globalThis.ref(), request_js.asObjectRef()); - // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object - // but we received nothing or the connection was aborted - if (request_js.as(Request)) |req| { - // the promise is pending - if (req.body == .Locked and (req.body.Locked.action != .none or req.body.Locked.promise != null)) { - this.pending_promises_for_abort += 1; - req.body.toErrorInstance(JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis), this.server.globalThis); - } - req.uws_request = null; - } - } - - // then, we reject the response promise - if (this.promise) |promise| { - this.pending_promises_for_abort += 1; - this.promise = null; - promise.asPromise().?.reject(this.server.globalThis, JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis)); - } - - if (this.pending_promises_for_abort > 0) { - this.server.vm.tick(); - } - } - } - - pub fn markComplete(this: *RequestContext) void { - if (!this.has_marked_complete) this.server.onRequestComplete(); - this.has_marked_complete = true; - } - - // This function may be called multiple times - // so it's important that we can safely do that - pub fn finalizeWithoutDeinit(this: *RequestContext) void { - this.blob.detach(); - - if (comptime Environment.allow_assert) { - std.debug.assert(!this.finalized); - this.finalized = true; - } - - if (!this.response_jsvalue.isEmpty()) { - this.server.response_objects_pool.push(this.server.globalThis, this.response_jsvalue); - this.response_jsvalue = JSC.JSValue.zero; - } - - if (this.request_js_object != null) { - var request_js = this.request_js_object.?.value(); - request_js.ensureStillAlive(); - - this.request_js_object = null; - defer request_js.ensureStillAlive(); - defer JSC.C.JSValueUnprotect(this.server.globalThis.ref(), request_js.asObjectRef()); - // User called .blob(), .json(), text(), or .arrayBuffer() on the Request object - // but we received nothing or the connection was aborted - if (request_js.as(Request)) |req| { - // the promise is pending - if (req.body == .Locked and req.body.Locked.action != .none and req.body.Locked.promise != null) { - req.body.toErrorInstance(JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis), this.server.globalThis); - } - req.uws_request = null; - } - } - - if (this.promise) |promise| { - this.promise = null; - - if (promise.asInternalPromise()) |prom| { - prom.rejectAsHandled(this.server.globalThis, (JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis))); - } else if (promise.asPromise()) |prom| { - prom.rejectAsHandled(this.server.globalThis, (JSC.toTypeError(.ABORT_ERR, "Request aborted", .{}, this.server.globalThis))); - } - JSC.C.JSValueUnprotect(this.server.globalThis.ref(), promise.asObjectRef()); - } - - if (this.response_headers != null) { - this.response_headers.?.deref(); - this.response_headers = null; - } - } - pub fn finalize(this: *RequestContext) void { - this.finalizeWithoutDeinit(); - this.markComplete(); - this.deinit(); - } - - pub fn deinit(this: *RequestContext) void { - if (comptime Environment.allow_assert) - std.debug.assert(this.finalized); - - if (comptime Environment.allow_assert) - std.debug.assert(this.has_marked_complete); - - var server = this.server; - this.request_body_buf.clearAndFree(this.allocator); - this.response_buf_owned.clearAndFree(this.allocator); - - server.request_pool_allocator.destroy(this); - } - - fn writeHeaders( - this: *RequestContext, - headers: *JSC.FetchHeaders, - ) void { - headers.remove(&ZigString.init("content-length")); - headers.remove(&ZigString.init("transfer-encoding")); - if (!ssl_enabled) headers.remove(&ZigString.init("strict-transport-security")); - headers.toUWSResponse(ssl_enabled, this.resp); - } - - pub fn writeStatus(this: *RequestContext, status: u16) void { - var status_text_buf: [48]u8 = undefined; - - if (status == 302) { - this.resp.writeStatus("302 Found"); - } else { - this.resp.writeStatus(std.fmt.bufPrint(&status_text_buf, "{d} HM", .{status}) catch unreachable); - } - } - - fn cleanupAndFinalizeAfterSendfile(this: *RequestContext) void { - this.resp.setWriteOffset(this.sendfile.offset); - this.resp.endWithoutBody(); - // use node syscall so that we don't segfault on BADF - if (this.sendfile.auto_close) - _ = JSC.Node.Syscall.close(this.sendfile.fd); - this.sendfile = undefined; - this.finalize(); - } - const separator: string = "\r\n"; - const separator_iovec = [1]std.os.iovec_const{.{ - .iov_base = separator.ptr, - .iov_len = separator.len, - }}; - - pub fn onSendfile(this: *RequestContext) bool { - if (this.aborted) { - this.cleanupAndFinalizeAfterSendfile(); - return false; - } - - const adjusted_count_temporary = @minimum(@as(u64, this.sendfile.remain), @as(u63, std.math.maxInt(u63))); - // TODO we should not need this int cast; improve the return type of `@minimum` - const adjusted_count = @intCast(u63, adjusted_count_temporary); - - if (Environment.isLinux) { - var signed_offset = @intCast(i64, this.sendfile.offset); - const start = this.sendfile.offset; - const val = - // this does the syscall directly, without libc - linux.sendfile(this.sendfile.socket_fd, this.sendfile.fd, &signed_offset, this.sendfile.remain); - this.sendfile.offset = @intCast(Blob.SizeType, signed_offset); - - const errcode = linux.getErrno(val); - - this.sendfile.remain -= @intCast(Blob.SizeType, this.sendfile.offset - start); - - if (errcode != .SUCCESS or this.aborted or this.sendfile.remain == 0 or val == 0) { - if (errcode != .AGAIN and errcode != .SUCCESS and errcode != .PIPE) { - Output.prettyErrorln("Error: {s}", .{@tagName(errcode)}); - Output.flush(); - } - this.cleanupAndFinalizeAfterSendfile(); - return errcode != .SUCCESS; - } - } else { - var sbytes: std.os.off_t = adjusted_count; - const signed_offset = @bitCast(i64, @as(u64, this.sendfile.offset)); - - const errcode = std.c.getErrno(std.c.sendfile( - this.sendfile.fd, - this.sendfile.socket_fd, - - signed_offset, - &sbytes, - null, - 0, - )); - const wrote = @intCast(Blob.SizeType, sbytes); - this.sendfile.offset += wrote; - this.sendfile.remain -= wrote; - if (errcode != .AGAIN or this.aborted or this.sendfile.remain == 0 or sbytes == 0) { - if (errcode != .AGAIN and errcode != .SUCCESS and errcode != .PIPE) { - Output.prettyErrorln("Error: {s}", .{@tagName(errcode)}); - Output.flush(); - } - this.cleanupAndFinalizeAfterSendfile(); - return errcode == .SUCCESS; - } - } - - if (!this.sendfile.has_set_on_writable) { - this.sendfile.has_set_on_writable = true; - this.resp.onWritable(*RequestContext, onWritableSendfile, this); - } - - this.setAbortHandler(); - this.resp.markNeedsMore(); - - return true; - } - - pub fn onWritableBytes(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool { - std.debug.assert(this.resp == resp); - if (this.aborted) { - this.finalizeForAbort(); - return false; - } - - var bytes = this.blob.sharedView(); - return this.sendWritableBytes(bytes, write_offset, resp); - } - - pub fn sendWritableBytes(this: *RequestContext, bytes_: []const u8, write_offset: c_ulong, resp: *App.Response) bool { - std.debug.assert(this.resp == resp); - - var bytes = bytes_[@minimum(bytes_.len, @truncate(usize, write_offset))..]; - if (resp.tryEnd(bytes, bytes_.len)) { - this.finalize(); - return true; - } else { - this.resp.onWritable(*RequestContext, onWritableBytes, this); - return true; - } - } - - pub fn onWritableSendfile(this: *RequestContext, _: c_ulong, _: *App.Response) callconv(.C) bool { - return this.onSendfile(); - } - - // We tried open() in another thread for this - // it was not faster due to the mountain of syscalls - pub fn renderSendFile(this: *RequestContext, blob: JSC.WebCore.Blob) void { - this.blob = blob; - const file = &this.blob.store.?.data.file; - var file_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - const auto_close = file.pathlike != .fd; - const fd = if (!auto_close) - file.pathlike.fd - else switch (JSC.Node.Syscall.open(file.pathlike.path.sliceZ(&file_buf), std.os.O.RDONLY | std.os.O.NONBLOCK | std.os.O.CLOEXEC, 0)) { - .result => |_fd| _fd, - .err => |err| return this.runErrorHandler(err.withPath(file.pathlike.path.slice()).toSystemError().toErrorInstance( - this.server.globalThis, - )), - }; - - // stat only blocks if the target is a file descriptor - const stat: std.os.Stat = switch (JSC.Node.Syscall.fstat(fd)) { - .result => |result| result, - .err => |err| { - this.runErrorHandler(err.withPath(file.pathlike.path.slice()).toSystemError().toErrorInstance( - this.server.globalThis, - )); - if (auto_close) { - _ = JSC.Node.Syscall.close(fd); - } - return; - }, - }; - - if (Environment.isMac) { - if (!std.os.S.ISREG(stat.mode)) { - if (auto_close) { - _ = JSC.Node.Syscall.close(fd); - } - - var err = JSC.Node.Syscall.Error{ - .errno = @intCast(JSC.Node.Syscall.Error.Int, @enumToInt(std.os.E.INVAL)), - .path = file.pathlike.path.slice(), - .syscall = .sendfile, - }; - var sys = err.toSystemError(); - sys.message = ZigString.init("MacOS does not support sending non-regular files"); - this.runErrorHandler(sys.toErrorInstance( - this.server.globalThis, - )); - return; - } - } - - if (Environment.isLinux) { - if (!(std.os.S.ISREG(stat.mode) or std.os.S.ISFIFO(stat.mode))) { - if (auto_close) { - _ = JSC.Node.Syscall.close(fd); - } - - var err = JSC.Node.Syscall.Error{ - .errno = @intCast(JSC.Node.Syscall.Error.Int, @enumToInt(std.os.E.INVAL)), - .path = file.pathlike.path.slice(), - .syscall = .sendfile, - }; - var sys = err.toSystemError(); - sys.message = ZigString.init("File must be regular or FIFO"); - this.runErrorHandler(sys.toErrorInstance( - this.server.globalThis, - )); - return; - } - } - - this.blob.size = @intCast(Blob.SizeType, stat.size); - this.needs_content_length = true; - - this.sendfile = .{ - .fd = fd, - .remain = this.blob.size, - .auto_close = auto_close, - .socket_fd = if (!this.aborted) this.resp.getNativeHandle() else -999, - }; - - this.resp.runCorked(*RequestContext, renderMetadataAndNewline, this); - - if (this.blob.size == 0) { - this.cleanupAndFinalizeAfterSendfile(); - return; - } - - _ = this.onSendfile(); - } - - pub fn renderMetadataAndNewline(this: *RequestContext) void { - this.renderMetadata(); - this.resp.prepareForSendfile(); - } - - pub fn doSendfile(this: *RequestContext, blob: Blob) void { - if (this.aborted) { - this.finalizeForAbort(); - return; - } - - if (this.has_sendfile_ctx) return; - - this.has_sendfile_ctx = true; - - if (comptime can_sendfile) { - return this.renderSendFile(blob); - } - - this.setAbortHandler(); - this.blob.doReadFileInternal(*RequestContext, this, onReadFile, this.server.globalThis); - } - - pub fn onReadFile(this: *RequestContext, result: Blob.Store.ReadFile.ResultType) void { - if (this.aborted) { - this.finalizeForAbort(); - return; - } - - if (result == .err) { - this.runErrorHandler(result.err.toErrorInstance(this.server.globalThis)); - return; - } - - const is_temporary = result.result.is_temporary; - if (!is_temporary) { - this.blob.resolveSize(); - this.doRenderBlob(); - } else { - this.blob.size = @truncate(Blob.SizeType, result.result.buf.len); - this.response_buf_owned = .{ .items = result.result.buf, .capacity = result.result.buf.len }; - this.renderResponseBuffer(); - } - } - - pub fn doRenderWithBodyLocked(this: *anyopaque, value: *JSC.WebCore.Body.Value) void { - doRenderWithBody(bun.cast(*RequestContext, this), value); - } - - pub fn doRenderWithBody(this: *RequestContext, value: *JSC.WebCore.Body.Value) void { - switch (value.*) { - .Error => { - const err = value.Error; - _ = value.use(); - if (this.aborted) { - this.finalizeForAbort(); - return; - } - this.runErrorHandler(err); - return; - }, - .Blob => { - this.blob = value.use(); - - if (this.aborted) { - this.finalizeForAbort(); - return; - } - - if (this.blob.needsToReadFile()) { - this.req.setYield(false); - if (!this.has_sendfile_ctx) - this.doSendfile(this.blob); - return; - } - }, - // TODO: this needs to support streaming! - .Locked => |*lock| { - lock.callback = doRenderWithBodyLocked; - lock.task = this; - return; - }, - else => {}, - } - - this.doRenderBlob(); - } - - pub fn doRenderBlob(this: *RequestContext) void { - if (this.has_abort_handler) - this.resp.runCorked(*RequestContext, renderMetadata, this) - else - this.renderMetadata(); - - this.renderBytes(); - } - - pub fn doRender(this: *RequestContext) void { - if (this.aborted) { - this.finalizeForAbort(); - return; - } - var response = this.response_ptr.?; - this.doRenderWithBody(&response.body.value); - } - - pub fn renderProductionError(this: *RequestContext, status: u16) void { - switch (status) { - 404 => { - this.resp.writeStatus("404 Not Found"); - this.resp.endWithoutBody(); - }, - else => { - this.resp.writeStatus("500 Internal Server Error"); - this.resp.writeHeader("content-type", "text/plain"); - this.resp.end("Something went wrong!", true); - }, - } - - this.finalize(); - } - - pub fn runErrorHandler( - this: *RequestContext, - value: JSC.JSValue, - ) void { - runErrorHandlerWithStatusCode(this, value, 500); - } - - pub fn runErrorHandlerWithStatusCode( - this: *RequestContext, - value: JSC.JSValue, - status: u16, - ) void { - JSC.markBinding(); - if (this.resp.hasResponded()) return; - - var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(this.allocator); - defer exception_list.deinit(); - if (!this.server.config.onError.isEmpty() and !this.has_called_error_handler) { - this.has_called_error_handler = true; - var args = [_]JSC.C.JSValueRef{value.asObjectRef()}; - const result = JSC.C.JSObjectCallAsFunctionReturnValue(this.server.globalThis.ref(), this.server.config.onError.asObjectRef(), this.server.thisObject.asObjectRef(), 1, &args); - - if (!result.isEmptyOrUndefinedOrNull()) { - if (result.isError() or result.isAggregateError(this.server.globalThis)) { - this.runErrorHandler(result); - return; - } else if (result.as(Response)) |response| { - this.render(response); - return; - } - } - } - - if (comptime debug_mode) { - JSC.VirtualMachine.vm.defaultErrorHandler(value, &exception_list); - - this.renderDefaultError( - JSC.VirtualMachine.vm.log, - error.ExceptionOcurred, - exception_list.toOwnedSlice(), - "<r><red>{s}<r> - <b>{s}<r> failed", - .{ std.mem.span(@tagName(this.method)), this.url }, - ); - } else { - if (status != 404) - JSC.VirtualMachine.vm.defaultErrorHandler(value, &exception_list); - this.renderProductionError(status); - } - JSC.VirtualMachine.vm.log.reset(); - return; - } - - pub fn renderMetadata(this: *RequestContext) void { - var response: *JSC.WebCore.Response = this.response_ptr.?; - var status = response.statusCode(); - const size = this.blob.size; - status = if (status == 200 and size == 0) - 204 - else - status; - - this.writeStatus(status); - var needs_content_type = true; - const content_type: MimeType = brk: { - if (response.body.init.headers) |headers_| { - if (headers_.get("content-type")) |content| { - needs_content_type = false; - break :brk MimeType.init(content); - } - } - break :brk if (this.blob.content_type.len > 0) - MimeType.init(this.blob.content_type) - else if (MimeType.sniff(this.blob.sharedView())) |content| - content - else if (this.blob.is_all_ascii orelse false) - MimeType.text - else - MimeType.other; - }; - - var has_content_disposition = false; - - if (response.body.init.headers) |headers_| { - this.writeHeaders(headers_); - has_content_disposition = headers_.has(&ZigString.init("content-disposition")); - response.body.init.headers = null; - headers_.deref(); - } - - if (needs_content_type) { - this.resp.writeHeader("content-type", content_type.value); - } - - // automatically include the filename when: - // 1. Bun.file("foo") - // 2. The content-disposition header is not present - if (!has_content_disposition and content_type.category.autosetFilename()) { - if (this.blob.store) |store| { - if (store.data == .file) { - if (store.data.file.pathlike == .path) { - const basename = std.fs.path.basename(store.data.file.pathlike.path.slice()); - if (basename.len > 0) { - var filename_buf: [1024]u8 = undefined; - - this.resp.writeHeader( - "content-disposition", - std.fmt.bufPrint(&filename_buf, "filename=\"{s}\"", .{basename[0..@minimum(basename.len, 1024 - 32)]}) catch "", - ); - } - } - } - } - } - - if (this.needs_content_length) { - this.resp.writeHeaderInt("content-length", size); - this.needs_content_length = false; - } - } - - pub fn renderBytes(this: *RequestContext) void { - const bytes = this.blob.sharedView(); - - if (!this.resp.tryEnd( - bytes, - bytes.len, - )) { - this.resp.onWritable(*RequestContext, onWritableBytes, this); - return; - } - - this.finalize(); - } - - pub fn render(this: *RequestContext, response: *JSC.WebCore.Response) void { - this.response_ptr = response; - - this.doRender(); - } - - pub fn resolveRequestBody(this: *RequestContext) void { - if (this.aborted) { - this.finalizeForAbort(); - return; - } - - if (JSC.JSValue.fromRef(this.request_js_object).as(Request)) |req| { - var bytes = this.request_body_buf.toOwnedSlice(this.allocator); - var old = req.body; - req.body = .{ - .Blob = if (bytes.len > 0) - Blob.init(bytes, this.allocator, this.server.globalThis) - else - Blob.initEmpty(this.server.globalThis), - }; - old.resolve(&req.body, this.server.globalThis); - VirtualMachine.vm.tick(); - return; - } - } - - pub fn onBodyChunk(this: *RequestContext, resp: *App.Response, chunk: []const u8, last: bool) void { - std.debug.assert(this.resp == resp); - - if (this.aborted) return; - this.request_body_buf.appendSlice(this.allocator, chunk) catch @panic("Out of memory while allocating request body"); - if (last) { - if (JSC.JSValue.fromRef(this.request_js_object).as(Request) != null) { - uws.Loop.get().?.nextTick(*RequestContext, this, resolveRequestBody); - } else { - this.request_body_buf.deinit(this.allocator); - this.request_body_buf = .{}; - } - } - } - - pub fn onPull(this: *RequestContext) void { - if (this.req.header("content-length")) |content_length| { - const len = std.fmt.parseInt(usize, content_length, 10) catch 0; - if (len == 0) { - if (JSC.JSValue.fromRef(this.request_js_object).as(Request)) |req| { - var old = req.body; - old.Locked.callback = null; - req.body = .{ .Empty = .{} }; - old.resolve(&req.body, this.server.globalThis); - VirtualMachine.vm.tick(); - return; - } - } - - if (len >= this.server.config.max_request_body_size) { - if (JSC.JSValue.fromRef(this.request_js_object).as(Request)) |req| { - var old = req.body; - old.Locked.callback = null; - req.body = .{ .Empty = .{} }; - old.toError(error.RequestBodyTooLarge, this.server.globalThis); - VirtualMachine.vm.tick(); - return; - } - - this.resp.writeStatus("413 Request Entity Too Large"); - this.resp.endWithoutBody(); - this.finalize(); - return; - } - - this.request_body_buf.ensureTotalCapacityPrecise(this.allocator, len) catch @panic("Out of memory while allocating request body buffer"); - } - this.setAbortHandler(); - - this.resp.onData(*RequestContext, onBodyChunk, this); - } - - pub fn onPullCallback(this: *anyopaque) void { - onPull(bun.cast(*RequestContext, this)); - } - - comptime { - if (!JSC.is_bindgen) { - @export(PromiseHandler.resolve, .{ - .name = Export[0].symbol_name, - }); - @export(PromiseHandler.reject, .{ - .name = Export[1].symbol_name, - }); - } - } - }; -} - -pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { - return struct { - pub const ssl_enabled = ssl_enabled_; - const debug_mode = debug_mode_; - - const ThisServer = @This(); - pub const RequestContext = NewRequestContext(ssl_enabled, debug_mode, @This()); - - pub const App = uws.NewApp(ssl_enabled); - - listener: ?*App.ListenSocket = null, - thisObject: JSC.JSValue = JSC.JSValue.zero, - app: *App = undefined, - vm: *JSC.VirtualMachine = undefined, - globalThis: *JSGlobalObject, - base_url_string_for_joining: string = "", - response_objects_pool: JSC.WebCore.Response.Pool = JSC.WebCore.Response.Pool{}, - config: ServerConfig = ServerConfig{}, - pending_requests: usize = 0, - request_pool_allocator: std.mem.Allocator = undefined, - has_js_deinited: bool = false, - listen_callback: JSC.AnyTask = undefined, - allocator: std.mem.Allocator, - - pub const Class = JSC.NewClass( - ThisServer, - .{ .name = "Server" }, - .{ - .stop = .{ - .rfn = JSC.wrapSync(ThisServer, "stopFromJS"), - }, - .finalize = .{ - .rfn = finalize, - }, - }, - .{ - .port = .{ - .get = JSC.getterWrap(ThisServer, "getPort"), - }, - .hostname = .{ - .get = JSC.getterWrap(ThisServer, "getHostname"), - }, - .development = .{ - .get = JSC.getterWrap(ThisServer, "getDevelopment"), - }, - .pendingRequests = .{ - .get = JSC.getterWrap(ThisServer, "getPendingRequests"), - }, - }, - ); - - pub fn stopFromJS(this: *ThisServer) JSC.JSValue { - if (this.listener != null) { - JSC.C.JSValueUnprotect(this.globalThis.ref(), this.thisObject.asObjectRef()); - this.thisObject = JSC.JSValue.jsUndefined(); - this.stop(); - } - - return JSC.JSValue.jsUndefined(); - } - - pub fn getPort(this: *ThisServer) JSC.JSValue { - return JSC.JSValue.jsNumber(this.config.port); - } - - pub fn getPendingRequests(this: *ThisServer) JSC.JSValue { - return JSC.JSValue.jsNumber(@intCast(i32, @truncate(u31, this.pending_requests))); - } - - pub fn getHostname(this: *ThisServer, globalThis: *JSGlobalObject) JSC.JSValue { - return ZigString.init(this.config.base_uri).toValue(globalThis); - } - - pub fn getDevelopment( - _: *ThisServer, - ) JSC.JSValue { - return JSC.JSValue.jsBoolean(debug_mode); - } - - pub fn onRequestComplete(this: *ThisServer) void { - this.pending_requests -= 1; - this.deinitIfWeCan(); - } - - pub fn finalize(this: *ThisServer) void { - this.has_js_deinited = true; - this.deinitIfWeCan(); - } - - pub fn deinitIfWeCan(this: *ThisServer) void { - if (this.pending_requests == 0 and this.listener == null and this.has_js_deinited) - this.deinit(); - } - - pub fn stop(this: *ThisServer) void { - if (this.listener) |listener| { - listener.close(); - this.listener = null; - this.vm.disable_run_us_loop = false; - } - - this.deinitIfWeCan(); - } - - pub fn deinit(this: *ThisServer) void { - if (this.vm.response_objects_pool) |pool| { - if (pool == &this.response_objects_pool) { - this.vm.response_objects_pool = null; - } - } - - this.app.destroy(); - const allocator = this.allocator; - allocator.destroy(this); - } - - pub fn init(config: ServerConfig, globalThis: *JSGlobalObject) *ThisServer { - var server = bun.default_allocator.create(ThisServer) catch @panic("Out of memory!"); - server.* = .{ - .globalThis = globalThis, - .config = config, - .base_url_string_for_joining = strings.trim(config.base_url.href, "/"), - .vm = JSC.VirtualMachine.vm, - .allocator = Arena.getThreadlocalDefault(), - }; - if (RequestContext.pool == null) { - RequestContext.pool = server.allocator.create(RequestContext.RequestContextStackAllocator) catch @panic("Out of memory!"); - RequestContext.pool.?.* = .{ - .fallback_allocator = server.allocator, - }; - server.request_pool_allocator = RequestContext.pool.?.get(); - RequestContext.pool_allocator = server.request_pool_allocator; - } else { - server.request_pool_allocator = RequestContext.pool_allocator; - } - - return server; - } - - noinline fn onListenFailed(this: *ThisServer) void { - var zig_str: ZigString = ZigString.init("Failed to start server"); - if (comptime ssl_enabled) { - var output_buf: [4096]u8 = undefined; - output_buf[0] = 0; - var written: usize = 0; - var ssl_error = BoringSSL.ERR_get_error(); - while (ssl_error != 0 and written < output_buf.len) : (ssl_error = BoringSSL.ERR_get_error()) { - if (written > 0) { - output_buf[written] = '\n'; - written += 1; - } - - if (BoringSSL.ERR_reason_error_string( - ssl_error, - )) |reason_ptr| { - const reason = std.mem.span(reason_ptr); - if (reason.len == 0) { - break; - } - @memcpy(output_buf[written..].ptr, reason.ptr, reason.len); - written += reason.len; - } - - if (BoringSSL.ERR_func_error_string( - ssl_error, - )) |reason_ptr| { - const reason = std.mem.span(reason_ptr); - if (reason.len > 0) { - output_buf[written..][0.." via ".len].* = " via ".*; - written += " via ".len; - @memcpy(output_buf[written..].ptr, reason.ptr, reason.len); - written += reason.len; - } - } - - if (BoringSSL.ERR_lib_error_string( - ssl_error, - )) |reason_ptr| { - const reason = std.mem.span(reason_ptr); - if (reason.len > 0) { - output_buf[written..][0] = ' '; - written += 1; - @memcpy(output_buf[written..].ptr, reason.ptr, reason.len); - written += reason.len; - } - } - } - - if (written > 0) { - var message = output_buf[0..written]; - zig_str = ZigString.init(std.fmt.allocPrint(bun.default_allocator, "OpenSSL {s}", .{message}) catch unreachable); - zig_str.withEncoding().mark(); - } - } - // store the exception in here - this.thisObject = zig_str.toErrorInstance(this.globalThis); - return; - } - - pub fn onListen(this: *ThisServer, socket: ?*App.ListenSocket, _: uws.uws_app_listen_config_t) void { - if (socket == null) { - return this.onListenFailed(); - } - - this.listener = socket; - const needs_post_handler = this.vm.uws_event_loop == null; - this.vm.uws_event_loop = uws.Loop.get(); - this.vm.response_objects_pool = &this.response_objects_pool; - this.listen_callback = JSC.AnyTask.New(ThisServer, run).init(this); - this.vm.eventLoop().enqueueTask(JSC.Task.init(&this.listen_callback)); - if (needs_post_handler) { - _ = this.vm.uws_event_loop.?.addPostHandler(*JSC.EventLoop, this.vm.eventLoop(), JSC.EventLoop.tick); - } - } - - pub fn run(this: *ThisServer) void { - // this.app.addServerName(hostname_pattern: [*:0]const u8) - - // we do not increment the reference count here - // uWS manages running the loop, so it is unnecessary - // this.vm.us_loop_reference_count +|= 1; - this.vm.disable_run_us_loop = true; - - this.app.run(); - } - - pub fn onBunInfoRequest(this: *ThisServer, req: *uws.Request, resp: *App.Response) void { - JSC.markBinding(); - this.pending_requests += 1; - defer this.pending_requests -= 1; - req.setYield(false); - var stack_fallback = std.heap.stackFallback(8096, this.allocator); - var allocator = stack_fallback.get(); - - var buffer_writer = js_printer.BufferWriter.init(allocator) catch unreachable; - var writer = js_printer.BufferPrinter.init(buffer_writer); - defer writer.ctx.buffer.deinit(); - var source = logger.Source.initEmptyFile("info.json"); - _ = js_printer.printJSON( - *js_printer.BufferPrinter, - &writer, - bun.Global.BunInfo.generate(*Bundler, &JSC.VirtualMachine.vm.bundler, allocator) catch unreachable, - &source, - ) catch unreachable; - - resp.writeStatus("200 OK"); - resp.writeHeader("Content-Type", MimeType.json.value); - resp.writeHeader("Cache-Control", "public, max-age=3600"); - resp.writeHeaderInt("Age", 0); - const buffer = writer.ctx.written; - resp.end(buffer, false); - } - - pub fn onSrcRequest(this: *ThisServer, req: *uws.Request, resp: *App.Response) void { - JSC.markBinding(); - this.pending_requests += 1; - defer this.pending_requests -= 1; - req.setYield(false); - if (req.header("open-in-editor") == null) { - resp.writeStatus("501 Not Implemented"); - resp.end("Viewing source without opening in editor is not implemented yet!", false); - return; - } - - var ctx = &JSC.VirtualMachine.vm.rareData().editor_context; - ctx.autoDetectEditor(JSC.VirtualMachine.vm.bundler.env); - var line: ?string = req.header("editor-line"); - var column: ?string = req.header("editor-column"); - - if (ctx.editor) |editor| { - resp.writeStatus("200 Opened"); - resp.end("Opened in editor", false); - var url = req.url()["/src:".len..]; - if (strings.indexOfChar(url, ':')) |colon| { - url = url[0..colon]; - } - editor.open(ctx.path, url, line, column, this.allocator) catch Output.prettyErrorln("Failed to open editor", .{}); - } else { - resp.writeStatus("500 Missing Editor :("); - resp.end("Please set your editor in bunfig.toml", false); - } - } - - pub fn onRequest(this: *ThisServer, req: *uws.Request, resp: *App.Response) void { - JSC.markBinding(); - this.pending_requests += 1; - var vm = this.vm; - req.setYield(false); - var ctx = this.request_pool_allocator.create(RequestContext) catch @panic("ran out of memory"); - ctx.create(this, req, resp); - - var request_object = this.allocator.create(JSC.WebCore.Request) catch unreachable; - request_object.* = .{ - .url = JSC.ZigString.init(ctx.url), - .method = ctx.method, - .uws_request = req, - .body = .{ - .Locked = .{ - .task = ctx, - .global = this.globalThis, - .onPull = RequestContext.onPullCallback, - }, - }, - }; - request_object.url.mark(); - // We keep the Request object alive for the duration of the request so that we can remove the pointer to the UWS request object. - var args = [_]JSC.C.JSValueRef{JSC.WebCore.Request.Class.make(this.globalThis.ref(), request_object)}; - ctx.request_js_object = args[0]; - JSC.C.JSValueProtect(this.globalThis.ref(), args[0]); - const response_value = JSC.C.JSObjectCallAsFunctionReturnValue(this.globalThis.ref(), this.config.onRequest.asObjectRef(), this.thisObject.asObjectRef(), 1, &args); - - if (ctx.aborted) { - ctx.finalizeForAbort(); - return; - } - if (response_value.isEmptyOrUndefinedOrNull() and !ctx.resp.hasResponded()) { - ctx.renderMissing(); - return; - } - - if (response_value.isError() or response_value.isAggregateError(this.globalThis) or response_value.isException(this.globalThis.vm())) { - ctx.runErrorHandler(response_value); - return; - } - - if (response_value.as(JSC.WebCore.Response)) |response| { - JSC.C.JSValueProtect(this.globalThis.ref(), response_value.asObjectRef()); - ctx.response_jsvalue = response_value; - - ctx.render(response); - return; - } - - var wait_for_promise = false; - - if (response_value.asPromise()) |promise| { - // If we immediately have the value available, we can skip the extra event loop tick - switch (promise.status(vm.global.vm())) { - .Pending => {}, - .Fulfilled => { - ctx.handleResolve(promise.result(vm.global.vm())); - return; - }, - .Rejected => { - ctx.handleReject(promise.result(vm.global.vm())); - return; - }, - } - wait_for_promise = true; - // I don't think this case should happen - // But I'm uncertain - } else if (response_value.asInternalPromise()) |promise| { - switch (promise.status(vm.global.vm())) { - .Pending => {}, - .Fulfilled => { - ctx.handleResolve(promise.result(vm.global.vm())); - return; - }, - .Rejected => { - ctx.handleReject(promise.result(vm.global.vm())); - return; - }, - } - wait_for_promise = true; - } - - if (wait_for_promise) { - ctx.setAbortHandler(); - - RequestContext.PromiseHandler.then(ctx, response_value, this.globalThis); - return; - } - - // The user returned something that wasn't a promise or a promise with a response - if (!ctx.resp.hasResponded()) ctx.renderMissing(); - } - - pub fn listen(this: *ThisServer) void { - if (ssl_enabled) { - BoringSSL.load(); - const ssl_config = this.config.ssl_config orelse @panic("Assertion failure: ssl_config"); - this.app = App.create(.{ - .key_file_name = ssl_config.key_file_name, - .cert_file_name = ssl_config.cert_file_name, - .passphrase = ssl_config.passphrase, - .dh_params_file_name = ssl_config.dh_params_file_name, - .ca_file_name = ssl_config.ca_file_name, - .ssl_prefer_low_memory_usage = @as(c_int, @boolToInt(ssl_config.low_memory_mode)), - }); - - if (ssl_config.server_name != null and std.mem.span(ssl_config.server_name).len > 0) { - this.app.addServerName(ssl_config.server_name); - } - } else { - this.app = App.create(.{}); - } - - this.app.any("/*", *ThisServer, this, onRequest); - - if (comptime debug_mode) { - this.app.get("/bun:info", *ThisServer, this, onBunInfoRequest); - this.app.get("/src:/*", *ThisServer, this, onSrcRequest); - } - - this.app.listenWithConfig(*ThisServer, this, onListen, .{ - .port = this.config.port, - .host = this.config.hostname, - .options = 0, - }); - } - }; -} - -pub const Server = NewServer(false, false); -pub const SSLServer = NewServer(true, false); -pub const DebugServer = NewServer(false, true); -pub const DebugSSLServer = NewServer(true, true); diff --git a/src/javascript/jsc/api/transpiler.zig b/src/javascript/jsc/api/transpiler.zig deleted file mode 100644 index 69732c643..000000000 --- a/src/javascript/jsc/api/transpiler.zig +++ /dev/null @@ -1,1304 +0,0 @@ -const std = @import("std"); -const Api = @import("../../../api/schema.zig").Api; -const FilesystemRouter = @import("../../../router.zig"); -const http = @import("../../../http.zig"); -const JavaScript = @import("../javascript.zig"); -const QueryStringMap = @import("../../../url.zig").QueryStringMap; -const CombinedScanner = @import("../../../url.zig").CombinedScanner; -const bun = @import("../../../global.zig"); -const string = bun.string; -const JSC = @import("../../../jsc.zig"); -const js = JSC.C; -const WebCore = @import("../webcore/response.zig"); -const Bundler = @import("../../../bundler.zig"); -const options = @import("../../../options.zig"); -const VirtualMachine = JavaScript.VirtualMachine; -const ScriptSrcStream = std.io.FixedBufferStream([]u8); -const ZigString = JSC.ZigString; -const Fs = @import("../../../fs.zig"); -const Base = @import("../base.zig"); -const getAllocator = Base.getAllocator; -const JSObject = JSC.JSObject; -const JSError = Base.JSError; -const JSValue = JSC.JSValue; -const JSGlobalObject = JSC.JSGlobalObject; -const strings = @import("strings"); -const NewClass = Base.NewClass; -const To = Base.To; -const Request = WebCore.Request; -const d = Base.d; -const FetchEvent = WebCore.FetchEvent; -const MacroMap = @import("../../../resolver/package_json.zig").MacroMap; -const TSConfigJSON = @import("../../../resolver/tsconfig_json.zig").TSConfigJSON; -const PackageJSON = @import("../../../resolver/package_json.zig").PackageJSON; -const logger = @import("../../../logger.zig"); -const Loader = options.Loader; -const Platform = options.Platform; -const JSAst = @import("../../../js_ast.zig"); -const Transpiler = @This(); -const JSParser = @import("../../../js_parser.zig"); -const JSPrinter = @import("../../../js_printer.zig"); -const ScanPassResult = JSParser.ScanPassResult; -const Mimalloc = @import("../../../mimalloc_arena.zig"); -const Runtime = @import("../../../runtime.zig").Runtime; -const JSLexer = @import("../../../js_lexer.zig"); -const Expr = JSAst.Expr; - -bundler: Bundler.Bundler, -arena: std.heap.ArenaAllocator, -transpiler_options: TranspilerOptions, -scan_pass_result: ScanPassResult, -buffer_writer: ?JSPrinter.BufferWriter = null, - -pub const Class = NewClass( - Transpiler, - .{ .name = "Transpiler" }, - .{ - .scanImports = .{ - .rfn = scanImports, - }, - .scan = .{ - .rfn = scan, - }, - .transform = .{ - .rfn = transform, - }, - .transformSync = .{ - .rfn = transformSync, - }, - // .resolve = .{ - // .rfn = resolve, - // }, - // .buildSync = .{ - // .rfn = buildSync, - // }, - .finalize = finalize, - }, - .{}, -); - -pub const Constructor = JSC.NewConstructor( - @This(), - .{ - .constructor = .{ .rfn = constructor }, - }, - .{}, -); - -const default_transform_options: Api.TransformOptions = brk: { - var opts = std.mem.zeroes(Api.TransformOptions); - opts.disable_hmr = true; - opts.platform = Api.Platform.browser; - opts.serve = false; - break :brk opts; -}; - -const TranspilerOptions = struct { - transform: Api.TransformOptions = default_transform_options, - default_loader: options.Loader = options.Loader.jsx, - macro_map: MacroMap = MacroMap{}, - tsconfig: ?*TSConfigJSON = null, - tsconfig_buf: []const u8 = "", - macros_buf: []const u8 = "", - log: logger.Log, - runtime: Runtime.Features = Runtime.Features{ .top_level_await = true }, - tree_shaking: bool = false, - trim_unused_imports: ?bool = null, -}; - -// Mimalloc gets unstable if we try to move this to a different thread -// threadlocal var transform_buffer: bun.MutableString = undefined; -// threadlocal var transform_buffer_loaded: bool = false; - -// This is going to be hard to not leak -pub const TransformTask = struct { - input_code: ZigString = ZigString.init(""), - protected_input_value: JSC.JSValue = @intToEnum(JSC.JSValue, 0), - output_code: ZigString = ZigString.init(""), - bundler: Bundler.Bundler = undefined, - log: logger.Log, - err: ?anyerror = null, - macro_map: MacroMap = MacroMap{}, - tsconfig: ?*TSConfigJSON = null, - loader: Loader, - global: *JSGlobalObject, - replace_exports: Runtime.Features.ReplaceableExport.Map = .{}, - - pub const AsyncTransformTask = JSC.ConcurrentPromiseTask(TransformTask); - pub const AsyncTransformEventLoopTask = AsyncTransformTask.EventLoopTask; - - pub fn create(transpiler: *Transpiler, protected_input_value: JSC.C.JSValueRef, globalThis: *JSGlobalObject, input_code: ZigString, loader: Loader) !*AsyncTransformTask { - var transform_task = try bun.default_allocator.create(TransformTask); - transform_task.* = .{ - .input_code = input_code, - .protected_input_value = if (protected_input_value != null) JSC.JSValue.fromRef(protected_input_value) else @intToEnum(JSC.JSValue, 0), - .bundler = undefined, - .global = globalThis, - .macro_map = transpiler.transpiler_options.macro_map, - .tsconfig = transpiler.transpiler_options.tsconfig, - .log = logger.Log.init(bun.default_allocator), - .loader = loader, - .replace_exports = transpiler.transpiler_options.runtime.replace_exports, - }; - transform_task.bundler = transpiler.bundler; - transform_task.bundler.linker.resolver = &transform_task.bundler.resolver; - - transform_task.bundler.setLog(&transform_task.log); - transform_task.bundler.setAllocator(bun.default_allocator); - return try AsyncTransformTask.createOnJSThread(bun.default_allocator, globalThis, transform_task); - } - - pub fn run(this: *TransformTask) void { - const name = this.loader.stdinName(); - const source = logger.Source.initPathString(name, this.input_code.slice()); - - JSAst.Stmt.Data.Store.create(bun.default_allocator); - JSAst.Expr.Data.Store.create(bun.default_allocator); - - var arena = Mimalloc.Arena.init() catch unreachable; - - const allocator = arena.allocator(); - - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - arena.deinit(); - } - - this.bundler.setAllocator(allocator); - const jsx = if (this.tsconfig != null) - this.tsconfig.?.mergeJSX(this.bundler.options.jsx) - else - this.bundler.options.jsx; - - const parse_options = Bundler.Bundler.ParseOptions{ - .allocator = allocator, - .macro_remappings = this.macro_map, - .dirname_fd = 0, - .file_descriptor = null, - .loader = this.loader, - .jsx = jsx, - .path = source.path, - .virtual_source = &source, - .replace_exports = this.replace_exports, - // .allocator = this. - }; - - const parse_result = this.bundler.parse(parse_options, null) orelse { - this.err = error.ParseError; - return; - }; - - if (parse_result.empty) { - this.output_code = ZigString.init(""); - return; - } - - var global_allocator = arena.backingAllocator(); - var buffer_writer = JSPrinter.BufferWriter.init(global_allocator) catch |err| { - this.err = err; - return; - }; - buffer_writer.buffer.list.ensureTotalCapacity(global_allocator, 512) catch unreachable; - buffer_writer.reset(); - - // defer { - // transform_buffer = buffer_writer.buffer; - // } - - var printer = JSPrinter.BufferPrinter.init(buffer_writer); - const printed = this.bundler.print(parse_result, @TypeOf(&printer), &printer, .esm_ascii) catch |err| { - this.err = err; - return; - }; - - if (printed > 0) { - buffer_writer = printer.ctx; - buffer_writer.buffer.list.items = buffer_writer.written; - - var output = JSC.ZigString.init(buffer_writer.written); - output.mark(); - this.output_code = output; - } else { - this.output_code = ZigString.init(""); - } - } - - pub fn then(this: *TransformTask, promise: *JSC.JSInternalPromise) void { - if (this.log.hasAny() or this.err != null) { - const error_value: JSValue = brk: { - if (this.err) |err| { - if (!this.log.hasAny()) { - break :brk JSC.JSValue.fromRef(JSC.BuildError.create( - this.global, - bun.default_allocator, - logger.Msg{ - .data = logger.Data{ .text = std.mem.span(@errorName(err)) }, - }, - )); - } - } - - break :brk this.log.toJS(this.global, bun.default_allocator, "Transform failed"); - }; - - promise.reject(this.global, error_value); - return; - } - - finish(this.output_code, this.global, promise); - - if (@enumToInt(this.protected_input_value) != 0) { - this.protected_input_value = @intToEnum(JSC.JSValue, 0); - } - this.deinit(); - } - - noinline fn finish(code: ZigString, global: *JSGlobalObject, promise: *JSC.JSInternalPromise) void { - promise.resolve(global, code.toValueGC(global)); - } - - pub fn deinit(this: *TransformTask) void { - var should_cleanup = false; - defer if (should_cleanup) bun.Global.mimalloc_cleanup(false); - - this.log.deinit(); - if (this.input_code.isGloballyAllocated()) { - this.input_code.deinitGlobal(); - } - - if (this.output_code.isGloballyAllocated()) { - should_cleanup = this.output_code.len > 512_000; - this.output_code.deinitGlobal(); - } - - bun.default_allocator.destroy(this); - } -}; - -fn exportReplacementValue(value: JSValue, globalThis: *JSGlobalObject) ?JSAst.Expr { - if (value.isBoolean()) { - return Expr{ - .data = .{ - .e_boolean = .{ - .value = value.toBoolean(), - }, - }, - .loc = logger.Loc.Empty, - }; - } - - if (value.isNumber()) { - return Expr{ - .data = .{ - .e_number = .{ .value = value.asNumber() }, - }, - .loc = logger.Loc.Empty, - }; - } - - if (value.isNull()) { - return Expr{ - .data = .{ - .e_null = .{}, - }, - .loc = logger.Loc.Empty, - }; - } - - if (value.isUndefined()) { - return Expr{ - .data = .{ - .e_undefined = .{}, - }, - .loc = logger.Loc.Empty, - }; - } - - if (value.isString()) { - var str = JSAst.E.String{ - .data = std.fmt.allocPrint(bun.default_allocator, "{}", .{value.getZigString(globalThis)}) catch unreachable, - }; - var out = bun.default_allocator.create(JSAst.E.String) catch unreachable; - out.* = str; - return Expr{ - .data = .{ - .e_string = out, - }, - .loc = logger.Loc.Empty, - }; - } - - return null; -} - -fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allocator, args: *JSC.Node.ArgumentsSlice, exception: JSC.C.ExceptionRef) !TranspilerOptions { - var globalThis = ctx.ptr(); - const object = args.next() orelse return TranspilerOptions{ .log = logger.Log.init(temp_allocator) }; - if (object.isUndefinedOrNull()) return TranspilerOptions{ .log = logger.Log.init(temp_allocator) }; - - args.eat(); - var allocator = args.arena.allocator(); - - var transpiler = TranspilerOptions{ - .default_loader = .jsx, - .transform = default_transform_options, - .log = logger.Log.init(allocator), - }; - transpiler.log.level = .warn; - - if (!object.isObject()) { - JSC.throwInvalidArguments("Expected an object", .{}, ctx, exception); - return transpiler; - } - - if (object.getIfPropertyExists(ctx.ptr(), "define")) |define| { - define: { - if (define.isUndefinedOrNull()) { - break :define; - } - - if (!define.isObject()) { - JSC.throwInvalidArguments("define must be an object", .{}, ctx, exception); - return transpiler; - } - - var array = JSC.C.JSObjectCopyPropertyNames(globalThis.ref(), define.asObjectRef()); - defer JSC.C.JSPropertyNameArrayRelease(array); - const count = JSC.C.JSPropertyNameArrayGetCount(array); - // cannot be a temporary because it may be loaded on different threads. - var map_entries = allocator.alloc([]u8, count * 2) catch unreachable; - var names = map_entries[0..count]; - - var values = map_entries[count..]; - - var i: usize = 0; - while (i < count) : (i += 1) { - var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex( - array, - i, - ); - defer JSC.C.JSStringRelease(property_name_ref); - const prop: []const u8 = JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..JSC.C.JSStringGetLength(property_name_ref)]; - const property_value: JSC.JSValue = JSC.JSValue.fromRef( - JSC.C.JSObjectGetProperty( - globalThis.ref(), - define.asObjectRef(), - property_name_ref, - null, - ), - ); - const value_type = property_value.jsType(); - - if (!value_type.isStringLike()) { - JSC.throwInvalidArguments("define \"{s}\" must be a JSON string", .{prop}, ctx, exception); - return transpiler; - } - names[i] = allocator.dupe(u8, prop) catch unreachable; - var val = JSC.ZigString.init(""); - property_value.toZigString(&val, globalThis); - if (val.len == 0) { - val = JSC.ZigString.init("\"\""); - } - values[i] = std.fmt.allocPrint(allocator, "{}", .{val}) catch unreachable; - } - transpiler.transform.define = Api.StringMap{ - .keys = names, - .values = values, - }; - } - } - - if (object.get(globalThis, "external")) |external| { - external: { - if (external.isUndefinedOrNull()) break :external; - - const toplevel_type = external.jsType(); - if (toplevel_type.isStringLike()) { - var zig_str = JSC.ZigString.init(""); - external.toZigString(&zig_str, globalThis); - if (zig_str.len == 0) break :external; - var single_external = allocator.alloc(string, 1) catch unreachable; - single_external[0] = std.fmt.allocPrint(allocator, "{}", .{external}) catch unreachable; - transpiler.transform.external = single_external; - } else if (toplevel_type.isArray()) { - const count = external.getLengthOfArray(globalThis); - if (count == 0) break :external; - - var externals = allocator.alloc(string, count) catch unreachable; - var iter = external.arrayIterator(globalThis); - var i: usize = 0; - while (iter.next()) |entry| { - if (!entry.jsType().isStringLike()) { - JSC.throwInvalidArguments("external must be a string or string[]", .{}, ctx, exception); - return transpiler; - } - - var zig_str = JSC.ZigString.init(""); - entry.toZigString(&zig_str, globalThis); - if (zig_str.len == 0) continue; - externals[i] = std.fmt.allocPrint(allocator, "{}", .{external}) catch unreachable; - i += 1; - } - - transpiler.transform.external = externals[0..i]; - } else { - JSC.throwInvalidArguments("external must be a string or string[]", .{}, ctx, exception); - return transpiler; - } - } - } - - if (object.get(globalThis, "loader")) |loader| { - if (Loader.fromJS(globalThis, loader, exception)) |resolved| { - if (!resolved.isJavaScriptLike()) { - JSC.throwInvalidArguments("only JavaScript-like loaders supported for now", .{}, ctx, exception); - return transpiler; - } - - transpiler.default_loader = resolved; - } - - if (exception.* != null) { - return transpiler; - } - } - - if (object.get(globalThis, "platform")) |platform| { - if (Platform.fromJS(globalThis, platform, exception)) |resolved| { - transpiler.transform.platform = resolved.toAPI(); - } - - if (exception.* != null) { - return transpiler; - } - } - - if (object.get(globalThis, "tsconfig")) |tsconfig| { - tsconfig: { - if (tsconfig.isUndefinedOrNull()) break :tsconfig; - const kind = tsconfig.jsType(); - var out = JSC.ZigString.init(""); - - if (kind.isArray()) { - JSC.throwInvalidArguments("tsconfig must be a string or object", .{}, ctx, exception); - return transpiler; - } - - if (!kind.isStringLike()) { - tsconfig.jsonStringify(globalThis, 0, &out); - } else { - tsconfig.toZigString(&out, globalThis); - } - - if (out.len == 0) break :tsconfig; - transpiler.tsconfig_buf = std.fmt.allocPrint(allocator, "{}", .{out}) catch unreachable; - - // TODO: JSC -> Ast conversion - if (TSConfigJSON.parse( - allocator, - &transpiler.log, - logger.Source.initPathString("tsconfig.json", transpiler.tsconfig_buf), - &VirtualMachine.vm.bundler.resolver.caches.json, - true, - ) catch null) |parsed_tsconfig| { - transpiler.tsconfig = parsed_tsconfig; - } - } - } - - transpiler.runtime.allow_runtime = false; - - if (object.getIfPropertyExists(globalThis, "macro")) |macros| { - macros: { - if (macros.isUndefinedOrNull()) break :macros; - const kind = macros.jsType(); - const is_object = kind.isObject(); - if (!(kind.isStringLike() or is_object)) { - JSC.throwInvalidArguments("macro must be an object", .{}, ctx, exception); - return transpiler; - } - - var out: ZigString = ZigString.init(""); - // TODO: write a converter between JSC types and Bun AST types - if (is_object) { - macros.jsonStringify(globalThis, 0, &out); - } else { - macros.toZigString(&out, globalThis); - } - - if (out.len == 0) break :macros; - transpiler.macros_buf = std.fmt.allocPrint(allocator, "{}", .{out}) catch unreachable; - const source = logger.Source.initPathString("macros.json", transpiler.macros_buf); - const json = (VirtualMachine.vm.bundler.resolver.caches.json.parseJSON( - &transpiler.log, - source, - allocator, - ) catch null) orelse break :macros; - transpiler.macro_map = PackageJSON.parseMacrosJSON(allocator, json, &transpiler.log, &source); - } - } - - if (object.get(globalThis, "autoImportJSX")) |flag| { - transpiler.runtime.auto_import_jsx = flag.toBoolean(); - } - - if (object.get(globalThis, "allowBunRuntime")) |flag| { - transpiler.runtime.allow_runtime = flag.toBoolean(); - } - - if (object.get(globalThis, "jsxOptimizationInline")) |flag| { - transpiler.runtime.jsx_optimization_inline = flag.toBoolean(); - } - - if (object.get(globalThis, "jsxOptimizationHoist")) |flag| { - transpiler.runtime.jsx_optimization_hoist = flag.toBoolean(); - - if (!transpiler.runtime.jsx_optimization_inline and transpiler.runtime.jsx_optimization_hoist) { - JSC.throwInvalidArguments("jsxOptimizationHoist requires jsxOptimizationInline", .{}, ctx, exception); - return transpiler; - } - } - - if (object.get(globalThis, "sourcemap")) |flag| { - if (flag.isBoolean() or flag.isUndefinedOrNull()) { - if (flag.toBoolean()) { - transpiler.transform.source_map = Api.SourceMapMode.external; - } else { - transpiler.transform.source_map = Api.SourceMapMode.inline_into_file; - } - } else { - var sourcemap = flag.toSlice(globalThis, allocator); - if (options.SourceMapOption.map.get(sourcemap.slice())) |source| { - transpiler.transform.source_map = source.toAPI(); - } else { - JSC.throwInvalidArguments("sourcemap must be one of \"inline\", \"external\", or \"none\"", .{}, ctx, exception); - return transpiler; - } - } - } - - var tree_shaking: ?bool = null; - if (object.get(globalThis, "treeShaking")) |treeShaking| { - tree_shaking = treeShaking.toBoolean(); - } - - var trim_unused_imports: ?bool = null; - if (object.get(globalThis, "trimUnusedImports")) |trimUnusedImports| { - trim_unused_imports = trimUnusedImports.toBoolean(); - } - - if (object.getTruthy(globalThis, "exports")) |exports| { - if (!exports.isObject()) { - JSC.throwInvalidArguments("exports must be an object", .{}, ctx, exception); - return transpiler; - } - - var replacements = Runtime.Features.ReplaceableExport.Map{}; - errdefer replacements.clearAndFree(bun.default_allocator); - - if (exports.getTruthy(globalThis, "eliminate")) |eliminate| { - if (!eliminate.jsType().isArray()) { - JSC.throwInvalidArguments("exports.eliminate must be an array", .{}, ctx, exception); - return transpiler; - } - - var total_name_buf_len: u32 = 0; - var string_count: u32 = 0; - var iter = JSC.JSArrayIterator.init(eliminate, globalThis); - { - var length_iter = iter; - while (length_iter.next()) |value| { - if (value.isString()) { - const length = value.getLengthOfArray(globalThis); - string_count += @as(u32, @boolToInt(length > 0)); - total_name_buf_len += length; - } - } - } - - if (total_name_buf_len > 0) { - var buf = try std.ArrayListUnmanaged(u8).initCapacity(bun.default_allocator, total_name_buf_len); - try replacements.ensureUnusedCapacity(bun.default_allocator, string_count); - { - var length_iter = iter; - while (length_iter.next()) |value| { - if (!value.isString()) continue; - var str = value.getZigString(globalThis); - if (str.len == 0) continue; - const name = std.fmt.bufPrint(buf.items.ptr[buf.items.len..buf.capacity], "{}", .{str}) catch { - JSC.throwInvalidArguments("Error reading exports.eliminate. TODO: utf-16", .{}, ctx, exception); - return transpiler; - }; - buf.items.len += name.len; - if (name.len > 0) { - replacements.putAssumeCapacity(name, .{ .delete = .{} }); - } - } - } - } - } - - if (exports.getTruthy(globalThis, "replace")) |replace| { - if (!replace.isObject()) { - JSC.throwInvalidArguments("replace must be an object", .{}, ctx, exception); - return transpiler; - } - - var total_name_buf_len: usize = 0; - - var array = js.JSObjectCopyPropertyNames(ctx, replace.asObjectRef()); - defer js.JSPropertyNameArrayRelease(array); - const property_names_count = @intCast(u32, js.JSPropertyNameArrayGetCount(array)); - var iter = JSC.JSPropertyNameIterator{ - .array = array, - .count = @intCast(u32, property_names_count), - }; - - { - var key_iter = iter; - while (key_iter.next()) |item| { - total_name_buf_len += JSC.C.JSStringGetLength(item); - } - } - - if (total_name_buf_len > 0) { - var total_name_buf = try std.ArrayList(u8).initCapacity(bun.default_allocator, total_name_buf_len); - errdefer total_name_buf.clearAndFree(); - - try replacements.ensureUnusedCapacity(bun.default_allocator, property_names_count); - defer { - if (exception.* != null) { - total_name_buf.clearAndFree(); - replacements.clearAndFree(bun.default_allocator); - } - } - - while (iter.next()) |item| { - const start = total_name_buf.items.len; - total_name_buf.items.len += @maximum( - // this returns a null terminated string - JSC.C.JSStringGetUTF8CString(item, total_name_buf.items.ptr + start, total_name_buf.capacity - start), - 1, - ) - 1; - JSC.C.JSStringRelease(item); - const key = total_name_buf.items[start..total_name_buf.items.len]; - // if somehow the string is empty, skip it - if (key.len == 0) - continue; - - const value = replace.get(globalThis, key).?; - if (value.isEmpty()) continue; - - if (!JSLexer.isIdentifier(key)) { - JSC.throwInvalidArguments("\"{s}\" is not a valid ECMAScript identifier", .{key}, ctx, exception); - total_name_buf.deinit(); - return transpiler; - } - - var entry = replacements.getOrPutAssumeCapacity(key); - - if (exportReplacementValue(value, globalThis)) |expr| { - entry.value_ptr.* = .{ .replace = expr }; - continue; - } - - if (value.isObject() and value.getLengthOfArray(ctx.ptr()) == 2) { - const replacementValue = JSC.JSObject.getIndex(value, globalThis, 1); - if (exportReplacementValue(replacementValue, globalThis)) |to_replace| { - const replacementKey = JSC.JSObject.getIndex(value, globalThis, 0); - var slice = (try replacementKey.toSlice(globalThis, bun.default_allocator).cloneIfNeeded()); - var replacement_name = slice.slice(); - - if (!JSLexer.isIdentifier(replacement_name)) { - JSC.throwInvalidArguments("\"{s}\" is not a valid ECMAScript identifier", .{replacement_name}, ctx, exception); - total_name_buf.deinit(); - slice.deinit(); - return transpiler; - } - - entry.value_ptr.* = .{ - .inject = .{ - .name = replacement_name, - .value = to_replace, - }, - }; - continue; - } - } - - JSC.throwInvalidArguments("exports.replace values can only be string, null, undefined, number or boolean", .{}, ctx, exception); - return transpiler; - } - } - } - - tree_shaking = tree_shaking orelse (replacements.count() > 0); - transpiler.runtime.replace_exports = replacements; - } - - transpiler.tree_shaking = tree_shaking orelse false; - transpiler.trim_unused_imports = trim_unused_imports orelse transpiler.tree_shaking; - - return transpiler; -} - -pub fn constructor( - ctx: js.JSContextRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) js.JSObjectRef { - var temp = std.heap.ArenaAllocator.init(getAllocator(ctx)); - var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); - defer temp.deinit(); - const transpiler_options: TranspilerOptions = if (arguments.len > 0) - transformOptionsFromJSC(ctx, temp.allocator(), &args, exception) catch { - JSC.throwInvalidArguments("Failed to create transpiler", .{}, ctx, exception); - return null; - } - else - TranspilerOptions{ .log = logger.Log.init(getAllocator(ctx)) }; - - if (exception.* != null) { - return null; - } - - if ((transpiler_options.log.warnings + transpiler_options.log.errors) > 0) { - var out_exception = transpiler_options.log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to create transpiler"); - exception.* = out_exception.asObjectRef(); - return null; - } - - var log = getAllocator(ctx).create(logger.Log) catch unreachable; - log.* = transpiler_options.log; - var bundler = Bundler.Bundler.init( - getAllocator(ctx), - log, - transpiler_options.transform, - null, - JavaScript.VirtualMachine.vm.bundler.env, - ) catch |err| { - if ((log.warnings + log.errors) > 0) { - var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to create transpiler"); - exception.* = out_exception.asObjectRef(); - return null; - } - - JSC.throwInvalidArguments("Error creating transpiler: {s}", .{@errorName(err)}, ctx, exception); - return null; - }; - - bundler.configureLinkerWithAutoJSX(false); - bundler.options.env.behavior = .disable; - bundler.configureDefines() catch |err| { - if ((log.warnings + log.errors) > 0) { - var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to load define"); - exception.* = out_exception.asObjectRef(); - return null; - } - - JSC.throwInvalidArguments("Failed to load define: {s}", .{@errorName(err)}, ctx, exception); - return null; - }; - - if (transpiler_options.macro_map.count() > 0) { - bundler.options.macro_remap = transpiler_options.macro_map; - } - - bundler.options.tree_shaking = transpiler_options.tree_shaking; - bundler.options.trim_unused_imports = transpiler_options.trim_unused_imports; - bundler.options.allow_runtime = transpiler_options.runtime.allow_runtime; - bundler.options.auto_import_jsx = transpiler_options.runtime.auto_import_jsx; - bundler.options.hot_module_reloading = transpiler_options.runtime.hot_module_reloading; - bundler.options.jsx.supports_fast_refresh = bundler.options.hot_module_reloading and - bundler.options.allow_runtime and transpiler_options.runtime.react_fast_refresh; - - var transpiler = getAllocator(ctx).create(Transpiler) catch unreachable; - transpiler.* = Transpiler{ - .transpiler_options = transpiler_options, - .bundler = bundler, - .arena = args.arena, - .scan_pass_result = ScanPassResult.init(getAllocator(ctx)), - }; - - return Class.make(ctx, transpiler); -} - -pub fn finalize( - this: *Transpiler, -) void { - this.bundler.log.deinit(); - this.scan_pass_result.named_imports.deinit(); - this.scan_pass_result.import_records.deinit(); - this.scan_pass_result.used_symbols.deinit(); - if (this.buffer_writer != null) { - this.buffer_writer.?.buffer.deinit(); - } - - // bun.default_allocator.free(this.transpiler_options.tsconfig_buf); - // bun.default_allocator.free(this.transpiler_options.macros_buf); - this.arena.deinit(); -} - -fn getParseResult(this: *Transpiler, allocator: std.mem.Allocator, code: []const u8, loader: ?Loader, macro_js_ctx: JSValue) ?Bundler.ParseResult { - const name = this.transpiler_options.default_loader.stdinName(); - const source = logger.Source.initPathString(name, code); - - const jsx = if (this.transpiler_options.tsconfig != null) - this.transpiler_options.tsconfig.?.mergeJSX(this.bundler.options.jsx) - else - this.bundler.options.jsx; - - const parse_options = Bundler.Bundler.ParseOptions{ - .allocator = allocator, - .macro_remappings = this.transpiler_options.macro_map, - .dirname_fd = 0, - .file_descriptor = null, - .loader = loader orelse this.transpiler_options.default_loader, - .jsx = jsx, - .path = source.path, - .virtual_source = &source, - .replace_exports = this.transpiler_options.runtime.replace_exports, - .macro_js_ctx = macro_js_ctx, - // .allocator = this. - }; - - var parse_result = this.bundler.parse(parse_options, null); - - // necessary because we don't run the linker - if (parse_result) |*res| { - for (res.ast.import_records) |*import| { - if (import.kind.isCommonJS()) { - import.wrap_with_to_module = true; - import.module_id = @truncate(u32, std.hash.Wyhash.hash(0, import.path.pretty)); - } - } - } - - return parse_result; -} - -pub fn scan( - this: *Transpiler, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); - defer args.arena.deinit(); - const code_arg = args.next() orelse { - JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - - const code_holder = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), args.arena.allocator(), code_arg, exception) orelse { - if (exception.* == null) JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - - const code = code_holder.slice(); - args.eat(); - const loader: ?Loader = brk: { - if (args.next()) |arg| { - args.eat(); - break :brk Loader.fromJS(ctx.ptr(), arg, exception); - } - - break :brk null; - }; - - if (exception.* != null) return null; - - var arena = Mimalloc.Arena.init() catch unreachable; - var prev_allocator = this.bundler.allocator; - this.bundler.setAllocator(arena.allocator()); - var log = logger.Log.init(arena.backingAllocator()); - defer log.deinit(); - this.bundler.setLog(&log); - defer { - this.bundler.setLog(&this.transpiler_options.log); - this.bundler.setAllocator(prev_allocator); - arena.deinit(); - } - - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - } - - const parse_result = getParseResult(this, arena.allocator(), code, loader, JSC.JSValue.zero) orelse { - if ((this.bundler.log.warnings + this.bundler.log.errors) > 0) { - var out_exception = this.bundler.log.toJS(ctx.ptr(), getAllocator(ctx), "Parse error"); - exception.* = out_exception.asObjectRef(); - return null; - } - - JSC.throwInvalidArguments("Failed to parse", .{}, ctx, exception); - return null; - }; - - if ((this.bundler.log.warnings + this.bundler.log.errors) > 0) { - var out_exception = this.bundler.log.toJS(ctx.ptr(), getAllocator(ctx), "Parse error"); - exception.* = out_exception.asObjectRef(); - return null; - } - - const exports_label = JSC.ZigString.init("exports"); - const imports_label = JSC.ZigString.init("imports"); - const named_imports_value = namedImportsToJS( - ctx.ptr(), - parse_result.ast.import_records, - exception, - ); - if (exception.* != null) return null; - var named_exports_value = namedExportsToJS( - ctx.ptr(), - parse_result.ast.named_exports, - ); - return JSC.JSValue.createObject2(ctx.ptr(), &imports_label, &exports_label, named_imports_value, named_exports_value).asObjectRef(); -} - -// pub fn build( -// this: *Transpiler, -// ctx: js.JSContextRef, -// _: js.JSObjectRef, -// _: js.JSObjectRef, -// arguments: []const js.JSValueRef, -// exception: js.ExceptionRef, -// ) JSC.C.JSObjectRef {} - -pub fn transform( - this: *Transpiler, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); - defer args.arena.deinit(); - const code_arg = args.next() orelse { - JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - - const code_holder = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), this.arena.allocator(), code_arg, exception) orelse { - if (exception.* == null) JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - - const code = code_holder.slice(); - args.eat(); - const loader: ?Loader = brk: { - if (args.next()) |arg| { - args.eat(); - break :brk Loader.fromJS(ctx.ptr(), arg, exception); - } - - break :brk null; - }; - - if (exception.* != null) return null; - if (code_holder == .string) { - JSC.C.JSValueProtect(ctx, arguments[0]); - } - - var task = TransformTask.create(this, if (code_holder == .string) arguments[0] else null, ctx.ptr(), ZigString.init(code), loader orelse this.transpiler_options.default_loader) catch return null; - task.schedule(); - return task.promise.asObjectRef(); -} - -pub fn transformSync( - this: *Transpiler, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); - defer args.arena.deinit(); - const code_arg = args.next() orelse { - JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - - var arena = Mimalloc.Arena.init() catch unreachable; - defer arena.deinit(); - const code_holder = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), arena.allocator(), code_arg, exception) orelse { - if (exception.* == null) JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - - const code = code_holder.slice(); - JSC.JSValue.c(arguments[0]).ensureStillAlive(); - defer JSC.JSValue.c(arguments[0]).ensureStillAlive(); - - args.eat(); - var js_ctx_value: JSC.JSValue = JSC.JSValue.zero; - const loader: ?Loader = brk: { - if (args.next()) |arg| { - args.eat(); - if (arg.isNumber() or arg.isString()) { - break :brk Loader.fromJS(ctx.ptr(), arg, exception); - } - - if (arg.isObject()) { - js_ctx_value = arg; - break :brk null; - } - } - - break :brk null; - }; - - if (args.nextEat()) |arg| { - if (arg.isObject()) { - js_ctx_value = arg; - } else { - JSC.throwInvalidArguments("Expected a Loader or object", .{}, ctx, exception); - return null; - } - } - if (!js_ctx_value.isEmpty()) { - js_ctx_value.ensureStillAlive(); - } - - defer { - if (!js_ctx_value.isEmpty()) { - js_ctx_value.ensureStillAlive(); - } - } - - if (exception.* != null) return null; - - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - } - - var prev_bundler = this.bundler; - this.bundler.setAllocator(arena.allocator()); - this.bundler.macro_context = null; - var log = logger.Log.init(arena.backingAllocator()); - this.bundler.setLog(&log); - - defer { - this.bundler = prev_bundler; - } - - var parse_result = getParseResult( - this, - arena.allocator(), - code, - loader, - js_ctx_value, - ) orelse { - if ((this.bundler.log.warnings + this.bundler.log.errors) > 0) { - var out_exception = this.bundler.log.toJS(ctx.ptr(), getAllocator(ctx), "Parse error"); - exception.* = out_exception.asObjectRef(); - return null; - } - - JSC.throwInvalidArguments("Failed to parse", .{}, ctx, exception); - return null; - }; - - if ((this.bundler.log.warnings + this.bundler.log.errors) > 0) { - var out_exception = this.bundler.log.toJS(ctx.ptr(), getAllocator(ctx), "Parse error"); - exception.* = out_exception.asObjectRef(); - return null; - } - - var buffer_writer = this.buffer_writer orelse brk: { - var writer = JSPrinter.BufferWriter.init(arena.backingAllocator()) catch { - JSC.throwInvalidArguments("Failed to create BufferWriter", .{}, ctx, exception); - return null; - }; - - writer.buffer.growIfNeeded(code.len) catch unreachable; - writer.buffer.list.expandToCapacity(); - break :brk writer; - }; - - defer { - this.buffer_writer = buffer_writer; - } - - buffer_writer.reset(); - var printer = JSPrinter.BufferPrinter.init(buffer_writer); - _ = this.bundler.print(parse_result, @TypeOf(&printer), &printer, .esm_ascii) catch |err| { - JSC.JSError(bun.default_allocator, "Failed to print code: {s}", .{@errorName(err)}, ctx, exception); - - return null; - }; - - // TODO: benchmark if pooling this way is faster or moving is faster - buffer_writer = printer.ctx; - var out = JSC.ZigString.init(buffer_writer.written); - out.mark(); - - return out.toValueGC(ctx.ptr()).asObjectRef(); -} - -fn namedExportsToJS(global: *JSGlobalObject, named_exports: JSAst.Ast.NamedExports) JSC.JSValue { - if (named_exports.count() == 0) - return JSC.JSValue.fromRef(JSC.C.JSObjectMakeArray(global.ref(), 0, null, null)); - - var named_exports_iter = named_exports.iterator(); - var stack_fallback = std.heap.stackFallback(@sizeOf(JSC.ZigString) * 32, getAllocator(global.ref())); - var allocator = stack_fallback.get(); - var names = allocator.alloc( - JSC.ZigString, - named_exports.count(), - ) catch unreachable; - defer allocator.free(names); - var i: usize = 0; - while (named_exports_iter.next()) |entry| { - names[i] = JSC.ZigString.init(entry.key_ptr.*); - i += 1; - } - JSC.ZigString.sortAsc(names[0..i]); - return JSC.JSValue.createStringArray(global, names.ptr, names.len, true); -} - -const ImportRecord = @import("../../../import_record.zig").ImportRecord; - -fn namedImportsToJS( - global: *JSGlobalObject, - import_records: []const ImportRecord, - exception: JSC.C.ExceptionRef, -) JSC.JSValue { - var stack_fallback = std.heap.stackFallback(@sizeOf(JSC.C.JSObjectRef) * 32, getAllocator(global.ref())); - var allocator = stack_fallback.get(); - - var i: usize = 0; - const path_label = JSC.ZigString.init("path"); - const kind_label = JSC.ZigString.init("kind"); - var array_items = allocator.alloc( - JSC.C.JSValueRef, - import_records.len, - ) catch unreachable; - defer allocator.free(array_items); - - for (import_records) |record| { - if (record.is_internal) continue; - - const path = JSC.ZigString.init(record.path.text).toValueGC(global); - const kind = JSC.ZigString.init(record.kind.label()).toValue(global); - array_items[i] = JSC.JSValue.createObject2(global, &path_label, &kind_label, path, kind).asObjectRef(); - i += 1; - } - - return JSC.JSValue.fromRef(JSC.C.JSObjectMakeArray(global.ref(), i, array_items.ptr, exception)); -} - -pub fn scanImports( - this: *Transpiler, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, -) JSC.C.JSObjectRef { - var args = JSC.Node.ArgumentsSlice.init(ctx.bunVM(), @ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]); - const code_arg = args.next() orelse { - JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - - const code_holder = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), args.arena.allocator(), code_arg, exception) orelse { - if (exception.* == null) JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception); - return null; - }; - args.eat(); - const code = code_holder.slice(); - - var loader: Loader = this.transpiler_options.default_loader; - if (args.next()) |arg| { - if (Loader.fromJS(ctx.ptr(), arg, exception)) |_loader| { - loader = _loader; - } - args.eat(); - } - - if (!loader.isJavaScriptLike()) { - JSC.throwInvalidArguments("Only JavaScript-like files support this fast path", .{}, ctx, exception); - return null; - } - - if (exception.* != null) return null; - - var arena = Mimalloc.Arena.init() catch unreachable; - var prev_allocator = this.bundler.allocator; - this.bundler.setAllocator(arena.allocator()); - var log = logger.Log.init(arena.backingAllocator()); - defer log.deinit(); - this.bundler.setLog(&log); - defer { - this.bundler.setLog(&this.transpiler_options.log); - this.bundler.setAllocator(prev_allocator); - arena.deinit(); - } - - const source = logger.Source.initPathString(loader.stdinName(), code); - var bundler = &this.bundler; - const jsx = if (this.transpiler_options.tsconfig != null) - this.transpiler_options.tsconfig.?.mergeJSX(this.bundler.options.jsx) - else - this.bundler.options.jsx; - - var opts = JSParser.Parser.Options.init(jsx, loader); - if (this.bundler.macro_context == null) { - this.bundler.macro_context = JSAst.Macro.MacroContext.init(&this.bundler); - } - opts.macro_context = &this.bundler.macro_context.?; - - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - } - - bundler.resolver.caches.js.scan( - bundler.allocator, - &this.scan_pass_result, - opts, - bundler.options.define, - &log, - &source, - ) catch |err| { - defer this.scan_pass_result.reset(); - if ((log.warnings + log.errors) > 0) { - var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to scan imports"); - exception.* = out_exception.asObjectRef(); - return null; - } - - JSC.throwInvalidArguments("Failed to scan imports: {s}", .{@errorName(err)}, ctx, exception); - return null; - }; - - defer this.scan_pass_result.reset(); - - if ((log.warnings + log.errors) > 0) { - var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to scan imports"); - exception.* = out_exception.asObjectRef(); - return null; - } - - const named_imports_value = namedImportsToJS( - ctx.ptr(), - this.scan_pass_result.import_records.items, - exception, - ); - if (exception.* != null) return null; - return named_imports_value.asObjectRef(); -} |