diff options
author | 2022-10-25 00:44:25 -0700 | |
---|---|---|
committer | 2022-10-25 00:44:25 -0700 | |
commit | 02c920f4fd09ddc1a32cb2e92c6f391875415949 (patch) | |
tree | 4f524f5ce9d672fadb8740c68cc9b1a8410a714f | |
parent | 1b50ecc52b55df0c00f991c8206d4ced84ad89b8 (diff) | |
download | bun-02c920f4fd09ddc1a32cb2e92c6f391875415949.tar.gz bun-02c920f4fd09ddc1a32cb2e92c6f391875415949.tar.zst bun-02c920f4fd09ddc1a32cb2e92c6f391875415949.zip |
TCP & TLS Socket API (#1374)
* TCP Socket API
* Wip
* Add snippet for StringDecoder
* Rename `close` to `stop`, replace `close` with `end`
* Add a tcp echo server test
* Some docs
* Update README.md
* Fix build
* Update README.md
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
29 files changed, 4337 insertions, 40 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json index c866e2f0c..c3541749a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,8 +8,8 @@ "search.useIgnoreFiles": true, "zig.buildOnSave": false, "[zig]": { - "editor.tabSize": 4, - "editor.useTabStops": false, + "editor.tabSize": 4, + "editor.useTabStops": false, "editor.defaultFormatter": "AugusteRame.zls-vscode", "editor.formatOnSave": true }, @@ -87,6 +87,7 @@ "editor.defaultFormatter": "xaver.clang-format" }, "files.associations": { + "*.lock": "yarnlock", "*.idl": "cpp", "memory": "cpp", "iostream": "cpp", @@ -178,7 +179,8 @@ "ctype.h": "c", "ethernet.h": "c", "inet.h": "c", - "packet.h": "c" + "packet.h": "c", + "queue": "cpp" }, "cmake.configureOnOpen": false } @@ -2574,6 +2574,183 @@ const ls = Bun.which("ls", { console.log(ls); // null ``` +## `Bun.listen` & `Bun.connect` - TCP/TLS sockets + +`Bun.listen` and `Bun.connect` is bun's native TCP & TLS socket API. Use it to implement database clients, game servers – anything that needs to communicate over TCP (instead of HTTP). This is a low-level API intended for library others and for advanced use cases. + +Start a TCP server with `Bun.listen`: + +```ts +// The server +Bun.listen({ + hostname: "localhost", + port: 8080, + socket: { + open(socket) { + socket.write("hello world"); + }, + data(socket, data) { + console.log(data instanceof Uint8Array); // true + }, + drain(socket) { + console.log("gimme more data"); + }, + close(socket) { + console.log("goodbye!"); + }, + }, + // This is a TLS socket + // certFile: "/path/to/cert.pem", + // keyFile: "/path/to/key.pem", +}); +``` + +`Bun.connect` lets you create a TCP client: + +```ts +// The client +Bun.connect({ + hostname: "localhost", + port: 8080, + + socket: { + open(socket) { + socket.write("hello server, i'm the client!"); + }, + data(socket, message) { + socket.write("thanks for the message! Sincerely, " + socket.data.name); + }, + drain(socket) { + console.log("my socket is ready for more data"); + }, + close(socket) { + console.log(""); + }, + timeout(socket) { + console.log("socket timed out"); + }, + }, + + data: { + name: "Clienty McClientface", + }, +}); +``` + +#### Benchmark-driven API design + +Bun's TCP socket API is designed to go as fast as we can. + +Instead of using promises or assigning callbacks per socket instance (like Node.js' `EventEmitter` or the web-standard `WebSocket` API), assign all callbacks one time + +This design decision was made after benchmarking. For performance-sensitive servers, promise-heavy APIs or assigning callbacks per socket instance can cause significant garbage collector pressure and increase memory usage. If you're using a TCP server API, you probably care more about performance. + +```ts +Bun.listen({ + socket: { + open(socket) {}, + data(socket, data) {}, + drain(socket) {}, + close(socket) {}, + error(socket, error) {}, + }, + hostname: "localhost", + port: 8080, +}); +``` + +Instead of having to allocate unique functions for each instance of a socket, we can use each callback once for all sockets. This is a small optimization, but it adds up. + +How do you pass per-socket data to each socket object? + +`**data**` is a property on the `TCPSocket` & `TLSSocket` object that you can use to store per-socket data. + +```ts +socket.data = { name: "Clienty McClientface" }; +``` + +You can assign a default value to `data` in the `connect` or `listen` options. + +```ts +Bun.listen({ + socket: { + open(socket) { + console.log(socket.data); // { name: "Servery McServerface" } + }, + }, + data: { + name: "Servery McServerface", + }, +}); +``` + +#### Hot-reloading TCP servers & clients + +`TCPSocket` (returned by `Bun.connect` and passed through callbacks in `Bun.listen`) has a `reload` method that lets you reload the callbacks for all related sockets (either just the one for `Bun.connect` or all sockets for `Bun.listen`): + +```ts +const socket = Bun.connect({ + hostname: "localhost", + port: 8080, + socket: { + data(socket, msg) { + console.log("wow i got a message!"); + + // this will be called the next time the server sends a message + socket.reload({ + data(socket) { + console.log("okay, not so surprising this time"); + }, + }); + }, + }, +}); +``` + +#### No buffering + +Currently, `TCPSocket` & `TLSSocket` in Bun do not buffer data. Adding support for corking (similar to `ServerWebSocket`) is planned, but it means you will need to handle backpressure yourself using the `drain` callback. + +Your TCP client/server will have abysmal performance if you don't consider buffering carefully. + +For example, this: + +```ts +socket.write("h"); +socket.write("e"); +socket.write("l"); +socket.write("l"); +socket.write("o"); +``` + +Performs significantly worse than: + +```ts +socket.write("hello"); +``` + +To simplify this for now, consider using `ArrayBufferSink` with the `{stream: true}` option: + +```ts +const sink = new ArrayBufferSink({ stream: true, highWaterMark: 1024 }); + +sink.write("h"); +sink.write("e"); +sink.write("l"); +sink.write("l"); +sink.write("o"); + +queueMicrotask(() => { + var data = sink.flush(); + if (!socket.write(data)) { + // put it back in the sink if the socket is full + sink.write(data); + } +}); +``` + +Builtin buffering is planned in a future version of Bun. + ## `Bun.peek` - read a promise without resolving it `Bun.peek` is a utility function that lets you read a promise's result without `await` or `.then`, but only if the promise has already fulfilled or rejected. diff --git a/bench/snippets/string-decoder.mjs b/bench/snippets/string-decoder.mjs new file mode 100644 index 000000000..8f7cb31fb --- /dev/null +++ b/bench/snippets/string-decoder.mjs @@ -0,0 +1,28 @@ +import { bench, run } from "mitata"; +import { StringDecoder } from "string_decoder"; + +var short = Buffer.from("Hello World!"); +var shortUTF16 = Buffer.from("Hello World 💕💕💕"); +var long = Buffer.from("Hello World!".repeat(1024)); +var longUTF16 = Buffer.from("Hello World 💕💕💕".repeat(1024)); +bench(`${short.length} ascii`, () => { + var decoder = new StringDecoder(); + decoder.write(short); +}); + +bench(`${short.length} utf8`, () => { + var decoder = new StringDecoder(); + decoder.write(shortUTF16); +}); + +bench(`${long.length} ascii`, () => { + var decoder = new StringDecoder(); + decoder.write(long); +}); + +bench(`${longUTF16.length} utf8`, () => { + var decoder = new StringDecoder(); + decoder.write(longUTF16); +}); + +await run(); diff --git a/bench/snippets/tcp-echo.bun.ts b/bench/snippets/tcp-echo.bun.ts new file mode 100644 index 000000000..34250d65c --- /dev/null +++ b/bench/snippets/tcp-echo.bun.ts @@ -0,0 +1,49 @@ +import { listen, connect } from "bun"; + +var counter = 0; +const msg = "Hello World!"; + +const handlers = { + open(socket) { + if (!socket.data?.isServer) { + if (!socket.write(msg)) { + socket.data = { pending: msg }; + } + } + }, + data(socket, buffer) { + if (!socket.write(buffer)) { + socket.data = { pending: buffer }; + return; + } + counter++; + }, + drain(socket) { + const pending = socket.data?.pending; + if (!pending) return; + if (socket.write(pending)) { + socket.data = undefined; + counter++; + return; + } + }, +}; + +setInterval(() => { + console.log("Wrote", counter, "messages"); + counter = 0; +}, 1000); + +const server = listen({ + socket: handlers, + hostname: "localhost", + port: 8080, + data: { + isServer: true, + }, +}); +const connection = await connect({ + socket: handlers, + hostname: "localhost", + port: 8080, +}); diff --git a/bench/snippets/tcp-echo.node.mjs b/bench/snippets/tcp-echo.node.mjs new file mode 100644 index 000000000..3362b5a3c --- /dev/null +++ b/bench/snippets/tcp-echo.node.mjs @@ -0,0 +1,52 @@ +import { createRequire } from "node:module"; +const net = createRequire(import.meta.url)("net"); + +const buffer = Buffer.from("Hello World!"); +var counter = 0; +const handlers = { + open() { + if (!socket.data?.isServer) { + if (!this.write(buffer)) { + socket.data = { pending: buffer }; + } + } + }, + data(buffer) { + if (!this.write(buffer)) { + this.data = { pending: buffer.slice() }; + return; + } + counter++; + }, + drain() { + const pending = this.data?.pending; + if (!pending) return; + if (this.write(pending)) { + this.data = undefined; + counter++; + return; + } + }, +}; + +const server = net.createServer(function (socket) { + socket.data = { isServer: true }; + socket.on("connection", handlers.open.bind(socket)); + socket.on("data", handlers.data.bind(socket)); + socket.on("drain", handlers.drain.bind(socket)); + socket.setEncoding("binary"); +}); + +setInterval(() => { + console.log("Wrote", counter, "messages"); + counter = 0; +}, 1000); + +server.listen(8000); + +const socket = net.connect({ host: "localhost", port: 8000 }, () => {}); +socket.on("connection", handlers.open.bind(socket)); +socket.on("data", handlers.data.bind(socket)); +socket.on("drain", handlers.drain.bind(socket)); +socket.setEncoding("binary"); +socket.write(buffer); @@ -1 +1 @@ -1
\ No newline at end of file +2
\ No newline at end of file Binary files differdiff --git a/examples/tcp.ts b/examples/tcp.ts new file mode 100644 index 000000000..b392febd1 --- /dev/null +++ b/examples/tcp.ts @@ -0,0 +1,49 @@ +import { listen, connect } from "bun"; + +var counter = 0; +const msg = Buffer.from("Hello World!"); + +const handlers = { + open(socket) { + if (!socket.data?.isServer) { + if (!socket.write(msg)) { + socket.data = { pending: msg }; + } + } + }, + data(socket, buffer) { + if (!socket.write(buffer)) { + socket.data = { pending: buffer }; + return; + } + counter++; + }, + drain(socket) { + const pending = socket.data?.pending; + if (!pending) return; + if (socket.write(pending)) { + socket.data = undefined; + counter++; + return; + } + }, +}; + +setInterval(() => { + console.log("Wrote", counter, "messages"); + counter = 0; +}, 1000); + +const server = listen({ + socket: handlers, + hostname: "localhost", + port: 8080, + data: { + isServer: true, + }, +}); +const connection = await connect({ + socket: handlers, + hostname: "localhost", + port: 8080, +}); diff --git a/package.json b/package.json index e3b365483..7c2d22894 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { - "bun-types": "^0.1.5", + "bun-types": "latest", + "express": "^4.18.2", "mitata": "^0.1.3", "peechy": "0.4.32", "react": "^18.2.0", diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index ca84bbad0..a343730f8 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -1201,6 +1201,14 @@ pub const Class = NewClass( .spawnSync = .{ .rfn = JSC.wrapWithHasContainer(JSC.Subprocess, "spawnSync", false, false, false), }, + + .listen = .{ + .rfn = JSC.wrapWithHasContainer(JSC.API.Listener, "listen", false, false, false), + }, + + .connect = .{ + .rfn = JSC.wrapWithHasContainer(JSC.API.Listener, "connect", false, false, false), + }, }, .{ .main = .{ diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig new file mode 100644 index 000000000..8a4eca03b --- /dev/null +++ b/src/bun.js/api/bun/socket.zig @@ -0,0 +1,1438 @@ +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 JSC = @import("javascript_core"); +const JSValue = JSC.JSValue; +const JSGlobalObject = JSC.JSGlobalObject; +const Which = @import("../../../which.zig"); +const uws = @import("uws"); +const ZigString = JSC.ZigString; +// const Corker = struct { +// ptr: ?*[16384]u8 = null, +// holder: ?*anyopaque = null, +// list: bun.ByteList = .{}, + +// pub fn write(this: *Corker, owner: *anyopaque, bytes: []const u8) usize { +// if (this.holder != null and this.holder.? != owner) { +// return 0; +// } + +// this.holder = owner; +// if (this.ptr == null) { +// this.ptr = bun.default_allocator.alloc(u8, 16384) catch @panic("Out of memory allocating corker"); +// std.debug.assert(this.list.cap == 0); +// std.debug.assert(this.list.len == 0); +// this.list.cap = 16384; +// this.list.ptr = this.ptr.?; +// this.list.len = 0; +// } +// } + +// pub fn flushIfNecessary(this: *Corker, comptime ssl: bool, socket: uws.NewSocketHandler(ssl), owner: *anyopaque) void { +// if (this.holder == null or this.holder.? != owner) { +// return; +// } + +// if (this.ptr == null) { +// return; +// } + +// if (this.list.len == 0) { +// return; +// } + +// const bytes = ths.list.slice(); + +// this.list.len = 0; +// } +// }; + +const Handlers = struct { + onOpen: JSC.JSValue = .zero, + onClose: JSC.JSValue = .zero, + onData: JSC.JSValue = .zero, + onWritable: JSC.JSValue = .zero, + onTimeout: JSC.JSValue = .zero, + onConnectError: JSC.JSValue = .zero, + onEnd: JSC.JSValue = .zero, + onError: JSC.JSValue = .zero, + + encoding: JSC.Node.Encoding = .utf8, + + vm: *JSC.VirtualMachine, + globalObject: *JSC.JSGlobalObject, + active_connections: u32 = 0, + is_server: bool = false, + promise: JSC.Strong = .{}, + + // corker: Corker = .{}, + + pub fn resolvePromise(this: *Handlers, value: JSValue) void { + var promise = this.promise.get() orelse return; + this.promise.deinit(); + promise.asPromise().?.resolve(this.globalObject, value); + } + + pub fn rejectPromise(this: *Handlers, value: JSValue) bool { + var promise = this.promise.get() orelse return false; + this.promise.deinit(); + promise.asPromise().?.reject(this.globalObject, value); + return true; + } + + pub fn markInactive(this: *Handlers, ssl: bool, ctx: *uws.SocketContext) void { + this.active_connections -= 1; + if (this.active_connections == 0 and this.is_server) { + var listen_socket: *Listener = @fieldParentPtr(Listener, "handlers", this); + // allow it to be GC'd once the last connection is closed and it's not listening anymore + if (listen_socket.listener == null) { + listen_socket.strong_self.clear(); + } + } else if (this.active_connections == 0 and !this.is_server) { + this.unprotect(); + ctx.deinit(ssl); + bun.default_allocator.destroy(this); + } + } + + pub fn callErrorHandler(this: *Handlers, thisValue: JSValue, err: []const JSValue) bool { + const onError = this.onError; + if (onError == .zero) { + return false; + } + + const result = onError.callWithThis(this.globalObject, thisValue, err); + if (!result.isEmptyOrUndefinedOrNull() and result.isAnyError(this.globalObject)) { + this.vm.runErrorHandler(result, null); + } + + return true; + } + + pub fn fromJS(globalObject: *JSC.JSGlobalObject, opts: JSC.JSValue, exception: JSC.C.ExceptionRef) ?Handlers { + var handlers = Handlers{ + .vm = globalObject.bunVM(), + .globalObject = globalObject, + }; + + if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) { + exception.* = JSC.toInvalidArguments("Expected socket object", .{}, globalObject).asObjectRef(); + return null; + } + + const pairs = .{ + .{ "onData", "data" }, + .{ "onWritable", "drain" }, + .{ "onOpen", "open" }, + .{ "onClose", "close" }, + .{ "onData", "data" }, + .{ "onTimeout", "timeout" }, + .{ "onConnectError", "connectError" }, + .{ "onEnd", "end" }, + .{ "onError", "error" }, + }; + inline for (pairs) |pair| { + if (opts.getTruthy(globalObject, pair.@"1")) |callback_value| { + if (!callback_value.isCell() or !callback_value.isCallable(globalObject.vm())) { + exception.* = JSC.toInvalidArguments(comptime std.fmt.comptimePrint("Expected \"{s}\" callback to be a function", .{pair.@"1"}), .{}, globalObject).asObjectRef(); + return null; + } + + @field(handlers, pair.@"0") = callback_value; + } + } + + if (handlers.onData == .zero and handlers.onWritable == .zero) { + exception.* = JSC.toInvalidArguments("Expected at least \"data\" or \"drain\" callback", .{}, globalObject).asObjectRef(); + return null; + } + + return handlers; + } + + pub fn unprotect(this: *Handlers) void { + this.onOpen.unprotect(); + this.onClose.unprotect(); + this.onData.unprotect(); + this.onWritable.unprotect(); + this.onTimeout.unprotect(); + this.onConnectError.unprotect(); + this.onEnd.unprotect(); + this.onError.unprotect(); + } + + pub fn protect(this: *Handlers) void { + this.onOpen.protect(); + this.onClose.protect(); + this.onData.protect(); + this.onWritable.protect(); + this.onTimeout.protect(); + this.onConnectError.protect(); + this.onEnd.protect(); + this.onError.protect(); + } +}; + +pub const SocketConfig = struct { + hostname_or_unix: JSC.ZigString.Slice, + port: ?u16 = null, + ssl: ?JSC.API.ServerConfig.SSLConfig = null, + handlers: Handlers, + default_data: JSC.JSValue = .zero, + + pub fn fromJS( + opts: JSC.JSValue, + globalObject: *JSC.JSGlobalObject, + exception: JSC.C.ExceptionRef, + ) ?SocketConfig { + var hostname_or_unix: JSC.ZigString.Slice = JSC.ZigString.Slice.empty; + var port: ?u16 = null; + + var ssl: ?JSC.API.ServerConfig.SSLConfig = null; + var default_data = JSValue.zero; + + if (JSC.API.ServerConfig.SSLConfig.inJS(globalObject, opts, exception)) |ssl_config| { + ssl = ssl_config; + } else if (exception.* != null) { + return null; + } + + if (opts.getTruthy(globalObject, "hostname") orelse opts.getTruthy(globalObject, "host")) |hostname| { + if (hostname.isEmptyOrUndefinedOrNull() or !hostname.isString()) { + exception.* = JSC.toInvalidArguments("Expected \"hostname\" to be a string", .{}, globalObject).asObjectRef(); + return null; + } + + const port_value = opts.get(globalObject, "port") orelse JSValue.zero; + if (port_value.isEmptyOrUndefinedOrNull() or !port_value.isNumber() or port_value.toInt64() > std.math.maxInt(u16) or port_value.toInt64() < 0) { + exception.* = JSC.toInvalidArguments("Expected \"port\" to be a number between 0 and 65432", .{}, globalObject).asObjectRef(); + return null; + } + + hostname_or_unix = hostname.getZigString(globalObject).toSlice(bun.default_allocator); + port = port_value.toU16(); + + if (hostname_or_unix.len == 0) { + exception.* = JSC.toInvalidArguments("Expected \"hostname\" to be a non-empty string", .{}, globalObject).asObjectRef(); + return null; + } + } else if (opts.getTruthy(globalObject, "unix")) |unix_socket| { + if (unix_socket.isEmptyOrUndefinedOrNull() or !unix_socket.isString()) { + exception.* = JSC.toInvalidArguments("Expected \"unix\" to be a string", .{}, globalObject).asObjectRef(); + return null; + } + + hostname_or_unix = unix_socket.getZigString(globalObject).toSlice(bun.default_allocator); + + if (hostname_or_unix.len == 0) { + exception.* = JSC.toInvalidArguments("Expected \"unix\" to be a non-empty string", .{}, globalObject).asObjectRef(); + return null; + } + } else { + exception.* = JSC.toInvalidArguments("Expected either \"hostname\" or \"unix\"", .{}, globalObject).asObjectRef(); + return null; + } + + const handlers = Handlers.fromJS(globalObject, opts.get(globalObject, "socket") orelse JSValue.zero, exception) orelse { + hostname_or_unix.deinit(); + return null; + }; + + if (opts.getTruthy(globalObject, "data")) |default_data_value| { + default_data = default_data_value; + } + + return SocketConfig{ + .hostname_or_unix = hostname_or_unix, + .port = port, + .ssl = ssl, + .handlers = handlers, + .default_data = default_data, + }; + } +}; + +pub const Listener = struct { + const log = Output.scoped(.Listener, false); + + handlers: Handlers, + listener: ?*uws.ListenSocket = null, + poll_ref: JSC.PollRef = JSC.PollRef.init(), + connection: UnixOrHost, + socket_context: ?*uws.SocketContext = null, + ssl: bool = false, + + strong_data: JSC.Strong = .{}, + strong_self: JSC.Strong = .{}, + + pub usingnamespace JSC.Codegen.JSListener; + + pub fn getData( + this: *Listener, + _: *JSC.JSGlobalObject, + ) callconv(.C) JSValue { + log("getData()", .{}); + return this.strong_data.get() orelse JSValue.jsUndefined(); + } + + pub fn setData( + this: *Listener, + globalObject: *JSC.JSGlobalObject, + value: JSC.JSValue, + ) callconv(.C) bool { + log("setData()", .{}); + this.strong_data.set(globalObject, value); + return true; + } + + const UnixOrHost = union(enum) { + unix: []const u8, + host: struct { + host: []const u8, + port: u16, + }, + + pub fn deinit(this: UnixOrHost) void { + switch (this) { + .unix => |u| { + bun.default_allocator.destroy(@intToPtr([*]u8, @ptrToInt(u.ptr))); + }, + .host => |h| { + bun.default_allocator.destroy(@intToPtr([*]u8, @ptrToInt(h.host.ptr))); + }, + } + } + }; + + pub fn reload(this: *Listener, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + const args = callframe.arguments(1); + + if (args.len < 1 or (this.listener == null and this.handlers.active_connections == 0)) { + globalObject.throw("Expected 1 argument", .{}); + return .zero; + } + + const opts = args.ptr[0]; + if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) { + globalObject.throwValue(JSC.toInvalidArguments("Expected options object", .{}, globalObject)); + return .zero; + } + + var exception: JSC.C.JSValueRef = null; + + var socket_obj = opts.get(globalObject, "socket") orelse { + globalObject.throw("Expected \"socket\" object", .{}); + return .zero; + }; + + const handlers = Handlers.fromJS(globalObject, socket_obj, &exception) orelse { + globalObject.throwValue(exception.?.value()); + return .zero; + }; + + var prev_handlers = this.handlers; + prev_handlers.unprotect(); + this.handlers = handlers; // TODO: this is a memory leak + this.handlers.protect(); + + return JSValue.jsUndefined(); + } + + pub fn listen( + globalObject: *JSC.JSGlobalObject, + opts: JSValue, + exception: JSC.C.ExceptionRef, + ) JSValue { + log("listen", .{}); + if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) { + exception.* = JSC.toInvalidArguments("Expected object", .{}, globalObject).asObjectRef(); + return .zero; + } + + const socket_config = SocketConfig.fromJS(opts, globalObject, exception) orelse { + return .zero; + }; + var hostname_or_unix = socket_config.hostname_or_unix; + var port = socket_config.port; + var ssl = socket_config.ssl; + var handlers = socket_config.handlers; + handlers.is_server = true; + + const ssl_enabled = ssl != null; + + var socket = Listener{ + .handlers = handlers, + .connection = if (port) |port_| .{ + .host = .{ .host = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), .port = port_ }, + } else .{ + .unix = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), + }, + .ssl = ssl_enabled, + }; + + socket.handlers.protect(); + + if (socket_config.default_data != .zero) { + socket.strong_data = JSC.Strong.create(socket_config.default_data, globalObject); + } + + const socket_flags: i32 = 0; + + var ctx_opts: uws.us_socket_context_options_t = undefined; + @memset(@ptrCast([*]u8, &ctx_opts), 0, @sizeOf(uws.us_socket_context_options_t)); + + if (ssl) |ssl_config| { + ctx_opts.key_file_name = ssl_config.key_file_name; + ctx_opts.cert_file_name = ssl_config.cert_file_name; + ctx_opts.ca_file_name = ssl_config.ca_file_name; + ctx_opts.dh_params_file_name = ssl_config.dh_params_file_name; + ctx_opts.passphrase = ssl_config.passphrase; + ctx_opts.ssl_prefer_low_memory_usage = @boolToInt(ssl_config.low_memory_mode); + } + + socket.socket_context = uws.us_create_socket_context(@boolToInt(ssl_enabled), uws.Loop.get().?, @sizeOf(usize), ctx_opts); + + if (ssl) |ssl_config| { + uws.us_socket_context_add_server_name(1, socket.socket_context, ssl_config.server_name, ctx_opts, null); + } + + var this: *Listener = handlers.vm.allocator.create(Listener) catch @panic("OOM"); + this.* = socket; + this.socket_context.?.ext(ssl_enabled, *Listener).?.* = this; + + var this_value = this.toJS(globalObject); + this.strong_self.set(globalObject, this_value); + this.poll_ref.ref(handlers.vm); + + if (ssl_enabled) { + uws.NewSocketHandler(true).configure( + this.socket_context.?, + true, + *TLSSocket, + struct { + pub const onOpen = NewSocket(true).onOpen; + pub const onCreate = onCreateTLS; + pub const onClose = NewSocket(true).onClose; + pub const onData = NewSocket(true).onData; + pub const onWritable = NewSocket(true).onWritable; + pub const onTimeout = NewSocket(true).onTimeout; + pub const onConnectError = NewSocket(true).onConnectError; + pub const onEnd = NewSocket(true).onEnd; + }, + ); + } else { + uws.NewSocketHandler(false).configure( + this.socket_context.?, + true, + *TCPSocket, + struct { + pub const onOpen = NewSocket(false).onOpen; + pub const onCreate = onCreateTCP; + pub const onClose = NewSocket(false).onClose; + pub const onData = NewSocket(false).onData; + pub const onWritable = NewSocket(false).onWritable; + pub const onTimeout = NewSocket(false).onTimeout; + pub const onConnectError = NewSocket(false).onConnectError; + pub const onEnd = NewSocket(false).onEnd; + }, + ); + } + + switch (this.connection) { + .host => |c| { + var host = bun.default_allocator.dupeZ(u8, c.host) catch unreachable; + defer bun.default_allocator.destroy(host.ptr); + this.listener = uws.us_socket_context_listen(@boolToInt(ssl_enabled), this.socket_context, host, c.port, socket_flags, 8) orelse { + exception.* = JSC.toInvalidArguments( + "Failed to listen at {s}:{d}", + .{ + bun.span(host), + c.port, + }, + globalObject, + ).asObjectRef(); + this.poll_ref.unref(handlers.vm); + + this.strong_self.clear(); + this.strong_data.clear(); + + return .zero; + }; + }, + .unix => |u| { + var host = bun.default_allocator.dupeZ(u8, u) catch unreachable; + defer bun.default_allocator.destroy(host.ptr); + this.listener = uws.us_socket_context_listen_unix(@boolToInt(ssl_enabled), this.socket_context, host, socket_flags, 8) orelse { + exception.* = JSC.toInvalidArguments( + "Failed to listen on socket {s}", + .{ + bun.span(host), + }, + globalObject, + ).asObjectRef(); + this.poll_ref.unref(handlers.vm); + + this.strong_self.clear(); + this.strong_data.clear(); + + return .zero; + }; + }, + } + + return this_value; + } + + pub fn onCreateTLS( + socket: uws.NewSocketHandler(true), + ) void { + onCreate(true, socket); + } + + pub fn onCreateTCP( + socket: uws.NewSocketHandler(false), + ) void { + onCreate(false, socket); + } + + pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*Listener { + globalObject.throw("Cannot construct Listener", .{}); + return null; + } + + pub fn onCreate(comptime ssl: bool, socket: uws.NewSocketHandler(ssl)) void { + JSC.markBinding(@src()); + log("onCreate", .{}); + var listener: *Listener = socket.context().ext(ssl, *Listener).?.*; + const Socket = NewSocket(ssl); + std.debug.assert(ssl == listener.ssl); + + var this_socket = listener.handlers.vm.allocator.create(Socket) catch @panic("Out of memory"); + this_socket.* = Socket{ + .handlers = &listener.handlers, + .this_value = listener.strong_data.get() orelse JSValue.zero, + .socket = socket, + }; + socket.ext(**anyopaque).?.* = bun.cast(**anyopaque, this_socket); + socket.timeout(120000); + } + + pub fn stop(this: *Listener, _: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + log("close", .{}); + + var listener = this.listener orelse return JSValue.jsUndefined(); + this.listener = null; + listener.close(this.ssl); + if (this.handlers.active_connections == 0) { + this.poll_ref.unref(this.handlers.vm); + this.handlers.unprotect(); + this.socket_context.?.deinit(this.ssl); + this.socket_context = null; + this.strong_self.clear(); + this.strong_data.clear(); + } + + return JSValue.jsUndefined(); + } + + pub fn finalize(this: *Listener) callconv(.C) void { + log("Finalize", .{}); + this.deinit(); + } + + pub fn deinit(this: *Listener) void { + this.strong_self.deinit(); + this.strong_data.deinit(); + this.poll_ref.unref(this.handlers.vm); + std.debug.assert(this.listener == null); + std.debug.assert(this.handlers.active_connections == 0); + + if (this.socket_context) |ctx| { + ctx.deinit(this.ssl); + } + + this.handlers.unprotect(); + this.connection.deinit(); + bun.default_allocator.destroy(this); + } + + pub fn getConnectionsCount(this: *Listener, _: *JSC.JSGlobalObject) callconv(.C) JSValue { + return JSValue.jsNumber(this.handlers.active_connections); + } + + pub fn getUnix(this: *Listener, globalObject: *JSC.JSGlobalObject) callconv(.C) JSValue { + if (this.connection != .unix) { + return JSValue.jsUndefined(); + } + + return ZigString.init(this.connection.unix).withEncoding().toValueGC(globalObject); + } + + pub fn getHostname(this: *Listener, globalObject: *JSC.JSGlobalObject) callconv(.C) JSValue { + if (this.connection != .host) { + return JSValue.jsUndefined(); + } + + return ZigString.init(this.connection.host.host).withEncoding().toValueGC(globalObject); + } + + pub fn getPort(this: *Listener, _: *JSC.JSGlobalObject) callconv(.C) JSValue { + if (this.connection != .host) { + return JSValue.jsUndefined(); + } + + return JSValue.jsNumber(this.connection.host.port); + } + + pub fn ref(this: *Listener, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + var this_value = callframe.this(); + if (this.listener == null) return JSValue.jsUndefined(); + this.poll_ref.ref(globalObject.bunVM()); + this.strong_self.set(globalObject, this_value); + return JSValue.jsUndefined(); + } + + pub fn unref(this: *Listener, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + if (!this.poll_ref.isActive()) return JSValue.jsUndefined(); + + this.poll_ref.unref(globalObject.bunVM()); + if (this.handlers.active_connections == 0) { + this.strong_self.clear(); + } + return JSValue.jsUndefined(); + } + + pub fn connect( + globalObject: *JSC.JSGlobalObject, + opts: JSValue, + exception: JSC.C.ExceptionRef, + ) JSValue { + if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) { + exception.* = JSC.toInvalidArguments("Expected options object", .{}, globalObject).asObjectRef(); + return .zero; + } + + const socket_config = SocketConfig.fromJS(opts, globalObject, exception) orelse { + return .zero; + }; + var hostname_or_unix = socket_config.hostname_or_unix; + var port = socket_config.port; + var ssl = socket_config.ssl; + var handlers = socket_config.handlers; + var default_data = socket_config.default_data; + + const ssl_enabled = ssl != null; + + handlers.protect(); + + var ctx_opts: uws.us_socket_context_options_t = undefined; + @memset(@ptrCast([*]u8, &ctx_opts), 0, @sizeOf(uws.us_socket_context_options_t)); + + if (ssl) |ssl_config| { + if (ssl_config.key_file_name != null) + ctx_opts.key_file_name = ssl_config.key_file_name; + if (ssl_config.cert_file_name != null) + ctx_opts.cert_file_name = ssl_config.cert_file_name; + if (ssl_config.ca_file_name != null) + ctx_opts.ca_file_name = ssl_config.ca_file_name; + if (ssl_config.dh_params_file_name != null) + ctx_opts.dh_params_file_name = ssl_config.dh_params_file_name; + if (ssl_config.passphrase != null) + ctx_opts.passphrase = ssl_config.passphrase; + ctx_opts.ssl_prefer_low_memory_usage = @boolToInt(ssl_config.low_memory_mode); + } + + var socket_context = uws.us_create_socket_context(@boolToInt(ssl_enabled), uws.Loop.get().?, @sizeOf(usize), ctx_opts).?; + var connection: Listener.UnixOrHost = if (port) |port_| .{ + .host = .{ .host = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), .port = port_ }, + } else .{ + .unix = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), + }; + + if (ssl_enabled) { + uws.NewSocketHandler(true).configure( + socket_context, + true, + *TLSSocket, + struct { + pub const onOpen = NewSocket(true).onOpen; + pub const onClose = NewSocket(true).onClose; + pub const onData = NewSocket(true).onData; + pub const onWritable = NewSocket(true).onWritable; + pub const onTimeout = NewSocket(true).onTimeout; + pub const onConnectError = NewSocket(true).onConnectError; + pub const onEnd = NewSocket(true).onEnd; + }, + ); + } else { + uws.NewSocketHandler(false).configure( + socket_context, + true, + *TCPSocket, + struct { + pub const onOpen = NewSocket(false).onOpen; + pub const onClose = NewSocket(false).onClose; + pub const onData = NewSocket(false).onData; + pub const onWritable = NewSocket(false).onWritable; + pub const onTimeout = NewSocket(false).onTimeout; + pub const onConnectError = NewSocket(false).onConnectError; + pub const onEnd = NewSocket(false).onEnd; + }, + ); + } + + default_data.ensureStillAlive(); + + // const socket_flags: i32 = 0; + + var handlers_ptr = handlers.vm.allocator.create(Handlers) catch @panic("OOM"); + handlers_ptr.* = handlers; + handlers_ptr.is_server = false; + + if (ssl_enabled) { + var tls = handlers.vm.allocator.create(TLSSocket) catch @panic("OOM"); + + tls.* = .{ + .handlers = handlers_ptr, + .this_value = default_data, + .socket = undefined, + }; + + var promise = JSC.JSPromise.create(globalObject); + var promise_value = promise.asValue(globalObject); + handlers.promise.set(globalObject, promise_value); + + tls.doConnect(connection, socket_context) catch { + handlers_ptr.unprotect(); + socket_context.deinit(true); + handlers.vm.allocator.destroy(handlers_ptr); + handlers.promise.deinit(); + bun.default_allocator.destroy(tls); + exception.* = ZigString.static("Failed to connect").toErrorInstance(globalObject).asObjectRef(); + return .zero; + }; + + return promise_value; + } else { + var tcp = handlers.vm.allocator.create(TCPSocket) catch @panic("OOM"); + + tcp.* = .{ + .handlers = handlers_ptr, + .this_value = default_data, + .socket = undefined, + }; + var promise = JSC.JSPromise.create(globalObject); + var promise_value = promise.asValue(globalObject); + handlers.promise.set(globalObject, promise_value); + + tcp.doConnect(connection, socket_context) catch { + handlers_ptr.unprotect(); + socket_context.deinit(false); + handlers.vm.allocator.destroy(handlers_ptr); + handlers.promise.deinit(); + bun.default_allocator.destroy(tcp); + exception.* = ZigString.static("Failed to connect").toErrorInstance(globalObject).asObjectRef(); + return .zero; + }; + + return promise_value; + } + } +}; + +fn JSSocketType(comptime ssl: bool) type { + if (!ssl) { + return JSC.Codegen.JSTCPSocket; + } else { + return JSC.Codegen.JSTLSSocket; + } +} + +fn NewSocket(comptime ssl: bool) type { + return struct { + pub const Socket = uws.NewSocketHandler(ssl); + socket: Socket, + detached: bool = false, + handlers: *Handlers, + this_value: JSC.JSValue = .zero, + poll_ref: JSC.PollRef = JSC.PollRef.init(), + reffer: JSC.Ref = JSC.Ref.init(), + last_4: [4]u8 = .{ 0, 0, 0, 0 }, + + const This = @This(); + const log = Output.scoped(.Socket, false); + + pub usingnamespace JSSocketType(ssl); + + pub fn doConnect(this: *This, connection: Listener.UnixOrHost, socket_ctx: *uws.SocketContext) !void { + switch (connection) { + .host => |c| { + _ = @This().Socket.connectPtr( + c.host, + c.port, + socket_ctx, + @This(), + this, + "socket", + ) orelse return error.ConnectionFailed; + }, + .unix => |u| { + _ = @This().Socket.connectUnixPtr( + u, + socket_ctx, + @This(), + this, + "socket", + ) orelse return error.ConnectionFailed; + }, + } + } + + pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) ?*This { + globalObject.throw("Cannot construct Socket", .{}); + return null; + } + + pub fn onWritable( + this: *This, + _: Socket, + ) void { + JSC.markBinding(@src()); + if (this.detached) return; + var handlers = this.handlers; + const callback = handlers.onWritable; + if (callback == .zero) { + return; + } + + const this_value = this.getThisValue(handlers.globalObject); + const result = callback.callWithThis(handlers.globalObject, this_value, &[_]JSValue{ + this_value, + }); + + if (!result.isEmptyOrUndefinedOrNull() and result.isAnyError(handlers.globalObject)) { + if (handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, result })) { + return; + } + + handlers.vm.runErrorHandler(result, null); + } + } + pub fn onTimeout( + this: *This, + _: Socket, + ) void { + JSC.markBinding(@src()); + if (this.detached) return; + this.detached = true; + var handlers = this.handlers; + this.poll_ref.unref(handlers.vm); + var globalObject = handlers.globalObject; + const callback = handlers.onTimeout; + + this.markInactive(); + if (callback == .zero) { + return; + } + + const this_value = this.getThisValue(globalObject); + const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ + this_value, + }); + + if (!result.isEmptyOrUndefinedOrNull() and result.isAnyError(globalObject)) { + if (handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, result })) { + return; + } + + handlers.vm.runErrorHandler(result, null); + } + } + pub fn onConnectError(this: *This, socket: Socket, errno: c_int) void { + JSC.markBinding(@src()); + log("onConnectError({d}", .{errno}); + this.detached = true; + var handlers = this.handlers; + this.poll_ref.unref(handlers.vm); + var err = JSC.SystemError{ + .errno = errno, + .message = ZigString.init("Failed to connect"), + .syscall = ZigString.init("connect"), + }; + _ = handlers.rejectPromise(err.toErrorInstance(handlers.globalObject)); + this.reffer.unref(handlers.vm); + handlers.markInactive(ssl, socket.context()); + this.finalize(); + } + + pub fn markActive(this: *This) void { + if (!this.reffer.has) { + this.handlers.active_connections += 1; + this.reffer.ref(this.handlers.vm); + } + } + + pub fn markInactive(this: *This) void { + if (this.reffer.has) { + var vm = this.handlers.vm; + this.handlers.markInactive(ssl, this.socket.context()); + this.reffer.unref(vm); + this.poll_ref.unref(vm); + } + + if (this.this_value != .zero) { + this.this_value.unprotect(); + } + } + + pub fn onOpen(this: *This, socket: Socket) void { + JSC.markBinding(@src()); + log("onOpen", .{}); + this.poll_ref.ref(this.handlers.vm); + this.detached = false; + this.socket = socket; + socket.ext(**anyopaque).?.* = bun.cast(**anyopaque, this); + var handlers = this.handlers; + const old_this_value = this.this_value; + this.this_value = .zero; + const this_value = this.getThisValue(handlers.globalObject); + + if (old_this_value != .zero) { + This.dataSetCached(this_value, handlers.globalObject, old_this_value); + } + + if (handlers.onOpen == .zero and old_this_value == .zero) { + this.markActive(); + this.handlers.resolvePromise(this_value); + return; + } + + handlers.resolvePromise(this_value); + + const result = handlers.onOpen.callWithThis(handlers.globalObject, this_value, &[_]JSValue{ + this_value, + }); + + if (!result.isEmptyOrUndefinedOrNull() and result.isAnyError(handlers.globalObject)) { + if (!this.socket.isClosed()) { + log("Closing due to error", .{}); + this.detached = true; + this.socket.close(0, null); + } else { + log("Already closed", .{}); + } + + if (handlers.rejectPromise(this_value)) { + return; + } + + if (handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, result })) { + return; + } + + handlers.vm.runErrorHandler(result, null); + return; + } + + this.markActive(); + } + + pub fn getThisValue(this: *This, globalObject: *JSC.JSGlobalObject) JSValue { + if (this.this_value == .zero) { + const value = this.toJS(globalObject); + this.this_value = value; + value.protect(); + return value; + } + + return this.this_value; + } + + pub fn onEnd(this: *This, _: Socket) void { + JSC.markBinding(@src()); + log("onEnd", .{}); + this.detached = true; + var handlers = this.handlers; + const callback = handlers.onEnd; + + if (callback == .zero) { + return; + } + + const this_value = this.getThisValue(handlers.globalObject); + const result = callback.callWithThis(handlers.globalObject, this_value, &[_]JSValue{ + this_value, + }); + + if (!result.isEmptyOrUndefinedOrNull() and result.isAnyError(handlers.globalObject)) { + if (handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, result })) { + return; + } + + handlers.vm.runErrorHandler(result, null); + } + } + + pub fn onClose(this: *This, _: Socket, err: c_int, _: ?*anyopaque) void { + JSC.markBinding(@src()); + log("onClose", .{}); + this.detached = true; + var handlers = this.handlers; + this.poll_ref.unref(handlers.vm); + + const callback = handlers.onClose; + var globalObject = handlers.globalObject; + + if (callback == .zero) { + this.markInactive(); + return; + } + + const this_value = this.getThisValue(globalObject); + + const result = callback.callWithThis(globalObject, this_value, &[_]JSValue{ + this_value, + JSValue.jsNumber(@as(i32, err)), + }); + + if (!result.isEmptyOrUndefinedOrNull() and result.isAnyError(globalObject)) { + if (handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, result })) { + return; + } + + handlers.vm.runErrorHandler(result, null); + } + } + + pub fn onData(this: *This, _: Socket, data: []const u8) void { + JSC.markBinding(@src()); + if (comptime Environment.allow_assert) { + log("onData({d})", .{data.len}); + } + + if (this.detached) return; + var handlers = this.handlers; + // const encoding = handlers.encoding; + const callback = handlers.onData; + if (callback == .zero) { + return; + } + + const output_value = JSC.ArrayBuffer.create(handlers.globalObject, data, .Uint8Array); + + const this_value = this.getThisValue(handlers.globalObject); + const result = callback.callWithThis(handlers.globalObject, this_value, &[_]JSValue{ + this_value, + output_value, + }); + + if (!result.isEmptyOrUndefinedOrNull() and result.isAnyError(handlers.globalObject)) { + if (handlers.callErrorHandler(this_value, &[_]JSC.JSValue{ this_value, result })) { + return; + } + + handlers.vm.runErrorHandler(result, null); + } + } + + pub fn getData( + _: *This, + _: *JSC.JSGlobalObject, + ) callconv(.C) JSValue { + log("getData()", .{}); + return JSValue.jsUndefined(); + } + + pub fn setData( + this: *This, + globalObject: *JSC.JSGlobalObject, + value: JSC.JSValue, + ) callconv(.C) bool { + log("setData()", .{}); + This.dataSetCached(this.this_value, globalObject, value); + return true; + } + + pub fn getListener( + this: *This, + _: *JSC.JSGlobalObject, + ) callconv(.C) JSValue { + if (!this.handlers.is_server or this.detached) { + return JSValue.jsUndefined(); + } + + return @fieldParentPtr(Listener, "handlers", this.handlers).strong_self.get() orelse JSValue.jsUndefined(); + } + + pub fn getReadyState( + this: *This, + _: *JSC.JSGlobalObject, + ) callconv(.C) JSValue { + log("getReadyState()", .{}); + + if (this.detached) { + return JSValue.jsNumber(@as(i32, -1)); + } else if (this.socket.isClosed()) { + return JSValue.jsNumber(@as(i32, 0)); + } else if (this.socket.isEstablished()) { + return JSValue.jsNumber(@as(i32, 1)); + } else if (this.socket.isShutdown()) { + return JSValue.jsNumber(@as(i32, -2)); + } else { + return JSValue.jsNumber(@as(i32, 2)); + } + } + + pub fn timeout( + this: *This, + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + const args = callframe.arguments(1); + if (this.detached) return JSValue.jsUndefined(); + if (args.len == 0) { + globalObject.throw("Expected 1 argument, got 0", .{}); + return .zero; + } + const t = args.ptr[0].toInt32(); + if (t < 0) { + globalObject.throw("Timeout must be a positive integer", .{}); + return .zero; + } + + this.socket.timeout(@intCast(c_uint, t)); + + return JSValue.jsUndefined(); + } + + pub fn write( + this: *This, + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + + if (this.detached) { + return JSValue.jsNumber(@as(i32, -1)); + } + + const args = callframe.arguments(4); + + if (args.len == 0) { + globalObject.throw("Expected 1 - 4 arguments, got 0", .{}); + return .zero; + } + + return this.writeOrEnd(globalObject, args.ptr[0..args.len], false); + } + + pub fn getLocalPort( + this: *This, + _: *JSC.JSGlobalObject, + ) callconv(.C) JSValue { + if (this.detached) { + return JSValue.jsUndefined(); + } + + return JSValue.jsNumber(this.socket.localPort()); + } + + pub fn getRemoteAddress( + this: *This, + globalThis: *JSC.JSGlobalObject, + ) callconv(.C) JSValue { + if (this.detached) { + return JSValue.jsUndefined(); + } + + var buf: [512]u8 = undefined; + var length: i32 = 512; + this.socket.remoteAddress(&buf, &length); + const address = buf[0..@intCast(usize, @minimum(length, 0))]; + + if (address.len == 0) { + return JSValue.jsUndefined(); + } + + return ZigString.init(address).toValueGC(globalThis); + } + + fn writeMaybeCorked(this: *This, buffer: []const u8, is_end: bool) i32 { + // we don't cork yet but we might later + return this.socket.write(buffer, is_end); + } + + fn writeOrEnd(this: *This, globalObject: *JSC.JSGlobalObject, args: []const JSC.JSValue, is_end: bool) JSValue { + if (args.ptr[0].isEmptyOrUndefinedOrNull()) { + globalObject.throw("Expected an ArrayBufferView, a string, or a Blob", .{}); + return .zero; + } + + if (this.socket.isShutdown() or this.socket.isClosed()) { + return JSValue.jsNumber(@as(i32, -1)); + } + + if (args.ptr[0].asArrayBuffer(globalObject)) |array_buffer| { + var slice = array_buffer.slice(); + + if (args.len > 1) { + if (!args.ptr[1].isAnyInt()) { + globalObject.throw("Expected offset integer, got {any}", .{args.ptr[1].getZigString(globalObject)}); + return .zero; + } + + const offset = @minimum(args.ptr[1].toUInt64NoTruncate(), slice.len); + slice = slice[offset..]; + + if (args.len > 2) { + if (!args.ptr[2].isAnyInt()) { + globalObject.throw("Expected length integer, got {any}", .{args.ptr[2].getZigString(globalObject)}); + return .zero; + } + + const length = @minimum(args.ptr[2].toUInt64NoTruncate(), slice.len); + slice = slice[0..length]; + } + } + + if (slice.len == 0) { + return JSValue.jsNumber(@as(i32, 0)); + } + + return JSValue.jsNumber(this.writeMaybeCorked(slice, is_end)); + } else if (args.ptr[0].jsType() == .DOMWrapper) { + const blob: JSC.WebCore.AnyBlob = getter: { + if (args.ptr[0].as(JSC.WebCore.Blob)) |blob| { + break :getter JSC.WebCore.AnyBlob{ .Blob = blob.* }; + } else if (args.ptr[0].as(JSC.WebCore.Response)) |response| { + response.body.value.toBlobIfPossible(); + + if (response.body.value.tryUseAsAnyBlob()) |blob| { + break :getter blob; + } + + globalObject.throw("Only Blob/buffered bodies are supported for now", .{}); + return .zero; + } else if (args.ptr[0].as(JSC.WebCore.Request)) |request| { + request.body.toBlobIfPossible(); + if (request.body.tryUseAsAnyBlob()) |blob| { + break :getter blob; + } + + globalObject.throw("Only Blob/buffered bodies are supported for now", .{}); + return .zero; + } + + globalObject.throw("Expected Blob, Request or Response", .{}); + return .zero; + }; + + if (!blob.needsToReadFile()) { + var slice = blob.slice(); + + if (args.len > 1) { + if (!args.ptr[1].isAnyInt()) { + globalObject.throw("Expected offset integer, got {any}", .{args.ptr[1].getZigString(globalObject)}); + return .zero; + } + + const offset = @minimum(args.ptr[1].toUInt64NoTruncate(), slice.len); + slice = slice[offset..]; + + if (args.len > 2) { + if (!args.ptr[2].isAnyInt()) { + globalObject.throw("Expected length integer, got {any}", .{args.ptr[2].getZigString(globalObject)}); + return .zero; + } + + const length = @minimum(args.ptr[2].toUInt64NoTruncate(), slice.len); + slice = slice[0..length]; + } + } + + if (slice.len == 0) { + return JSValue.jsNumber(@as(i32, 0)); + } + + return JSValue.jsNumber(this.writeMaybeCorked(slice, is_end)); + } + + globalObject.throw("sendfile() not implemented yet", .{}); + return .zero; + } else if (args.ptr[0].toStringOrNull(globalObject)) |jsstring| { + var zig_str = jsstring.toSlice(globalObject, globalObject.bunVM().allocator); + defer zig_str.deinit(); + + var slice = zig_str.slice(); + + if (args.len > 1) { + if (!args.ptr[1].isAnyInt()) { + globalObject.throw("Expected offset integer, got {any}", .{args.ptr[1].getZigString(globalObject)}); + return .zero; + } + + const offset = @minimum(args.ptr[1].toUInt64NoTruncate(), slice.len); + slice = slice[offset..]; + + if (args.len > 2) { + if (!args.ptr[2].isAnyInt()) { + globalObject.throw("Expected length integer, got {any}", .{args.ptr[2].getZigString(globalObject)}); + return .zero; + } + + const length = @minimum(args.ptr[2].toUInt64NoTruncate(), slice.len); + slice = slice[0..length]; + } + } + + return JSValue.jsNumber(this.writeMaybeCorked(slice, is_end)); + } else { + globalObject.throw("Expected ArrayBufferView, a string, or a Blob", .{}); + return .zero; + } + } + + pub fn flush( + this: *This, + _: *JSC.JSGlobalObject, + _: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + if (!this.detached) + this.socket.flush(); + + return JSValue.jsUndefined(); + } + + pub fn shutdown( + this: *This, + _: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + const args = callframe.arguments(1); + if (!this.detached) { + if (args.len > 0 and args.ptr[0].toBoolean()) { + this.socket.shutdownRead(); + } else { + this.socket.shutdown(); + } + } + + return JSValue.jsUndefined(); + } + + pub fn end( + this: *This, + globalObject: *JSC.JSGlobalObject, + callframe: *JSC.CallFrame, + ) callconv(.C) JSValue { + JSC.markBinding(@src()); + + const args = callframe.arguments(4); + + if (args.len == 0) { + log("end()", .{}); + if (!this.detached) { + if (!this.socket.isClosed()) this.socket.flush(); + this.detached = true; + this.markInactive(); + if (!this.socket.isClosed()) + this.socket.close(0, null); + } + + return JSValue.jsUndefined(); + } + + log("end({d} args)", .{args.len}); + + if (this.detached) { + return JSValue.jsNumber(@as(i32, -1)); + } + + const result = this.writeOrEnd(globalObject, args.ptr[0..args.len], true); + if (result != .zero and result.toInt32() > 0) { + this.socket.flush(); + this.detached = true; + this.markInactive(); + if (!this.socket.isClosed()) + this.socket.close(0, null); + } + + return result; + } + + pub fn ref(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(@src()); + if (this.detached) return JSValue.jsUndefined(); + this.poll_ref.ref(globalObject.bunVM()); + return JSValue.jsUndefined(); + } + + pub fn unref(this: *This, globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(@src()); + this.poll_ref.unref(globalObject.bunVM()); + return JSValue.jsUndefined(); + } + + pub fn finalize(this: *This) callconv(.C) void { + log("finalize()", .{}); + if (!this.detached and !this.socket.isClosed()) { + this.detached = true; + this.socket.close(0, null); + } + this.markInactive(); + if (this.poll_ref.isActive()) this.poll_ref.unref(JSC.VirtualMachine.vm); + } + + pub fn reload(this: *This, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + const args = callframe.arguments(1); + + if (args.len < 1) { + globalObject.throw("Expected 1 argument", .{}); + return .zero; + } + + if (this.detached) { + return JSValue.jsUndefined(); + } + + const opts = args.ptr[0]; + if (opts.isEmptyOrUndefinedOrNull() or opts.isBoolean() or !opts.isObject()) { + globalObject.throw("Expected options object", .{}); + return .zero; + } + + var exception: JSC.C.JSValueRef = null; + + var socket_obj = opts.get(globalObject, "socket") orelse { + globalObject.throw("Expected \"socket\" option", .{}); + return .zero; + }; + + const handlers = Handlers.fromJS(globalObject, socket_obj, &exception) orelse { + globalObject.throwValue(exception.?.value()); + return .zero; + }; + + var prev_handlers = this.handlers; + prev_handlers.unprotect(); + this.handlers.* = handlers; // TODO: this is a memory leak + this.handlers.protect(); + + return JSValue.jsUndefined(); + } + }; +} + +pub const TCPSocket = NewSocket(false); +pub const TLSSocket = NewSocket(true); diff --git a/src/bun.js/api/sockets.classes.ts b/src/bun.js/api/sockets.classes.ts new file mode 100644 index 000000000..0c72d1d8d --- /dev/null +++ b/src/bun.js/api/sockets.classes.ts @@ -0,0 +1,130 @@ +import { define } from "../scripts/class-definitions"; + +function generate(ssl) { + return define({ + name: ssl ? "TCPSocket" : "TLSSocket", + JSType: "0b11101110", + proto: { + write: { + fn: "write", + length: 3, + }, + end: { + fn: "end", + length: 3, + }, + + // }, + listener: { + getter: "getListener", + }, + + timeout: { + fn: "timeout", + length: 1, + }, + + flush: { + fn: "flush", + length: 0, + }, + + shutdown: { + fn: "shutdown", + length: 1, + }, + + ref: { + fn: "ref", + length: 0, + }, + unref: { + fn: "unref", + length: 0, + }, + + localPort: { + getter: "getLocalPort", + }, + // cork: { + // fn: "cork", + // length: 1, + // }, + data: { + getter: "getData", + cache: true, + setter: "setData", + }, + readyState: { + getter: "getReadyState", + }, + + // topics: { + // getter: "getTopics", + // }, + + remoteAddress: { + getter: "getRemoteAddress", + cache: true, + }, + + reload: { + fn: "reload", + length: 1, + }, + }, + finalize: true, + construct: true, + klass: {}, + }); +} +export default [ + generate(true), + generate(false), + define({ + name: "Listener", + JSType: "0b11101110", + proto: { + stop: { + fn: "stop", + length: 1, + }, + + ref: { + fn: "ref", + length: 0, + }, + unref: { + fn: "unref", + length: 0, + }, + + port: { + getter: "getPort", + }, + + unix: { + getter: "getUnix", + cache: true, + }, + + reload: { + fn: "reload", + length: 1, + }, + + hostname: { + getter: "getHostname", + cache: true, + }, + + data: { + getter: "getData", + setter: "setData", + }, + }, + finalize: true, + construct: true, + klass: {}, + }), +]; diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h index 5014f0d4e..3ec74c09c 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMClientIsoSubspaces.h @@ -1,4 +1,7 @@ -std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForSubprocess; +std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTCPSocket; +std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTCPSocketConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTLSSocket; +std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForTLSSocketConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForListener; +std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForListenerConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForSubprocess; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForSubprocessConstructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForSHA1; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForSHA1Constructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMD5; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMD5Constructor;std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForMD4; diff --git a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h index 149dfe8b8..11c6780ac 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+DOMIsoSubspaces.h @@ -1,4 +1,7 @@ -std::unique_ptr<IsoSubspace> m_subspaceForSubprocess; +std::unique_ptr<IsoSubspace> m_subspaceForTCPSocket; +std::unique_ptr<IsoSubspace> m_subspaceForTCPSocketConstructor;std::unique_ptr<IsoSubspace> m_subspaceForTLSSocket; +std::unique_ptr<IsoSubspace> m_subspaceForTLSSocketConstructor;std::unique_ptr<IsoSubspace> m_subspaceForListener; +std::unique_ptr<IsoSubspace> m_subspaceForListenerConstructor;std::unique_ptr<IsoSubspace> m_subspaceForSubprocess; std::unique_ptr<IsoSubspace> m_subspaceForSubprocessConstructor;std::unique_ptr<IsoSubspace> m_subspaceForSHA1; std::unique_ptr<IsoSubspace> m_subspaceForSHA1Constructor;std::unique_ptr<IsoSubspace> m_subspaceForMD5; std::unique_ptr<IsoSubspace> m_subspaceForMD5Constructor;std::unique_ptr<IsoSubspace> m_subspaceForMD4; diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h index 3de6262da..0ca7db3db 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureHeader.h @@ -1,3 +1,21 @@ +JSC::Structure* JSTCPSocketStructure() { return m_JSTCPSocket.getInitializedOnMainThread(this); } + JSC::JSObject* JSTCPSocketConstructor() { return m_JSTCPSocket.constructorInitializedOnMainThread(this); } + JSC::JSValue JSTCPSocketPrototype() { return m_JSTCPSocket.prototypeInitializedOnMainThread(this); } + JSC::LazyClassStructure m_JSTCPSocket; + bool hasJSTCPSocketSetterValue { false }; + mutable JSC::WriteBarrier<JSC::Unknown> m_JSTCPSocketSetterValue; +JSC::Structure* JSTLSSocketStructure() { return m_JSTLSSocket.getInitializedOnMainThread(this); } + JSC::JSObject* JSTLSSocketConstructor() { return m_JSTLSSocket.constructorInitializedOnMainThread(this); } + JSC::JSValue JSTLSSocketPrototype() { return m_JSTLSSocket.prototypeInitializedOnMainThread(this); } + JSC::LazyClassStructure m_JSTLSSocket; + bool hasJSTLSSocketSetterValue { false }; + mutable JSC::WriteBarrier<JSC::Unknown> m_JSTLSSocketSetterValue; +JSC::Structure* JSListenerStructure() { return m_JSListener.getInitializedOnMainThread(this); } + JSC::JSObject* JSListenerConstructor() { return m_JSListener.constructorInitializedOnMainThread(this); } + JSC::JSValue JSListenerPrototype() { return m_JSListener.prototypeInitializedOnMainThread(this); } + JSC::LazyClassStructure m_JSListener; + bool hasJSListenerSetterValue { false }; + mutable JSC::WriteBarrier<JSC::Unknown> m_JSListenerSetterValue; JSC::Structure* JSSubprocessStructure() { return m_JSSubprocess.getInitializedOnMainThread(this); } JSC::JSObject* JSSubprocessConstructor() { return m_JSSubprocess.constructorInitializedOnMainThread(this); } JSC::JSValue JSSubprocessPrototype() { return m_JSSubprocess.prototypeInitializedOnMainThread(this); } diff --git a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h index e449c2904..b1144a1e8 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h +++ b/src/bun.js/bindings/ZigGeneratedClasses+lazyStructureImpl.h @@ -1,4 +1,22 @@ void GlobalObject::initGeneratedLazyClasses() { + m_JSTCPSocket.initLater( + [](LazyClassStructure::Initializer& init) { + init.setPrototype(WebCore::JSTCPSocket::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global))); + init.setStructure(WebCore::JSTCPSocket::createStructure(init.vm, init.global, init.prototype)); + init.setConstructor(WebCore::JSTCPSocketConstructor::create(init.vm, init.global, WebCore::JSTCPSocketConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), jsCast<WebCore::JSTCPSocketPrototype*>(init.prototype))); + }); + m_JSTLSSocket.initLater( + [](LazyClassStructure::Initializer& init) { + init.setPrototype(WebCore::JSTLSSocket::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global))); + init.setStructure(WebCore::JSTLSSocket::createStructure(init.vm, init.global, init.prototype)); + init.setConstructor(WebCore::JSTLSSocketConstructor::create(init.vm, init.global, WebCore::JSTLSSocketConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), jsCast<WebCore::JSTLSSocketPrototype*>(init.prototype))); + }); + m_JSListener.initLater( + [](LazyClassStructure::Initializer& init) { + init.setPrototype(WebCore::JSListener::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global))); + init.setStructure(WebCore::JSListener::createStructure(init.vm, init.global, init.prototype)); + init.setConstructor(WebCore::JSListenerConstructor::create(init.vm, init.global, WebCore::JSListenerConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), jsCast<WebCore::JSListenerPrototype*>(init.prototype))); + }); m_JSSubprocess.initLater( [](LazyClassStructure::Initializer& init) { init.setPrototype(WebCore::JSSubprocess::createPrototype(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.global))); @@ -87,6 +105,9 @@ void GlobalObject::initGeneratedLazyClasses() { template<typename Visitor> void GlobalObject::visitGeneratedLazyClasses(GlobalObject *thisObject, Visitor& visitor) { + thisObject->m_JSTCPSocket.visit(visitor); visitor.append(thisObject->m_JSTCPSocketSetterValue); + thisObject->m_JSTLSSocket.visit(visitor); visitor.append(thisObject->m_JSTLSSocketSetterValue); + thisObject->m_JSListener.visit(visitor); visitor.append(thisObject->m_JSListenerSetterValue); thisObject->m_JSSubprocess.visit(visitor); visitor.append(thisObject->m_JSSubprocessSetterValue); thisObject->m_JSSHA1.visit(visitor); visitor.append(thisObject->m_JSSHA1SetterValue); thisObject->m_JSMD5.visit(visitor); visitor.append(thisObject->m_JSMD5SetterValue); diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp index 26fcec7b6..fad332acc 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses.cpp +++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp @@ -23,7 +23,1371 @@ namespace WebCore { using namespace JSC; using namespace Zig; -extern "C" void* SubprocessClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*); +extern "C" void* TCPSocketClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*); +JSC_DECLARE_CUSTOM_GETTER(jsTCPSocketConstructor); +extern "C" void TCPSocketClass__finalize(void*); + +extern "C" JSC::EncodedJSValue TCPSocketPrototype__getData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TCPSocketPrototype__dataGetterWrap); + + +extern "C" bool TCPSocketPrototype__setData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue value); +JSC_DECLARE_CUSTOM_SETTER(TCPSocketPrototype__dataSetterWrap); + + +extern "C" EncodedJSValue TCPSocketPrototype__end(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__endCallback); + + +extern "C" EncodedJSValue TCPSocketPrototype__flush(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__flushCallback); + + +extern "C" JSC::EncodedJSValue TCPSocketPrototype__getListener(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TCPSocketPrototype__listenerGetterWrap); + + +extern "C" JSC::EncodedJSValue TCPSocketPrototype__getLocalPort(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TCPSocketPrototype__localPortGetterWrap); + + +extern "C" JSC::EncodedJSValue TCPSocketPrototype__getReadyState(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TCPSocketPrototype__readyStateGetterWrap); + + +extern "C" EncodedJSValue TCPSocketPrototype__ref(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__refCallback); + + +extern "C" EncodedJSValue TCPSocketPrototype__reload(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__reloadCallback); + + +extern "C" JSC::EncodedJSValue TCPSocketPrototype__getRemoteAddress(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TCPSocketPrototype__remoteAddressGetterWrap); + + +extern "C" EncodedJSValue TCPSocketPrototype__shutdown(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__shutdownCallback); + + +extern "C" EncodedJSValue TCPSocketPrototype__timeout(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__timeoutCallback); + + +extern "C" EncodedJSValue TCPSocketPrototype__unref(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__unrefCallback); + + +extern "C" EncodedJSValue TCPSocketPrototype__write(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TCPSocketPrototype__writeCallback); + + +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTCPSocketPrototype, JSTCPSocketPrototype::Base); + + + static const HashTableValue JSTCPSocketPrototypeTableValues[] = { +{ "data"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TCPSocketPrototype__dataGetterWrap, TCPSocketPrototype__dataSetterWrap } } , +{ "end"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__endCallback, 3 } } , +{ "flush"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__flushCallback, 0 } } , +{ "listener"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TCPSocketPrototype__listenerGetterWrap, 0 } } , +{ "localPort"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TCPSocketPrototype__localPortGetterWrap, 0 } } , +{ "readyState"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TCPSocketPrototype__readyStateGetterWrap, 0 } } , +{ "ref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__refCallback, 0 } } , +{ "reload"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__reloadCallback, 1 } } , +{ "remoteAddress"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TCPSocketPrototype__remoteAddressGetterWrap, 0 } } , +{ "shutdown"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__shutdownCallback, 1 } } , +{ "timeout"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__timeoutCallback, 1 } } , +{ "unref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__unrefCallback, 0 } } , +{ "write"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TCPSocketPrototype__writeCallback, 3 } } + }; + + + +const ClassInfo JSTCPSocketPrototype::s_info = { "TCPSocket"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTCPSocketPrototype) }; + + + +JSC_DEFINE_CUSTOM_GETTER(jsTCPSocketConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto* prototype = jsDynamicCast<JSTCPSocketPrototype*>(JSValue::decode(thisValue)); + + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(globalObject->JSTCPSocketConstructor()); +} + + + +JSC_DEFINE_CUSTOM_GETTER(TCPSocketPrototype__dataGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTCPSocket* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->m_data.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + TCPSocketPrototype__getData(thisObject->wrapped(), globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->m_data.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); +} +extern "C" void TCPSocketPrototype__dataSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); + auto* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + thisObject->m_data.set(vm, thisObject, JSValue::decode(value)); +} + + +JSC_DEFINE_CUSTOM_SETTER(TCPSocketPrototype__dataSetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTCPSocket* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + auto result = TCPSocketPrototype__setData(thisObject->wrapped(), lexicalGlobalObject, encodedValue); + + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__endCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__end(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__flushCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__flush(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_CUSTOM_GETTER(TCPSocketPrototype__listenerGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTCPSocket* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = TCPSocketPrototype__getListener(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_CUSTOM_GETTER(TCPSocketPrototype__localPortGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTCPSocket* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = TCPSocketPrototype__getLocalPort(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_CUSTOM_GETTER(TCPSocketPrototype__readyStateGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTCPSocket* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = TCPSocketPrototype__getReadyState(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__refCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__ref(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__reloadCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__reload(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_CUSTOM_GETTER(TCPSocketPrototype__remoteAddressGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTCPSocket* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->m_remoteAddress.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + TCPSocketPrototype__getRemoteAddress(thisObject->wrapped(), globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->m_remoteAddress.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); +} +extern "C" void TCPSocketPrototype__remoteAddressSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); + auto* thisObject = jsCast<JSTCPSocket*>(JSValue::decode(thisValue)); + thisObject->m_remoteAddress.set(vm, thisObject, JSValue::decode(value)); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__shutdownCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__shutdown(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__timeoutCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__timeout(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__unrefCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__unref(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TCPSocketPrototype__writeCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTCPSocket* thisObject = jsDynamicCast<JSTCPSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TCPSocketPrototype__write(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +void JSTCPSocketPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSTCPSocket::info(), JSTCPSocketPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +void JSTCPSocketConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSTCPSocketPrototype* prototype) +{ + Base::finishCreation(vm, 0, "TCPSocket"_s, PropertyAdditionMode::WithoutStructureTransition); + + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + ASSERT(inherits(info())); +} + +JSTCPSocketConstructor* JSTCPSocketConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSTCPSocketPrototype* prototype) { + JSTCPSocketConstructor* ptr = new (NotNull, JSC::allocateCell<JSTCPSocketConstructor>(vm)) JSTCPSocketConstructor(vm, structure, construct); + ptr->finishCreation(vm, globalObject, prototype); + return ptr; +} + +JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSTCPSocketConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + JSC::VM &vm = globalObject->vm(); + JSObject* newTarget = asObject(callFrame->newTarget()); + auto* constructor = globalObject->JSTCPSocketConstructor(); + Structure* structure = globalObject->JSTCPSocketStructure(); + if (constructor != newTarget) { + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* functionGlobalObject = reinterpret_cast<Zig::GlobalObject*>( + // ShadowRealm functions belong to a different global object. + getFunctionRealm(globalObject, newTarget) + ); + RETURN_IF_EXCEPTION(scope, {}); + structure = InternalFunction::createSubclassStructure( + globalObject, + newTarget, + functionGlobalObject->JSTCPSocketStructure() + ); + } + + void* ptr = TCPSocketClass__construct(globalObject, callFrame); + + if (UNLIKELY(!ptr)) { + return JSValue::encode(JSC::jsUndefined()); + } + + JSTCPSocket* instance = JSTCPSocket::create(vm, globalObject, structure, ptr); + + + return JSValue::encode(instance); +} + +extern "C" EncodedJSValue TCPSocket__create(Zig::GlobalObject* globalObject, void* ptr) { + auto &vm = globalObject->vm(); + JSC::Structure* structure = globalObject->JSTCPSocketStructure(); + JSTCPSocket* instance = JSTCPSocket::create(vm, globalObject, structure, ptr); + + return JSValue::encode(instance); +} + +void JSTCPSocketConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSTCPSocketPrototype* prototype) +{ + +} + +const ClassInfo JSTCPSocketConstructor::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTCPSocketConstructor) }; + + + extern "C" EncodedJSValue TCPSocket__getConstructor(Zig::GlobalObject* globalObject) { + return JSValue::encode(globalObject->JSTCPSocketConstructor()); + } + +JSTCPSocket::~JSTCPSocket() +{ + if (m_ctx) { + TCPSocketClass__finalize(m_ctx); + } +} +void JSTCPSocket::destroy(JSCell* cell) +{ + static_cast<JSTCPSocket*>(cell)->JSTCPSocket::~JSTCPSocket(); +} + +const ClassInfo JSTCPSocket::s_info = { "TCPSocket"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTCPSocket) }; + +void JSTCPSocket::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +JSTCPSocket* JSTCPSocket::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx) { + JSTCPSocket* ptr = new (NotNull, JSC::allocateCell<JSTCPSocket>(vm)) JSTCPSocket(vm, structure, ctx); + ptr->finishCreation(vm); + return ptr; +} + +extern "C" void* TCPSocket__fromJS(JSC::EncodedJSValue value) { + JSC::JSValue decodedValue = JSC::JSValue::decode(value); + if (!decodedValue || decodedValue.isUndefinedOrNull()) + return nullptr; + + JSTCPSocket* object = JSC::jsDynamicCast<JSTCPSocket*>(decodedValue); + + if (!object) + return nullptr; + + return object->wrapped(); +} + +extern "C" bool TCPSocket__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr) { + JSTCPSocket* object = JSC::jsDynamicCast<JSTCPSocket*>(JSValue::decode(value)); + if (!object) + return false; + + object->m_ctx = ptr; + return true; +} + + +extern "C" const size_t TCPSocket__ptrOffset = JSTCPSocket::offsetOfWrapped(); + +void JSTCPSocket::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSTCPSocket*>(cell); + if (void* wrapped = thisObject->wrapped()) { + // if (thisObject->scriptExecutionContext()) + // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + } + Base::analyzeHeap(cell, analyzer); +} + +JSObject* JSTCPSocket::createPrototype(VM& vm, JSDOMGlobalObject* globalObject) +{ + return JSTCPSocketPrototype::create(vm, globalObject, JSTCPSocketPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); +} + +template<typename Visitor> +void JSTCPSocket::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSTCPSocket* thisObject = jsCast<JSTCPSocket*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + + visitor.append(thisObject->m_data); + visitor.append(thisObject->m_remoteAddress); +} + +DEFINE_VISIT_CHILDREN(JSTCPSocket);extern "C" void* TLSSocketClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*); +JSC_DECLARE_CUSTOM_GETTER(jsTLSSocketConstructor); +extern "C" void TLSSocketClass__finalize(void*); + +extern "C" JSC::EncodedJSValue TLSSocketPrototype__getData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TLSSocketPrototype__dataGetterWrap); + + +extern "C" bool TLSSocketPrototype__setData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue value); +JSC_DECLARE_CUSTOM_SETTER(TLSSocketPrototype__dataSetterWrap); + + +extern "C" EncodedJSValue TLSSocketPrototype__end(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__endCallback); + + +extern "C" EncodedJSValue TLSSocketPrototype__flush(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__flushCallback); + + +extern "C" JSC::EncodedJSValue TLSSocketPrototype__getListener(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TLSSocketPrototype__listenerGetterWrap); + + +extern "C" JSC::EncodedJSValue TLSSocketPrototype__getLocalPort(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TLSSocketPrototype__localPortGetterWrap); + + +extern "C" JSC::EncodedJSValue TLSSocketPrototype__getReadyState(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TLSSocketPrototype__readyStateGetterWrap); + + +extern "C" EncodedJSValue TLSSocketPrototype__ref(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__refCallback); + + +extern "C" EncodedJSValue TLSSocketPrototype__reload(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__reloadCallback); + + +extern "C" JSC::EncodedJSValue TLSSocketPrototype__getRemoteAddress(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(TLSSocketPrototype__remoteAddressGetterWrap); + + +extern "C" EncodedJSValue TLSSocketPrototype__shutdown(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__shutdownCallback); + + +extern "C" EncodedJSValue TLSSocketPrototype__timeout(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__timeoutCallback); + + +extern "C" EncodedJSValue TLSSocketPrototype__unref(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__unrefCallback); + + +extern "C" EncodedJSValue TLSSocketPrototype__write(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(TLSSocketPrototype__writeCallback); + + +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSTLSSocketPrototype, JSTLSSocketPrototype::Base); + + + static const HashTableValue JSTLSSocketPrototypeTableValues[] = { +{ "data"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TLSSocketPrototype__dataGetterWrap, TLSSocketPrototype__dataSetterWrap } } , +{ "end"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__endCallback, 3 } } , +{ "flush"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__flushCallback, 0 } } , +{ "listener"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TLSSocketPrototype__listenerGetterWrap, 0 } } , +{ "localPort"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TLSSocketPrototype__localPortGetterWrap, 0 } } , +{ "readyState"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TLSSocketPrototype__readyStateGetterWrap, 0 } } , +{ "ref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__refCallback, 0 } } , +{ "reload"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__reloadCallback, 1 } } , +{ "remoteAddress"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, TLSSocketPrototype__remoteAddressGetterWrap, 0 } } , +{ "shutdown"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__shutdownCallback, 1 } } , +{ "timeout"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__timeoutCallback, 1 } } , +{ "unref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__unrefCallback, 0 } } , +{ "write"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, TLSSocketPrototype__writeCallback, 3 } } + }; + + + +const ClassInfo JSTLSSocketPrototype::s_info = { "TLSSocket"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTLSSocketPrototype) }; + + + +JSC_DEFINE_CUSTOM_GETTER(jsTLSSocketConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto* prototype = jsDynamicCast<JSTLSSocketPrototype*>(JSValue::decode(thisValue)); + + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(globalObject->JSTLSSocketConstructor()); +} + + + +JSC_DEFINE_CUSTOM_GETTER(TLSSocketPrototype__dataGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTLSSocket* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->m_data.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + TLSSocketPrototype__getData(thisObject->wrapped(), globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->m_data.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); +} +extern "C" void TLSSocketPrototype__dataSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); + auto* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + thisObject->m_data.set(vm, thisObject, JSValue::decode(value)); +} + + +JSC_DEFINE_CUSTOM_SETTER(TLSSocketPrototype__dataSetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTLSSocket* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + auto result = TLSSocketPrototype__setData(thisObject->wrapped(), lexicalGlobalObject, encodedValue); + + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__endCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__end(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__flushCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__flush(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_CUSTOM_GETTER(TLSSocketPrototype__listenerGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTLSSocket* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = TLSSocketPrototype__getListener(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_CUSTOM_GETTER(TLSSocketPrototype__localPortGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTLSSocket* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = TLSSocketPrototype__getLocalPort(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_CUSTOM_GETTER(TLSSocketPrototype__readyStateGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTLSSocket* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = TLSSocketPrototype__getReadyState(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__refCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__ref(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__reloadCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__reload(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_CUSTOM_GETTER(TLSSocketPrototype__remoteAddressGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSTLSSocket* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->m_remoteAddress.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + TLSSocketPrototype__getRemoteAddress(thisObject->wrapped(), globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->m_remoteAddress.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); +} +extern "C" void TLSSocketPrototype__remoteAddressSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); + auto* thisObject = jsCast<JSTLSSocket*>(JSValue::decode(thisValue)); + thisObject->m_remoteAddress.set(vm, thisObject, JSValue::decode(value)); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__shutdownCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__shutdown(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__timeoutCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__timeout(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__unrefCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__unref(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(TLSSocketPrototype__writeCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSTLSSocket* thisObject = jsDynamicCast<JSTLSSocket*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return TLSSocketPrototype__write(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +void JSTLSSocketPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSTLSSocket::info(), JSTLSSocketPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +void JSTLSSocketConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSTLSSocketPrototype* prototype) +{ + Base::finishCreation(vm, 0, "TLSSocket"_s, PropertyAdditionMode::WithoutStructureTransition); + + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + ASSERT(inherits(info())); +} + +JSTLSSocketConstructor* JSTLSSocketConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSTLSSocketPrototype* prototype) { + JSTLSSocketConstructor* ptr = new (NotNull, JSC::allocateCell<JSTLSSocketConstructor>(vm)) JSTLSSocketConstructor(vm, structure, construct); + ptr->finishCreation(vm, globalObject, prototype); + return ptr; +} + +JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSTLSSocketConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + JSC::VM &vm = globalObject->vm(); + JSObject* newTarget = asObject(callFrame->newTarget()); + auto* constructor = globalObject->JSTLSSocketConstructor(); + Structure* structure = globalObject->JSTLSSocketStructure(); + if (constructor != newTarget) { + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* functionGlobalObject = reinterpret_cast<Zig::GlobalObject*>( + // ShadowRealm functions belong to a different global object. + getFunctionRealm(globalObject, newTarget) + ); + RETURN_IF_EXCEPTION(scope, {}); + structure = InternalFunction::createSubclassStructure( + globalObject, + newTarget, + functionGlobalObject->JSTLSSocketStructure() + ); + } + + void* ptr = TLSSocketClass__construct(globalObject, callFrame); + + if (UNLIKELY(!ptr)) { + return JSValue::encode(JSC::jsUndefined()); + } + + JSTLSSocket* instance = JSTLSSocket::create(vm, globalObject, structure, ptr); + + + return JSValue::encode(instance); +} + +extern "C" EncodedJSValue TLSSocket__create(Zig::GlobalObject* globalObject, void* ptr) { + auto &vm = globalObject->vm(); + JSC::Structure* structure = globalObject->JSTLSSocketStructure(); + JSTLSSocket* instance = JSTLSSocket::create(vm, globalObject, structure, ptr); + + return JSValue::encode(instance); +} + +void JSTLSSocketConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSTLSSocketPrototype* prototype) +{ + +} + +const ClassInfo JSTLSSocketConstructor::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTLSSocketConstructor) }; + + + extern "C" EncodedJSValue TLSSocket__getConstructor(Zig::GlobalObject* globalObject) { + return JSValue::encode(globalObject->JSTLSSocketConstructor()); + } + +JSTLSSocket::~JSTLSSocket() +{ + if (m_ctx) { + TLSSocketClass__finalize(m_ctx); + } +} +void JSTLSSocket::destroy(JSCell* cell) +{ + static_cast<JSTLSSocket*>(cell)->JSTLSSocket::~JSTLSSocket(); +} + +const ClassInfo JSTLSSocket::s_info = { "TLSSocket"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTLSSocket) }; + +void JSTLSSocket::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +JSTLSSocket* JSTLSSocket::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx) { + JSTLSSocket* ptr = new (NotNull, JSC::allocateCell<JSTLSSocket>(vm)) JSTLSSocket(vm, structure, ctx); + ptr->finishCreation(vm); + return ptr; +} + +extern "C" void* TLSSocket__fromJS(JSC::EncodedJSValue value) { + JSC::JSValue decodedValue = JSC::JSValue::decode(value); + if (!decodedValue || decodedValue.isUndefinedOrNull()) + return nullptr; + + JSTLSSocket* object = JSC::jsDynamicCast<JSTLSSocket*>(decodedValue); + + if (!object) + return nullptr; + + return object->wrapped(); +} + +extern "C" bool TLSSocket__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr) { + JSTLSSocket* object = JSC::jsDynamicCast<JSTLSSocket*>(JSValue::decode(value)); + if (!object) + return false; + + object->m_ctx = ptr; + return true; +} + + +extern "C" const size_t TLSSocket__ptrOffset = JSTLSSocket::offsetOfWrapped(); + +void JSTLSSocket::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSTLSSocket*>(cell); + if (void* wrapped = thisObject->wrapped()) { + // if (thisObject->scriptExecutionContext()) + // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + } + Base::analyzeHeap(cell, analyzer); +} + +JSObject* JSTLSSocket::createPrototype(VM& vm, JSDOMGlobalObject* globalObject) +{ + return JSTLSSocketPrototype::create(vm, globalObject, JSTLSSocketPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); +} + +template<typename Visitor> +void JSTLSSocket::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSTLSSocket* thisObject = jsCast<JSTLSSocket*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + + visitor.append(thisObject->m_data); + visitor.append(thisObject->m_remoteAddress); +} + +DEFINE_VISIT_CHILDREN(JSTLSSocket);extern "C" void* ListenerClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*); +JSC_DECLARE_CUSTOM_GETTER(jsListenerConstructor); +extern "C" void ListenerClass__finalize(void*); + +extern "C" JSC::EncodedJSValue ListenerPrototype__getData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(ListenerPrototype__dataGetterWrap); + + +extern "C" bool ListenerPrototype__setData(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::EncodedJSValue value); +JSC_DECLARE_CUSTOM_SETTER(ListenerPrototype__dataSetterWrap); + + +extern "C" JSC::EncodedJSValue ListenerPrototype__getHostname(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(ListenerPrototype__hostnameGetterWrap); + + +extern "C" JSC::EncodedJSValue ListenerPrototype__getPort(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(ListenerPrototype__portGetterWrap); + + +extern "C" EncodedJSValue ListenerPrototype__ref(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(ListenerPrototype__refCallback); + + +extern "C" EncodedJSValue ListenerPrototype__reload(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(ListenerPrototype__reloadCallback); + + +extern "C" EncodedJSValue ListenerPrototype__stop(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(ListenerPrototype__stopCallback); + + +extern "C" JSC::EncodedJSValue ListenerPrototype__getUnix(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); +JSC_DECLARE_CUSTOM_GETTER(ListenerPrototype__unixGetterWrap); + + +extern "C" EncodedJSValue ListenerPrototype__unref(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(ListenerPrototype__unrefCallback); + + +STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSListenerPrototype, JSListenerPrototype::Base); + + + static const HashTableValue JSListenerPrototypeTableValues[] = { +{ "data"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, ListenerPrototype__dataGetterWrap, ListenerPrototype__dataSetterWrap } } , +{ "hostname"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, ListenerPrototype__hostnameGetterWrap, 0 } } , +{ "port"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, ListenerPrototype__portGetterWrap, 0 } } , +{ "ref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ListenerPrototype__refCallback, 0 } } , +{ "reload"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ListenerPrototype__reloadCallback, 1 } } , +{ "stop"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ListenerPrototype__stopCallback, 1 } } , +{ "unix"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, ListenerPrototype__unixGetterWrap, 0 } } , +{ "unref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, ListenerPrototype__unrefCallback, 0 } } + }; + + + +const ClassInfo JSListenerPrototype::s_info = { "Listener"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSListenerPrototype) }; + + + +JSC_DEFINE_CUSTOM_GETTER(jsListenerConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto* prototype = jsDynamicCast<JSListenerPrototype*>(JSValue::decode(thisValue)); + + if (UNLIKELY(!prototype)) + return throwVMTypeError(lexicalGlobalObject, throwScope); + return JSValue::encode(globalObject->JSListenerConstructor()); +} + + + +JSC_DEFINE_CUSTOM_GETTER(ListenerPrototype__dataGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSListener* thisObject = jsCast<JSListener*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = ListenerPrototype__getData(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_CUSTOM_SETTER(ListenerPrototype__dataSetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSListener* thisObject = jsCast<JSListener*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + auto result = ListenerPrototype__setData(thisObject->wrapped(), lexicalGlobalObject, encodedValue); + + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_CUSTOM_GETTER(ListenerPrototype__hostnameGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSListener* thisObject = jsCast<JSListener*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->m_hostname.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + ListenerPrototype__getHostname(thisObject->wrapped(), globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->m_hostname.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); +} +extern "C" void ListenerPrototype__hostnameSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); + auto* thisObject = jsCast<JSListener*>(JSValue::decode(thisValue)); + thisObject->m_hostname.set(vm, thisObject, JSValue::decode(value)); +} + + +JSC_DEFINE_CUSTOM_GETTER(ListenerPrototype__portGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSListener* thisObject = jsCast<JSListener*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + JSC::EncodedJSValue result = ListenerPrototype__getPort(thisObject->wrapped(), globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + RELEASE_AND_RETURN(throwScope, result); +} + + +JSC_DEFINE_HOST_FUNCTION(ListenerPrototype__refCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSListener* thisObject = jsDynamicCast<JSListener*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return ListenerPrototype__ref(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(ListenerPrototype__reloadCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSListener* thisObject = jsDynamicCast<JSListener*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return ListenerPrototype__reload(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_HOST_FUNCTION(ListenerPrototype__stopCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSListener* thisObject = jsDynamicCast<JSListener*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return ListenerPrototype__stop(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +JSC_DEFINE_CUSTOM_GETTER(ListenerPrototype__unixGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) +{ + auto& vm = lexicalGlobalObject->vm(); + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSListener* thisObject = jsCast<JSListener*>(JSValue::decode(thisValue)); + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + if (JSValue cachedValue = thisObject->m_unix.get()) + return JSValue::encode(cachedValue); + + JSC::JSValue result = JSC::JSValue::decode( + ListenerPrototype__getUnix(thisObject->wrapped(), globalObject) + ); + RETURN_IF_EXCEPTION(throwScope, {}); + thisObject->m_unix.set(vm, thisObject, result); + RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); +} +extern "C" void ListenerPrototype__unixSetCachedValue(JSC::EncodedJSValue thisValue, JSC::JSGlobalObject *globalObject, JSC::EncodedJSValue value) +{ + auto& vm = globalObject->vm(); + auto* thisObject = jsCast<JSListener*>(JSValue::decode(thisValue)); + thisObject->m_unix.set(vm, thisObject, JSValue::decode(value)); +} + + +JSC_DEFINE_HOST_FUNCTION(ListenerPrototype__unrefCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSListener* thisObject = jsDynamicCast<JSListener*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + return throwVMTypeError(lexicalGlobalObject, throwScope); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + + return ListenerPrototype__unref(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + + +void JSListenerPrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSListener::info(), JSListenerPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +void JSListenerConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSListenerPrototype* prototype) +{ + Base::finishCreation(vm, 0, "Listener"_s, PropertyAdditionMode::WithoutStructureTransition); + + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); + ASSERT(inherits(info())); +} + +JSListenerConstructor* JSListenerConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSListenerPrototype* prototype) { + JSListenerConstructor* ptr = new (NotNull, JSC::allocateCell<JSListenerConstructor>(vm)) JSListenerConstructor(vm, structure, construct); + ptr->finishCreation(vm, globalObject, prototype); + return ptr; +} + +JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSListenerConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + Zig::GlobalObject *globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + JSC::VM &vm = globalObject->vm(); + JSObject* newTarget = asObject(callFrame->newTarget()); + auto* constructor = globalObject->JSListenerConstructor(); + Structure* structure = globalObject->JSListenerStructure(); + if (constructor != newTarget) { + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* functionGlobalObject = reinterpret_cast<Zig::GlobalObject*>( + // ShadowRealm functions belong to a different global object. + getFunctionRealm(globalObject, newTarget) + ); + RETURN_IF_EXCEPTION(scope, {}); + structure = InternalFunction::createSubclassStructure( + globalObject, + newTarget, + functionGlobalObject->JSListenerStructure() + ); + } + + void* ptr = ListenerClass__construct(globalObject, callFrame); + + if (UNLIKELY(!ptr)) { + return JSValue::encode(JSC::jsUndefined()); + } + + JSListener* instance = JSListener::create(vm, globalObject, structure, ptr); + + + return JSValue::encode(instance); +} + +extern "C" EncodedJSValue Listener__create(Zig::GlobalObject* globalObject, void* ptr) { + auto &vm = globalObject->vm(); + JSC::Structure* structure = globalObject->JSListenerStructure(); + JSListener* instance = JSListener::create(vm, globalObject, structure, ptr); + + return JSValue::encode(instance); +} + +void JSListenerConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSListenerPrototype* prototype) +{ + +} + +const ClassInfo JSListenerConstructor::s_info = { "Function"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSListenerConstructor) }; + + + extern "C" EncodedJSValue Listener__getConstructor(Zig::GlobalObject* globalObject) { + return JSValue::encode(globalObject->JSListenerConstructor()); + } + +JSListener::~JSListener() +{ + if (m_ctx) { + ListenerClass__finalize(m_ctx); + } +} +void JSListener::destroy(JSCell* cell) +{ + static_cast<JSListener*>(cell)->JSListener::~JSListener(); +} + +const ClassInfo JSListener::s_info = { "Listener"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSListener) }; + +void JSListener::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +JSListener* JSListener::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx) { + JSListener* ptr = new (NotNull, JSC::allocateCell<JSListener>(vm)) JSListener(vm, structure, ctx); + ptr->finishCreation(vm); + return ptr; +} + +extern "C" void* Listener__fromJS(JSC::EncodedJSValue value) { + JSC::JSValue decodedValue = JSC::JSValue::decode(value); + if (!decodedValue || decodedValue.isUndefinedOrNull()) + return nullptr; + + JSListener* object = JSC::jsDynamicCast<JSListener*>(decodedValue); + + if (!object) + return nullptr; + + return object->wrapped(); +} + +extern "C" bool Listener__dangerouslySetPtr(JSC::EncodedJSValue value, void* ptr) { + JSListener* object = JSC::jsDynamicCast<JSListener*>(JSValue::decode(value)); + if (!object) + return false; + + object->m_ctx = ptr; + return true; +} + + +extern "C" const size_t Listener__ptrOffset = JSListener::offsetOfWrapped(); + +void JSListener::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) +{ + auto* thisObject = jsCast<JSListener*>(cell); + if (void* wrapped = thisObject->wrapped()) { + // if (thisObject->scriptExecutionContext()) + // analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + } + Base::analyzeHeap(cell, analyzer); +} + +JSObject* JSListener::createPrototype(VM& vm, JSDOMGlobalObject* globalObject) +{ + return JSListenerPrototype::create(vm, globalObject, JSListenerPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); +} + +template<typename Visitor> +void JSListener::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSListener* thisObject = jsCast<JSListener*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + + + visitor.append(thisObject->m_hostname); + visitor.append(thisObject->m_unix); +} + +DEFINE_VISIT_CHILDREN(JSListener);extern "C" void* SubprocessClass__construct(JSC::JSGlobalObject*, JSC::CallFrame*); JSC_DECLARE_CUSTOM_GETTER(jsSubprocessConstructor); extern "C" void SubprocessClass__finalize(void*); diff --git a/src/bun.js/bindings/ZigGeneratedClasses.h b/src/bun.js/bindings/ZigGeneratedClasses.h index 45e47f81a..c344437cc 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses.h +++ b/src/bun.js/bindings/ZigGeneratedClasses.h @@ -15,6 +15,381 @@ namespace WebCore { using namespace Zig; using namespace JSC; +class JSTCPSocket final : public JSC::JSDestructibleObject { + public: + using Base = JSC::JSDestructibleObject; + static JSTCPSocket* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx); + + DECLARE_EXPORT_INFO; + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSTCPSocket, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForTCPSocket.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTCPSocket = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForTCPSocket.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForTCPSocket = WTFMove(space); }); + } + + static void destroy(JSC::JSCell*); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info()); + } + + static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject); + + ~JSTCPSocket(); + + void* wrapped() const { return m_ctx; } + + void detach() + { + m_ctx = nullptr; + } + + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSTCPSocket, m_ctx); } + + void* m_ctx { nullptr }; + + + JSTCPSocket(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + : Base(vm, structure) + { + m_ctx = sinkPtr; + } + + void finishCreation(JSC::VM&); + + DECLARE_VISIT_CHILDREN; + + mutable JSC::WriteBarrier<JSC::Unknown> m_data; +mutable JSC::WriteBarrier<JSC::Unknown> m_remoteAddress; + }; +class JSTCPSocketPrototype final : public JSC::JSNonFinalObject { + public: + using Base = JSC::JSNonFinalObject; + + static JSTCPSocketPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSTCPSocketPrototype* ptr = new (NotNull, JSC::allocateCell<JSTCPSocketPrototype>(vm)) JSTCPSocketPrototype(vm, globalObject, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + private: + JSTCPSocketPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); + }; + + class JSTCPSocketConstructor final : public JSC::InternalFunction { + public: + using Base = JSC::InternalFunction; + static JSTCPSocketConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSTCPSocketPrototype* prototype); + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSTCPSocketConstructor, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForTCPSocketConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTCPSocketConstructor = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForTCPSocketConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForTCPSocketConstructor = WTFMove(space); }); + } + + + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSTCPSocketPrototype* prototype); + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + DECLARE_EXPORT_INFO; + private: + JSTCPSocketConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) + : Base(vm, structure, nativeFunction, nativeFunction) + + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSTCPSocketPrototype* prototype); + }; +class JSTLSSocket final : public JSC::JSDestructibleObject { + public: + using Base = JSC::JSDestructibleObject; + static JSTLSSocket* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx); + + DECLARE_EXPORT_INFO; + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSTLSSocket, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForTLSSocket.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTLSSocket = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForTLSSocket.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForTLSSocket = WTFMove(space); }); + } + + static void destroy(JSC::JSCell*); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info()); + } + + static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject); + + ~JSTLSSocket(); + + void* wrapped() const { return m_ctx; } + + void detach() + { + m_ctx = nullptr; + } + + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSTLSSocket, m_ctx); } + + void* m_ctx { nullptr }; + + + JSTLSSocket(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + : Base(vm, structure) + { + m_ctx = sinkPtr; + } + + void finishCreation(JSC::VM&); + + DECLARE_VISIT_CHILDREN; + + mutable JSC::WriteBarrier<JSC::Unknown> m_data; +mutable JSC::WriteBarrier<JSC::Unknown> m_remoteAddress; + }; +class JSTLSSocketPrototype final : public JSC::JSNonFinalObject { + public: + using Base = JSC::JSNonFinalObject; + + static JSTLSSocketPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSTLSSocketPrototype* ptr = new (NotNull, JSC::allocateCell<JSTLSSocketPrototype>(vm)) JSTLSSocketPrototype(vm, globalObject, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + private: + JSTLSSocketPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); + }; + + class JSTLSSocketConstructor final : public JSC::InternalFunction { + public: + using Base = JSC::InternalFunction; + static JSTLSSocketConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSTLSSocketPrototype* prototype); + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSTLSSocketConstructor, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForTLSSocketConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForTLSSocketConstructor = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForTLSSocketConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForTLSSocketConstructor = WTFMove(space); }); + } + + + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSTLSSocketPrototype* prototype); + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + DECLARE_EXPORT_INFO; + private: + JSTLSSocketConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) + : Base(vm, structure, nativeFunction, nativeFunction) + + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSTLSSocketPrototype* prototype); + }; +class JSListener final : public JSC::JSDestructibleObject { + public: + using Base = JSC::JSDestructibleObject; + static JSListener* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, void* ctx); + + DECLARE_EXPORT_INFO; + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSListener, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForListener.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForListener = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForListener.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForListener = WTFMove(space); }); + } + + static void destroy(JSC::JSCell*); + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(static_cast<JSC::JSType>(0b11101110), StructureFlags), info()); + } + + static JSObject* createPrototype(VM& vm, JSDOMGlobalObject* globalObject); + + ~JSListener(); + + void* wrapped() const { return m_ctx; } + + void detach() + { + m_ctx = nullptr; + } + + static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSListener, m_ctx); } + + void* m_ctx { nullptr }; + + + JSListener(JSC::VM& vm, JSC::Structure* structure, void* sinkPtr) + : Base(vm, structure) + { + m_ctx = sinkPtr; + } + + void finishCreation(JSC::VM&); + + DECLARE_VISIT_CHILDREN; + + mutable JSC::WriteBarrier<JSC::Unknown> m_hostname; +mutable JSC::WriteBarrier<JSC::Unknown> m_unix; + }; +class JSListenerPrototype final : public JSC::JSNonFinalObject { + public: + using Base = JSC::JSNonFinalObject; + + static JSListenerPrototype* create(JSC::VM& vm, JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSListenerPrototype* ptr = new (NotNull, JSC::allocateCell<JSListenerPrototype>(vm)) JSListenerPrototype(vm, globalObject, structure); + ptr->finishCreation(vm, globalObject); + return ptr; + } + + DECLARE_INFO; + template<typename CellType, JSC::SubspaceAccess> + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + } + + private: + JSListenerPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject*); + }; + + class JSListenerConstructor final : public JSC::InternalFunction { + public: + using Base = JSC::InternalFunction; + static JSListenerConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSListenerPrototype* prototype); + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr bool needsDestruction = false; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl<JSListenerConstructor, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForListenerConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForListenerConstructor = WTFMove(space); }, + [](auto& spaces) { return spaces.m_subspaceForListenerConstructor.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForListenerConstructor = WTFMove(space); }); + } + + + void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSListenerPrototype* prototype); + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + DECLARE_EXPORT_INFO; + private: + JSListenerConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) + : Base(vm, structure, nativeFunction, nativeFunction) + + { + } + + void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSListenerPrototype* prototype); + }; class JSSubprocess final : public JSC::JSDestructibleObject { public: using Base = JSC::JSDestructibleObject; diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index c19d3888b..7918e9197 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -163,8 +163,8 @@ using JSBuffer = WebCore::JSBuffer; #include "DOMJITHelpers.h" #include <JavaScriptCore/DFGAbstractHeap.h> -#include "JSCryptoKey.h" -#include "JSSubtleCrypto.h" +#include "webcrypto/JSCryptoKey.h" +#include "webcrypto/JSSubtleCrypto.h" #include "OnigurumaRegExp.h" diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 6a89db7b9..6d5f87662 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -1486,7 +1486,7 @@ pub const JSPromise = extern struct { ) JSValue { if (value.isEmpty()) { return resolvedPromiseValue(globalObject, JSValue.jsUndefined()); - } else if (value.isEmptyOrUndefinedOrNull()) { + } else if (value.isEmptyOrUndefinedOrNull() or !value.isCell()) { return resolvedPromiseValue(globalObject, value); } diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig index 5cb04779a..062372efe 100644 --- a/src/bun.js/bindings/generated_classes.zig +++ b/src/bun.js/bindings/generated_classes.zig @@ -7,6 +7,365 @@ pub const StaticGetterType = fn (*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue) pub const StaticSetterType = fn (*JSC.JSGlobalObject, JSC.JSValue, JSC.JSValue, JSC.JSValue) callconv(.C) bool; pub const StaticCallbackType = fn (*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue; +pub const JSTCPSocket = struct { + const TCPSocket = Classes.TCPSocket; + const GetterType = fn (*TCPSocket, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; + const SetterType = fn (*TCPSocket, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool; + const CallbackType = fn (*TCPSocket, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue; + + /// Return the pointer to the wrapped object. + /// If the object does not match the type, return null. + pub fn fromJS(value: JSC.JSValue) ?*TCPSocket { + JSC.markBinding(@src()); + return TCPSocket__fromJS(value); + } + + extern fn TCPSocketPrototype__dataSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + + /// Set the cached value for data on TCPSocket + /// This value will be visited by the garbage collector. + pub fn dataSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { + JSC.markBinding(@src()); + TCPSocketPrototype__dataSetCachedValue(thisValue, globalObject, value); + } + + extern fn TCPSocketPrototype__remoteAddressSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + + /// Set the cached value for remoteAddress on TCPSocket + /// This value will be visited by the garbage collector. + pub fn remoteAddressSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { + JSC.markBinding(@src()); + TCPSocketPrototype__remoteAddressSetCachedValue(thisValue, globalObject, value); + } + + /// Get the TCPSocket constructor value. + /// This loads lazily from the global object. + pub fn getConstructor(globalObject: *JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + return TCPSocket__getConstructor(globalObject); + } + + /// Create a new instance of TCPSocket + pub fn toJS(this: *TCPSocket, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + if (comptime Environment.allow_assert) { + const value__ = TCPSocket__create(globalObject, this); + std.debug.assert(value__.as(TCPSocket).? == this); // If this fails, likely a C ABI issue. + return value__; + } else { + return TCPSocket__create(globalObject, this); + } + } + + /// Modify the internal ptr to point to a new instance of TCPSocket. + pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*TCPSocket) bool { + JSC.markBinding(@src()); + return TCPSocket__dangerouslySetPtr(value, ptr); + } + + extern fn TCPSocket__fromJS(JSC.JSValue) ?*TCPSocket; + extern fn TCPSocket__getConstructor(*JSC.JSGlobalObject) JSC.JSValue; + + extern fn TCPSocket__create(globalObject: *JSC.JSGlobalObject, ptr: ?*TCPSocket) JSC.JSValue; + + extern fn TCPSocket__dangerouslySetPtr(JSC.JSValue, ?*TCPSocket) bool; + + comptime { + if (@TypeOf(TCPSocket.constructor) != (fn (*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) ?*TCPSocket)) { + @compileLog("TCPSocket.constructor is not a constructor"); + } + + if (@TypeOf(TCPSocket.finalize) != (fn (*TCPSocket) callconv(.C) void)) { + @compileLog("TCPSocket.finalize is not a finalizer"); + } + + if (@TypeOf(TCPSocket.getData) != GetterType) + @compileLog("Expected TCPSocket.getData to be a getter"); + + if (@TypeOf(TCPSocket.setData) != SetterType) + @compileLog("Expected TCPSocket.setData to be a setter"); + if (@TypeOf(TCPSocket.end) != CallbackType) + @compileLog("Expected TCPSocket.end to be a callback"); + if (@TypeOf(TCPSocket.flush) != CallbackType) + @compileLog("Expected TCPSocket.flush to be a callback"); + if (@TypeOf(TCPSocket.getListener) != GetterType) + @compileLog("Expected TCPSocket.getListener to be a getter"); + + if (@TypeOf(TCPSocket.getLocalPort) != GetterType) + @compileLog("Expected TCPSocket.getLocalPort to be a getter"); + + if (@TypeOf(TCPSocket.getReadyState) != GetterType) + @compileLog("Expected TCPSocket.getReadyState to be a getter"); + + if (@TypeOf(TCPSocket.ref) != CallbackType) + @compileLog("Expected TCPSocket.ref to be a callback"); + if (@TypeOf(TCPSocket.reload) != CallbackType) + @compileLog("Expected TCPSocket.reload to be a callback"); + if (@TypeOf(TCPSocket.getRemoteAddress) != GetterType) + @compileLog("Expected TCPSocket.getRemoteAddress to be a getter"); + + if (@TypeOf(TCPSocket.shutdown) != CallbackType) + @compileLog("Expected TCPSocket.shutdown to be a callback"); + if (@TypeOf(TCPSocket.timeout) != CallbackType) + @compileLog("Expected TCPSocket.timeout to be a callback"); + if (@TypeOf(TCPSocket.unref) != CallbackType) + @compileLog("Expected TCPSocket.unref to be a callback"); + if (@TypeOf(TCPSocket.write) != CallbackType) + @compileLog("Expected TCPSocket.write to be a callback"); + if (!JSC.is_bindgen) { + @export(TCPSocket.constructor, .{ .name = "TCPSocketClass__construct" }); + @export(TCPSocket.end, .{ .name = "TCPSocketPrototype__end" }); + @export(TCPSocket.finalize, .{ .name = "TCPSocketClass__finalize" }); + @export(TCPSocket.flush, .{ .name = "TCPSocketPrototype__flush" }); + @export(TCPSocket.getData, .{ .name = "TCPSocketPrototype__getData" }); + @export(TCPSocket.getListener, .{ .name = "TCPSocketPrototype__getListener" }); + @export(TCPSocket.getLocalPort, .{ .name = "TCPSocketPrototype__getLocalPort" }); + @export(TCPSocket.getReadyState, .{ .name = "TCPSocketPrototype__getReadyState" }); + @export(TCPSocket.getRemoteAddress, .{ .name = "TCPSocketPrototype__getRemoteAddress" }); + @export(TCPSocket.ref, .{ .name = "TCPSocketPrototype__ref" }); + @export(TCPSocket.reload, .{ .name = "TCPSocketPrototype__reload" }); + @export(TCPSocket.setData, .{ .name = "TCPSocketPrototype__setData" }); + @export(TCPSocket.shutdown, .{ .name = "TCPSocketPrototype__shutdown" }); + @export(TCPSocket.timeout, .{ .name = "TCPSocketPrototype__timeout" }); + @export(TCPSocket.unref, .{ .name = "TCPSocketPrototype__unref" }); + @export(TCPSocket.write, .{ .name = "TCPSocketPrototype__write" }); + } + } +}; +pub const JSTLSSocket = struct { + const TLSSocket = Classes.TLSSocket; + const GetterType = fn (*TLSSocket, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; + const SetterType = fn (*TLSSocket, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool; + const CallbackType = fn (*TLSSocket, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue; + + /// Return the pointer to the wrapped object. + /// If the object does not match the type, return null. + pub fn fromJS(value: JSC.JSValue) ?*TLSSocket { + JSC.markBinding(@src()); + return TLSSocket__fromJS(value); + } + + extern fn TLSSocketPrototype__dataSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + + /// Set the cached value for data on TLSSocket + /// This value will be visited by the garbage collector. + pub fn dataSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { + JSC.markBinding(@src()); + TLSSocketPrototype__dataSetCachedValue(thisValue, globalObject, value); + } + + extern fn TLSSocketPrototype__remoteAddressSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + + /// Set the cached value for remoteAddress on TLSSocket + /// This value will be visited by the garbage collector. + pub fn remoteAddressSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { + JSC.markBinding(@src()); + TLSSocketPrototype__remoteAddressSetCachedValue(thisValue, globalObject, value); + } + + /// Get the TLSSocket constructor value. + /// This loads lazily from the global object. + pub fn getConstructor(globalObject: *JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + return TLSSocket__getConstructor(globalObject); + } + + /// Create a new instance of TLSSocket + pub fn toJS(this: *TLSSocket, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + if (comptime Environment.allow_assert) { + const value__ = TLSSocket__create(globalObject, this); + std.debug.assert(value__.as(TLSSocket).? == this); // If this fails, likely a C ABI issue. + return value__; + } else { + return TLSSocket__create(globalObject, this); + } + } + + /// Modify the internal ptr to point to a new instance of TLSSocket. + pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*TLSSocket) bool { + JSC.markBinding(@src()); + return TLSSocket__dangerouslySetPtr(value, ptr); + } + + extern fn TLSSocket__fromJS(JSC.JSValue) ?*TLSSocket; + extern fn TLSSocket__getConstructor(*JSC.JSGlobalObject) JSC.JSValue; + + extern fn TLSSocket__create(globalObject: *JSC.JSGlobalObject, ptr: ?*TLSSocket) JSC.JSValue; + + extern fn TLSSocket__dangerouslySetPtr(JSC.JSValue, ?*TLSSocket) bool; + + comptime { + if (@TypeOf(TLSSocket.constructor) != (fn (*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) ?*TLSSocket)) { + @compileLog("TLSSocket.constructor is not a constructor"); + } + + if (@TypeOf(TLSSocket.finalize) != (fn (*TLSSocket) callconv(.C) void)) { + @compileLog("TLSSocket.finalize is not a finalizer"); + } + + if (@TypeOf(TLSSocket.getData) != GetterType) + @compileLog("Expected TLSSocket.getData to be a getter"); + + if (@TypeOf(TLSSocket.setData) != SetterType) + @compileLog("Expected TLSSocket.setData to be a setter"); + if (@TypeOf(TLSSocket.end) != CallbackType) + @compileLog("Expected TLSSocket.end to be a callback"); + if (@TypeOf(TLSSocket.flush) != CallbackType) + @compileLog("Expected TLSSocket.flush to be a callback"); + if (@TypeOf(TLSSocket.getListener) != GetterType) + @compileLog("Expected TLSSocket.getListener to be a getter"); + + if (@TypeOf(TLSSocket.getLocalPort) != GetterType) + @compileLog("Expected TLSSocket.getLocalPort to be a getter"); + + if (@TypeOf(TLSSocket.getReadyState) != GetterType) + @compileLog("Expected TLSSocket.getReadyState to be a getter"); + + if (@TypeOf(TLSSocket.ref) != CallbackType) + @compileLog("Expected TLSSocket.ref to be a callback"); + if (@TypeOf(TLSSocket.reload) != CallbackType) + @compileLog("Expected TLSSocket.reload to be a callback"); + if (@TypeOf(TLSSocket.getRemoteAddress) != GetterType) + @compileLog("Expected TLSSocket.getRemoteAddress to be a getter"); + + if (@TypeOf(TLSSocket.shutdown) != CallbackType) + @compileLog("Expected TLSSocket.shutdown to be a callback"); + if (@TypeOf(TLSSocket.timeout) != CallbackType) + @compileLog("Expected TLSSocket.timeout to be a callback"); + if (@TypeOf(TLSSocket.unref) != CallbackType) + @compileLog("Expected TLSSocket.unref to be a callback"); + if (@TypeOf(TLSSocket.write) != CallbackType) + @compileLog("Expected TLSSocket.write to be a callback"); + if (!JSC.is_bindgen) { + @export(TLSSocket.constructor, .{ .name = "TLSSocketClass__construct" }); + @export(TLSSocket.end, .{ .name = "TLSSocketPrototype__end" }); + @export(TLSSocket.finalize, .{ .name = "TLSSocketClass__finalize" }); + @export(TLSSocket.flush, .{ .name = "TLSSocketPrototype__flush" }); + @export(TLSSocket.getData, .{ .name = "TLSSocketPrototype__getData" }); + @export(TLSSocket.getListener, .{ .name = "TLSSocketPrototype__getListener" }); + @export(TLSSocket.getLocalPort, .{ .name = "TLSSocketPrototype__getLocalPort" }); + @export(TLSSocket.getReadyState, .{ .name = "TLSSocketPrototype__getReadyState" }); + @export(TLSSocket.getRemoteAddress, .{ .name = "TLSSocketPrototype__getRemoteAddress" }); + @export(TLSSocket.ref, .{ .name = "TLSSocketPrototype__ref" }); + @export(TLSSocket.reload, .{ .name = "TLSSocketPrototype__reload" }); + @export(TLSSocket.setData, .{ .name = "TLSSocketPrototype__setData" }); + @export(TLSSocket.shutdown, .{ .name = "TLSSocketPrototype__shutdown" }); + @export(TLSSocket.timeout, .{ .name = "TLSSocketPrototype__timeout" }); + @export(TLSSocket.unref, .{ .name = "TLSSocketPrototype__unref" }); + @export(TLSSocket.write, .{ .name = "TLSSocketPrototype__write" }); + } + } +}; +pub const JSListener = struct { + const Listener = Classes.Listener; + const GetterType = fn (*Listener, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; + const SetterType = fn (*Listener, *JSC.JSGlobalObject, JSC.JSValue) callconv(.C) bool; + const CallbackType = fn (*Listener, *JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) JSC.JSValue; + + /// Return the pointer to the wrapped object. + /// If the object does not match the type, return null. + pub fn fromJS(value: JSC.JSValue) ?*Listener { + JSC.markBinding(@src()); + return Listener__fromJS(value); + } + + extern fn ListenerPrototype__hostnameSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + + /// Set the cached value for hostname on Listener + /// This value will be visited by the garbage collector. + pub fn hostnameSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { + JSC.markBinding(@src()); + ListenerPrototype__hostnameSetCachedValue(thisValue, globalObject, value); + } + + extern fn ListenerPrototype__unixSetCachedValue(JSC.JSValue, *JSC.JSGlobalObject, JSC.JSValue) void; + + /// Set the cached value for unix on Listener + /// This value will be visited by the garbage collector. + pub fn unixSetCached(thisValue: JSC.JSValue, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { + JSC.markBinding(@src()); + ListenerPrototype__unixSetCachedValue(thisValue, globalObject, value); + } + + /// Get the Listener constructor value. + /// This loads lazily from the global object. + pub fn getConstructor(globalObject: *JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + return Listener__getConstructor(globalObject); + } + + /// Create a new instance of Listener + pub fn toJS(this: *Listener, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + if (comptime Environment.allow_assert) { + const value__ = Listener__create(globalObject, this); + std.debug.assert(value__.as(Listener).? == this); // If this fails, likely a C ABI issue. + return value__; + } else { + return Listener__create(globalObject, this); + } + } + + /// Modify the internal ptr to point to a new instance of Listener. + pub fn dangerouslySetPtr(value: JSC.JSValue, ptr: ?*Listener) bool { + JSC.markBinding(@src()); + return Listener__dangerouslySetPtr(value, ptr); + } + + extern fn Listener__fromJS(JSC.JSValue) ?*Listener; + extern fn Listener__getConstructor(*JSC.JSGlobalObject) JSC.JSValue; + + extern fn Listener__create(globalObject: *JSC.JSGlobalObject, ptr: ?*Listener) JSC.JSValue; + + extern fn Listener__dangerouslySetPtr(JSC.JSValue, ?*Listener) bool; + + comptime { + if (@TypeOf(Listener.constructor) != (fn (*JSC.JSGlobalObject, *JSC.CallFrame) callconv(.C) ?*Listener)) { + @compileLog("Listener.constructor is not a constructor"); + } + + if (@TypeOf(Listener.finalize) != (fn (*Listener) callconv(.C) void)) { + @compileLog("Listener.finalize is not a finalizer"); + } + + if (@TypeOf(Listener.getData) != GetterType) + @compileLog("Expected Listener.getData to be a getter"); + + if (@TypeOf(Listener.setData) != SetterType) + @compileLog("Expected Listener.setData to be a setter"); + if (@TypeOf(Listener.getHostname) != GetterType) + @compileLog("Expected Listener.getHostname to be a getter"); + + if (@TypeOf(Listener.getPort) != GetterType) + @compileLog("Expected Listener.getPort to be a getter"); + + if (@TypeOf(Listener.ref) != CallbackType) + @compileLog("Expected Listener.ref to be a callback"); + if (@TypeOf(Listener.reload) != CallbackType) + @compileLog("Expected Listener.reload to be a callback"); + if (@TypeOf(Listener.stop) != CallbackType) + @compileLog("Expected Listener.stop to be a callback"); + if (@TypeOf(Listener.getUnix) != GetterType) + @compileLog("Expected Listener.getUnix to be a getter"); + + if (@TypeOf(Listener.unref) != CallbackType) + @compileLog("Expected Listener.unref to be a callback"); + if (!JSC.is_bindgen) { + @export(Listener.constructor, .{ .name = "ListenerClass__construct" }); + @export(Listener.finalize, .{ .name = "ListenerClass__finalize" }); + @export(Listener.getData, .{ .name = "ListenerPrototype__getData" }); + @export(Listener.getHostname, .{ .name = "ListenerPrototype__getHostname" }); + @export(Listener.getPort, .{ .name = "ListenerPrototype__getPort" }); + @export(Listener.getUnix, .{ .name = "ListenerPrototype__getUnix" }); + @export(Listener.ref, .{ .name = "ListenerPrototype__ref" }); + @export(Listener.reload, .{ .name = "ListenerPrototype__reload" }); + @export(Listener.setData, .{ .name = "ListenerPrototype__setData" }); + @export(Listener.stop, .{ .name = "ListenerPrototype__stop" }); + @export(Listener.unref, .{ .name = "ListenerPrototype__unref" }); + } + } +}; pub const JSSubprocess = struct { const Subprocess = Classes.Subprocess; const GetterType = fn (*Subprocess, *JSC.JSGlobalObject) callconv(.C) JSC.JSValue; @@ -1392,6 +1751,9 @@ pub const JSBlob = struct { }; comptime { + _ = JSTCPSocket; + _ = JSTLSSocket; + _ = JSListener; _ = JSSubprocess; _ = JSSHA1; _ = JSMD5; diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig index 0f74343f6..acae606e3 100644 --- a/src/bun.js/bindings/generated_classes_list.zig +++ b/src/bun.js/bindings/generated_classes_list.zig @@ -15,4 +15,7 @@ pub const Classes = struct { pub const Blob = JSC.WebCore.Blob; pub const Subprocess = JSC.Subprocess; pub const ServerWebSocket = JSC.API.ServerWebSocket; + pub const TCPSocket = JSC.API.TCPSocket; + pub const TLSSocket = JSC.API.TLSSocket; + pub const Listener = JSC.API.Listener; }; diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h index 539e71afc..47ca7ef31 100644 --- a/src/bun.js/bindings/headers-cpp.h +++ b/src/bun.js/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1666254195 +//-- AUTOGENERATED FILE -- 1666335003 // clang-format off #pragma once diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index de4eb42d4..d0b3cc938 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format off -//-- AUTOGENERATED FILE -- 1666254195 +//-- AUTOGENERATED FILE -- 1666335003 #pragma once #include <stddef.h> diff --git a/src/deps/uws.zig b/src/deps/uws.zig index 9b4903657..06d46fd3d 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -10,6 +10,7 @@ pub const LIBUS_LISTEN_DEFAULT: i32 = 0; pub const LIBUS_LISTEN_EXCLUSIVE_PORT: i32 = 1; pub const Socket = opaque {}; const bun = @import("../global.zig"); +const uws = @This(); const BoringSSL = @import("boringssl"); fn NativeSocketHandleType(comptime ssl: bool) type { @@ -53,7 +54,7 @@ pub fn NewSocketHandler(comptime ssl: bool) type { return us_socket_context( comptime ssl_int, this.socket, - ); + ).?; } pub fn flush(this: ThisSocket) void { return us_socket_flush( @@ -143,6 +144,52 @@ pub fn NewSocketHandler(comptime ssl: bool) type { return holder; } + pub fn connectPtr( + host: []const u8, + port: i32, + socket_ctx: *SocketContext, + comptime Context: type, + ctx: *Context, + comptime socket_field_name: []const u8, + ) ?*Context { + const this_socket = connectAnon(host, port, socket_ctx, ctx) orelse return null; + @field(ctx, socket_field_name) = this_socket; + return ctx; + } + + pub fn connectUnixPtr( + path: []const u8, + socket_ctx: *SocketContext, + comptime Context: type, + ctx: *Context, + comptime socket_field_name: []const u8, + ) ?*Context { + const this_socket = connectUnixAnon(path, socket_ctx, ctx) orelse return null; + @field(ctx, socket_field_name) = this_socket; + return ctx; + } + + pub fn connectUnixAnon( + path: []const u8, + socket_ctx: *SocketContext, + ctx: *anyopaque, + ) ?ThisSocket { + var stack_fallback = std.heap.stackFallback(1024, bun.default_allocator); + var allocator = stack_fallback.get(); + var path_ = allocator.dupeZ(u8, path) catch return null; + defer allocator.free(path_); + + var socket = us_socket_context_connect_unix(comptime ssl_int, socket_ctx, path_, 0, 8) orelse return null; + const socket_ = ThisSocket{ .socket = socket }; + var holder = socket_.ext(*anyopaque) orelse { + if (comptime bun.Environment.allow_assert) unreachable; + _ = us_socket_close_connecting(comptime ssl_int, socket); + return null; + }; + holder.* = ctx; + return socket_; + } + pub fn connectAnon( host: []const u8, port: i32, @@ -167,6 +214,7 @@ pub fn NewSocketHandler(comptime ssl: bool) type { pub fn configure( ctx: *SocketContext, + comptime deref: bool, comptime ContextType: type, comptime Fields: anytype, ) void { @@ -177,17 +225,37 @@ pub fn NewSocketHandler(comptime ssl: bool) type { @sizeOf(usize) else std.meta.alignment(ContextType); + const deref_ = deref; + const ValueType = if (deref) ContextType else *ContextType; + fn getValue(socket: *Socket) ValueType { + if (comptime ContextType == anyopaque) { + return us_socket_ext(comptime ssl_int, socket).?; + } + + if (comptime deref_) { + return (ThisSocket{ .socket = socket }).ext(ContextType).?.*; + } - pub fn on_open(socket: *Socket, _: i32, _: [*c]u8, _: i32) callconv(.C) ?*Socket { + return (ThisSocket{ .socket = socket }).ext(ContextType).?; + } + + pub fn on_open(socket: *Socket, is_client: i32, _: [*c]u8, _: i32) callconv(.C) ?*Socket { + if (comptime @hasDecl(Fields, "onCreate")) { + if (is_client == 0) { + Fields.onCreate( + ThisSocket{ .socket = socket }, + ); + } + } Fields.onOpen( - @ptrCast(*ContextType, @alignCast(alignment, us_socket_ext(comptime ssl_int, socket).?)), + getValue(socket), ThisSocket{ .socket = socket }, ); return socket; } pub fn on_close(socket: *Socket, code: i32, reason: ?*anyopaque) callconv(.C) ?*Socket { Fields.onClose( - @ptrCast(*ContextType, @alignCast(alignment, us_socket_ext(comptime ssl_int, socket).?)), + getValue(socket), ThisSocket{ .socket = socket }, code, reason, @@ -196,7 +264,7 @@ pub fn NewSocketHandler(comptime ssl: bool) type { } pub fn on_data(socket: *Socket, buf: ?[*]u8, len: i32) callconv(.C) ?*Socket { Fields.onData( - @ptrCast(*ContextType, @alignCast(alignment, us_socket_ext(comptime ssl_int, socket).?)), + getValue(socket), ThisSocket{ .socket = socket }, buf.?[0..@intCast(usize, len)], ); @@ -204,21 +272,21 @@ pub fn NewSocketHandler(comptime ssl: bool) type { } pub fn on_writable(socket: *Socket) callconv(.C) ?*Socket { Fields.onWritable( - @ptrCast(*ContextType, @alignCast(alignment, us_socket_ext(comptime ssl_int, socket).?)), + getValue(socket), ThisSocket{ .socket = socket }, ); return socket; } pub fn on_timeout(socket: *Socket) callconv(.C) ?*Socket { Fields.onTimeout( - @ptrCast(*ContextType, @alignCast(alignment, us_socket_ext(comptime ssl_int, socket).?)), + getValue(socket), ThisSocket{ .socket = socket }, ); return socket; } pub fn on_connect_error(socket: *Socket, code: i32) callconv(.C) ?*Socket { Fields.onConnectError( - @ptrCast(*ContextType, @alignCast(alignment, us_socket_ext(comptime ssl_int, socket).?)), + getValue(socket), ThisSocket{ .socket = socket }, code, ); @@ -226,7 +294,7 @@ pub fn NewSocketHandler(comptime ssl: bool) type { } pub fn on_end(socket: *Socket) callconv(.C) ?*Socket { Fields.onEnd( - @ptrCast(*ContextType, @alignCast(alignment, us_socket_ext(comptime ssl_int, socket).?)), + getValue(socket), ThisSocket{ .socket = socket }, ); return socket; @@ -306,6 +374,24 @@ pub const SocketContext = opaque { pub fn getNativeHandle(this: *SocketContext, comptime ssl: bool) *anyopaque { return us_socket_context_get_native_handle(comptime @as(i32, @boolToInt(ssl)), this).?; } + + pub fn deinit(this: *SocketContext, ssl: bool) void { + us_socket_context_free(@as(i32, @boolToInt(ssl)), this); + } + + pub fn ext(this: *SocketContext, ssl: bool, comptime ContextType: type) ?*ContextType { + const alignment = if (ContextType == *anyopaque) + @sizeOf(usize) + else + std.meta.alignment(ContextType); + + var ptr = us_socket_context_ext( + @boolToInt(ssl), + this, + ) orelse return null; + + return @ptrCast(*ContextType, @alignCast(alignment, ptr)); + } }; pub const Loop = extern struct { internal_loop_data: InternalLoopData align(16), @@ -450,12 +536,12 @@ pub const us_socket_context_options_t = extern struct { }; extern fn SocketContextimestamp(ssl: i32, context: ?*SocketContext) c_ushort; -extern fn us_socket_context_add_server_name(ssl: i32, context: ?*SocketContext, hostname_pattern: [*c]const u8, options: us_socket_context_options_t, ?*anyopaque) void; +pub extern fn us_socket_context_add_server_name(ssl: i32, context: ?*SocketContext, hostname_pattern: [*c]const u8, options: us_socket_context_options_t, ?*anyopaque) void; extern fn us_socket_context_remove_server_name(ssl: i32, context: ?*SocketContext, hostname_pattern: [*c]const u8) void; extern fn us_socket_context_on_server_name(ssl: i32, context: ?*SocketContext, cb: ?fn (?*SocketContext, [*c]const u8) callconv(.C) void) void; extern fn us_socket_context_get_native_handle(ssl: i32, context: ?*SocketContext) ?*anyopaque; pub extern fn us_create_socket_context(ssl: i32, loop: ?*Loop, ext_size: i32, options: us_socket_context_options_t) ?*SocketContext; -extern fn us_socket_context_free(ssl: i32, context: ?*SocketContext) void; +pub extern fn us_socket_context_free(ssl: i32, context: ?*SocketContext) void; extern fn us_socket_context_on_open(ssl: i32, context: ?*SocketContext, on_open: fn (*Socket, i32, [*c]u8, i32) callconv(.C) ?*Socket) void; extern fn us_socket_context_on_close(ssl: i32, context: ?*SocketContext, on_close: fn (*Socket, i32, ?*anyopaque) callconv(.C) ?*Socket) void; extern fn us_socket_context_on_data(ssl: i32, context: ?*SocketContext, on_data: fn (*Socket, [*c]u8, i32) callconv(.C) ?*Socket) void; @@ -465,9 +551,10 @@ extern fn us_socket_context_on_connect_error(ssl: i32, context: ?*SocketContext, extern fn us_socket_context_on_end(ssl: i32, context: ?*SocketContext, on_end: fn (*Socket) callconv(.C) ?*Socket) void; extern fn us_socket_context_ext(ssl: i32, context: ?*SocketContext) ?*anyopaque; -extern fn us_socket_context_listen(ssl: i32, context: ?*SocketContext, host: [*c]const u8, port: i32, options: i32, socket_ext_size: i32) ?*listen_socket_t; - +pub extern fn us_socket_context_listen(ssl: i32, context: ?*SocketContext, host: [*c]const u8, port: i32, options: i32, socket_ext_size: i32) ?*ListenSocket; +pub extern fn us_socket_context_listen_unix(ssl: i32, context: ?*SocketContext, path: [*c]const u8, options: i32, socket_ext_size: i32) ?*ListenSocket; pub extern fn us_socket_context_connect(ssl: i32, context: ?*SocketContext, host: [*c]const u8, port: i32, source_host: [*c]const u8, options: i32, socket_ext_size: i32) ?*Socket; +pub extern fn us_socket_context_connect_unix(ssl: i32, context: ?*SocketContext, path: [*c]const u8, options: i32, socket_ext_size: i32) ?*Socket; pub extern fn us_socket_is_established(ssl: i32, s: ?*Socket) i32; pub extern fn us_socket_close_connecting(ssl: i32, s: ?*Socket) ?*Socket; pub extern fn us_socket_context_loop(ssl: i32, context: ?*SocketContext) ?*Loop; @@ -804,7 +891,7 @@ pub const WebSocketBehavior = extern struct { }; } }; -pub const uws_listen_handler = ?fn (?*listen_socket_t, ?*anyopaque) callconv(.C) void; +pub const uws_listen_handler = ?fn (?*ListenSocket, ?*anyopaque) callconv(.C) void; pub const uws_method_handler = ?fn (*uws_res, *Request, ?*anyopaque) callconv(.C) void; pub const uws_filter_handler = ?fn (*uws_res, i32, ?*anyopaque) callconv(.C) void; pub const uws_missing_server_handler = ?fn ([*c]const u8, ?*anyopaque) callconv(.C) void; @@ -852,8 +939,12 @@ pub const Request = opaque { extern fn uws_req_get_parameter(res: *Request, index: c_ushort, dest: *[*]const u8) usize; }; -const listen_socket_t = opaque {}; -extern fn us_listen_socket_close(ssl: i32, ls: *listen_socket_t) void; +pub const ListenSocket = opaque { + pub fn close(this: *ListenSocket, ssl: bool) void { + us_listen_socket_close(@boolToInt(ssl), this); + } +}; +extern fn us_listen_socket_close(ssl: i32, ls: *ListenSocket) void; pub fn NewApp(comptime ssl: bool) type { return opaque { @@ -907,11 +998,11 @@ pub fn NewApp(comptime ssl: bool) type { } pub const ListenSocket = opaque { - pub inline fn close(this: *ListenSocket) void { + pub inline fn close(this: *ThisApp.ListenSocket) void { if (comptime is_bindgen) { unreachable; } - return us_listen_socket_close(ssl_flag, @ptrCast(*listen_socket_t, this)); + return us_listen_socket_close(ssl_flag, @ptrCast(*uws.ListenSocket, this)); } }; @@ -1046,19 +1137,20 @@ pub fn NewApp(comptime ssl: bool) type { port: i32, comptime UserData: type, user_data: UserData, - comptime handler: fn (UserData, ?*ListenSocket, uws_app_listen_config_t) void, + comptime handler: fn (UserData, ?*ThisApp.ListenSocket, uws_app_listen_config_t) void, ) void { if (comptime is_bindgen) { unreachable; } const Wrapper = struct { - pub fn handle(socket: ?*listen_socket_t, conf: uws_app_listen_config_t, data: ?*anyopaque) callconv(.C) void { + const handler = handler; + pub fn handle(socket: ?*uws.ListenSocket, conf: uws_app_listen_config_t, data: ?*anyopaque) callconv(.C) void { if (comptime UserData == void) { - @call(.{ .modifier = .always_inline }, handler, .{ void{}, @ptrCast(?*ListenSocket, socket), conf }); + @call(.{ .modifier = .always_inline }, handler, .{ void{}, @ptrCast(?*ThisApp.ListenSocket, socket), conf }); } else { @call(.{ .modifier = .always_inline }, handler, .{ @ptrCast(UserData, @alignCast(@alignOf(UserData), data.?)), - @ptrCast(?*ListenSocket, socket), + @ptrCast(?*ThisApp.ListenSocket, socket), conf, }); } @@ -1071,17 +1163,19 @@ pub fn NewApp(comptime ssl: bool) type { app: *ThisApp, comptime UserData: type, user_data: UserData, - comptime handler: fn (UserData, ?*ListenSocket) void, + comptime handler: fn (UserData, ?*ThisApp.ListenSocket) void, config: uws_app_listen_config_t, ) void { const Wrapper = struct { - pub fn handle(socket: ?*listen_socket_t, data: ?*anyopaque) callconv(.C) void { + const handler = handler; + + pub fn handle(socket: ?*uws.ListenSocket, data: ?*anyopaque) callconv(.C) void { if (comptime UserData == void) { - @call(.{ .modifier = .always_inline }, handler, .{ void{}, @ptrCast(?*ListenSocket, socket) }); + @call(.{ .modifier = .always_inline }, handler, .{ void{}, @ptrCast(?*ThisApp.ListenSocket, socket) }); } else { @call(.{ .modifier = .always_inline }, handler, .{ @ptrCast(UserData, @alignCast(@alignOf(UserData), data.?)), - @ptrCast(?*ListenSocket, socket), + @ptrCast(?*ThisApp.ListenSocket, socket), }); } } diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig index ac6d605bd..f0cdc2e54 100644 --- a/src/http/websocket_http_client.zig +++ b/src/http/websocket_http_client.zig @@ -149,6 +149,7 @@ pub fn NewHTTPUpgradeClient(comptime ssl: bool) type { Socket.configure( ctx, + false, HTTPClient, struct { pub const onOpen = handleOpen; @@ -793,6 +794,7 @@ pub fn NewWebSocketClient(comptime ssl: bool) type { Socket.configure( ctx, + false, WebSocket, struct { pub const onClose = handleClose; diff --git a/src/http_client_async.zig b/src/http_client_async.zig index c566f29bf..c49979e7f 100644 --- a/src/http_client_async.zig +++ b/src/http_client_async.zig @@ -100,6 +100,7 @@ fn NewHTTPContext(comptime ssl: bool) type { HTTPSocket.configure( this.us_socket_context, + false, anyopaque, Handler, ); diff --git a/src/jsc.zig b/src/jsc.zig index b9e7b8db5..4d98536d9 100644 --- a/src/jsc.zig +++ b/src/jsc.zig @@ -26,13 +26,16 @@ pub const Jest = @import("./bun.js/test/jest.zig"); pub const API = struct { pub const Transpiler = @import("./bun.js/api/transpiler.zig"); pub const Server = @import("./bun.js/api/server.zig").Server; + pub const ServerConfig = @import("./bun.js/api/server.zig").ServerConfig; pub const ServerWebSocket = @import("./bun.js/api/server.zig").ServerWebSocket; pub const SSLServer = @import("./bun.js/api/server.zig").SSLServer; pub const DebugServer = @import("./bun.js/api/server.zig").DebugServer; pub const DebugSSLServer = @import("./bun.js/api/server.zig").DebugSSLServer; pub const Bun = @import("./bun.js/api/bun.zig"); pub const Router = @import("./bun.js/api/router.zig"); - pub const ServerConfig = @import("./bun.js/api/server.zig").ServerConfig; + pub const TCPSocket = @import("./bun.js/api/bun/socket.zig").TCPSocket; + pub const TLSSocket = @import("./bun.js/api/bun/socket.zig").TLSSocket; + pub const Listener = @import("./bun.js/api/bun/socket.zig").Listener; }; pub const FFI = @import("./bun.js/api/ffi.zig").FFI; pub const Node = struct { diff --git a/test/bun.js/tcp-server.test.ts b/test/bun.js/tcp-server.test.ts new file mode 100644 index 000000000..f0dfc8ce5 --- /dev/null +++ b/test/bun.js/tcp-server.test.ts @@ -0,0 +1,114 @@ +import { listen, connect, finalizationCounter } from "bun"; +import { expect, describe, it } from "bun:test"; +import * as JSC from "bun:jsc"; + +var decoder = new TextDecoder(); + +it("echo server 1 on 1", async () => { + // wrap it in a separate closure so the GC knows to clean it up + // the sockets & listener don't escape the closure + await (async function () { + var resolve, reject, serverResolve, serverReject; + var prom = new Promise((resolve1, reject1) => { + resolve = resolve1; + reject = reject1; + }); + var serverProm = new Promise((resolve1, reject1) => { + serverResolve = resolve1; + serverReject = reject1; + }); + + var serverData, clientData; + const handlers = { + open(socket) { + socket.data.counter = 1; + if (!socket.data?.isServer) { + clientData = socket.data; + clientData.sendQueue = ["client: Hello World! " + 0]; + if (!socket.write("client: Hello World! " + 0)) { + socket.data = { pending: "server: Hello World! " + 0 }; + } + } else { + serverData = socket.data; + serverData.sendQueue = ["server: Hello World! " + 0]; + } + + if (clientData) clientData.other = serverData; + if (serverData) serverData.other = clientData; + if (clientData) clientData.other = serverData; + if (serverData) serverData.other = clientData; + }, + data(socket, buffer) { + const msg = `${ + socket.data.isServer ? "server:" : "client:" + } Hello World! ${socket.data.counter++}`; + socket.data.sendQueue.push(msg); + + expect(decoder.decode(buffer)).toBe(socket.data.other.sendQueue.pop()); + + if (socket.data.counter > 10) { + if (!socket.data.finished) { + socket.data.finished = true; + if (socket.data.isServer) { + setTimeout(() => { + serverResolve(); + socket.end(); + }, 1); + } else { + setTimeout(() => { + resolve(); + socket.end(); + }, 1); + } + } + } + + if (!socket.write(msg)) { + socket.data.pending = msg; + return; + } + }, + error(socket, error) { + reject(error); + }, + drain(socket) { + reject(new Error("Unexpected backpressure")); + }, + }; + + var server = listen({ + socket: handlers, + hostname: "localhost", + port: 8084, + data: { + isServer: true, + counter: 0, + }, + }); + connect({ + socket: handlers, + hostname: "localhost", + port: 8084, + data: { + counter: 0, + }, + }); + await Promise.all([prom, serverProm]); + server.stop(); + server = serverData = clientData = undefined; + Bun.gc(true); + })(); + + // Tell the garbage collector for sure that we're done with the sockets + await new Promise((resolve, reject) => { + setTimeout(() => { + Bun.gc(true); + resolve(); + }, 1); + }); + + // assert we don't leak the sockets + // we expect 1 because that's the prototype / structure + expect(JSC.heapStats().objectTypeCounts.TCPSocket).toBe(1); + expect(JSC.heapStats().objectTypeCounts.Listener).toBe(1); +}); |