diff options
Diffstat (limited to 'src/bun.js')
26 files changed, 1159 insertions, 1238 deletions
diff --git a/src/bun.js/api/bun.classes.ts b/src/bun.js/api/bun.classes.ts index 6d8e80b6d..36f48f790 100644 --- a/src/bun.js/api/bun.classes.ts +++ b/src/bun.js/api/bun.classes.ts @@ -44,6 +44,11 @@ export default [ length: 0, }, + send: { + fn: "doSend", + length: 1, + }, + kill: { fn: "kill", length: 1, diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index f86031caf..36e52821f 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -4426,6 +4426,7 @@ pub const EnvironmentVariables = struct { } return len; } + pub fn getEnvValue(globalObject: *JSC.JSGlobalObject, name: ZigString) ?ZigString { var vm = globalObject.bunVM(); var sliced = name.toSlice(vm.allocator); diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index c3244131d..50bd846ac 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -1,4 +1,3 @@ -const Bun = @This(); const default_allocator = @import("root").bun.default_allocator; const bun = @import("root").bun; const Environment = bun.Environment; @@ -14,6 +13,8 @@ const JSC = @import("root").bun.JSC; const JSValue = JSC.JSValue; const JSGlobalObject = JSC.JSGlobalObject; const Which = @import("../../../which.zig"); +const uws = @import("../../../deps/uws.zig"); +const IPC = @import("../../ipc.zig"); pub const Subprocess = struct { const log = Output.scoped(.Subprocess, false); @@ -61,15 +62,27 @@ pub const Subprocess = struct { is_sync: bool = false, this_jsvalue: JSC.JSValue = .zero, + ipc: IPCMode, + // this is only ever accessed when `ipc` is not `none` + ipc_socket: IPC.Socket = undefined, + ipc_callback: JSC.Strong = .{}, + ipc_buffer: bun.ByteList, + pub const SignalCode = bun.SignalCode; + pub const IPCMode = enum { + none, + bun, + // json, + }; + pub fn hasExited(this: *const Subprocess) bool { return this.exit_code != null or this.waitpid_err != null or this.signal_code != null; } pub fn updateHasPendingActivityFlag(this: *Subprocess) void { @fence(.SeqCst); - this.has_pending_activity.store(this.waitpid_err == null and this.exit_code == null, .SeqCst); + this.has_pending_activity.store(this.waitpid_err == null and this.exit_code == null and this.ipc == .none, .SeqCst); } pub fn hasPendingActivity(this: *Subprocess) callconv(.C) bool { @@ -79,7 +92,7 @@ pub const Subprocess = struct { pub fn updateHasPendingActivity(this: *Subprocess) void { @fence(.Release); - this.has_pending_activity.store(this.waitpid_err == null and this.exit_code == null, .Release); + this.has_pending_activity.store(this.waitpid_err == null and this.exit_code == null and this.ipc == .none, .Release); } pub fn ref(this: *Subprocess) void { @@ -411,6 +424,35 @@ pub const Subprocess = struct { return JSC.JSValue.jsUndefined(); } + pub fn doSend(this: *Subprocess, global: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + if (this.ipc == .none) { + global.throw("Subprocess.send() can only be used if an IPC channel is open.", .{}); + return .zero; + } + + if (callFrame.argumentsCount() == 0) { + global.throwInvalidArguments("Subprocess.send() requires one argument", .{}); + return .zero; + } + + const value = callFrame.argument(0); + + const success = IPC.serializeJSValueForSubprocess( + global, + value, + this.ipc_socket.fd(), + ); + if (!success) return .zero; + + return JSC.JSValue.jsUndefined(); + } + + pub fn disconnect(this: *Subprocess) void { + if (this.ipc == .none) return; + this.ipc_socket.close(0, null); + this.ipc = .none; + } + pub fn getPid( this: *Subprocess, _: *JSGlobalObject, @@ -1070,8 +1112,11 @@ pub const Subprocess = struct { var PATH = jsc_vm.bundler.env.get("PATH") orelse ""; var argv: std.ArrayListUnmanaged(?[*:0]const u8) = undefined; var cmd_value = JSValue.zero; - var detached: bool = false; + var detached = false; var args = args_; + var ipc_mode = IPCMode.none; + var ipc_callback: JSValue = .zero; + { if (args.isEmptyOrUndefinedOrNull()) { globalThis.throwInvalidArguments("cmd must be an array", .{}); @@ -1164,7 +1209,11 @@ pub const Subprocess = struct { globalThis.throwInvalidArguments("onExit must be a function or undefined", .{}); return .zero; } - on_exit_callback = onExit_.withAsyncContextIfNeeded(globalThis); + + on_exit_callback = if (comptime is_sync) + onExit_ + else + onExit_.withAsyncContextIfNeeded(globalThis); } } @@ -1186,8 +1235,13 @@ pub const Subprocess = struct { return .zero; }; + // If the env object does not include a $PATH, it must disable path lookup for argv[0] + PATH = ""; + while (object_iter.next()) |key| { var value = object_iter.value; + if (value == .undefined) continue; + var line = std.fmt.allocPrintZ(allocator, "{}={}", .{ key, value.getZigString(globalThis) }) catch { globalThis.throw("out of memory", .{}); return .zero; @@ -1209,7 +1263,7 @@ pub const Subprocess = struct { if (!stdio_val.isEmptyOrUndefinedOrNull()) { if (stdio_val.jsType().isArray()) { var stdio_iter = stdio_val.arrayIterator(globalThis); - stdio_iter.len = @min(stdio_iter.len, 3); + stdio_iter.len = @min(stdio_iter.len, 4); var i: u32 = 0; while (stdio_iter.next()) |value| : (i += 1) { if (!extractStdio(globalThis, i, value, &stdio)) @@ -1250,6 +1304,15 @@ pub const Subprocess = struct { detached = detached_val.toBoolean(); } } + + if (args.get(globalThis, "ipc")) |val| { + if (val.isCell() and val.isCallable(globalThis.vm())) { + // In the future, we should add a way to use a different IPC serialization format, specifically `json`. + // but the only use case this has is doing interop with node.js IPC and other programs. + ipc_mode = .bun; + ipc_callback = val.withAsyncContextIfNeeded(globalThis); + } + } } } @@ -1328,6 +1391,42 @@ pub const Subprocess = struct { return .zero; }; + // IPC is currently implemented in a very limited way. + // + // Node lets you pass as many fds as you want, they all become be sockets; then, IPC is just a special + // runtime-owned version of "pipe" (in which pipe is a misleading name since they're bidirectional sockets). + // + // Bun currently only supports three fds: stdin, stdout, and stderr, which are all unidirectional + // + // And then fd 3 is assigned specifically and only for IPC. This is quite lame, because Node.js allows + // the ipc fd to be any number and it just works. But most people only care about the default `.fork()` + // behavior, where this workaround suffices. + // + // When Bun.spawn() is given a `.onMessage` callback, it enables IPC as follows: + var socket: IPC.Socket = undefined; + if (ipc_mode != .none) { + if (comptime is_sync) { + globalThis.throwInvalidArguments("IPC is not supported in Bun.spawnSync", .{}); + return .zero; + } + + env_array.ensureUnusedCapacity(allocator, 2) catch |err| return globalThis.handleError(err, "in posix_spawn"); + env_array.appendAssumeCapacity("BUN_INTERNAL_IPC_FD=3"); + + var fds: [2]uws.LIBUS_SOCKET_DESCRIPTOR = undefined; + socket = uws.newSocketFromPair( + jsc_vm.rareData().spawnIPCContext(jsc_vm), + @sizeOf(*Subprocess), + &fds, + ) orelse { + globalThis.throw("failed to create socket pair: E{s}", .{ + @tagName(bun.sys.getErrno(-1)), + }); + return .zero; + }; + actions.dup2(fds[1], 3) catch |err| return globalThis.handleError(err, "in posix_spawn"); + } + env_array.append(allocator, null) catch { globalThis.throw("out of memory", .{}); return .zero; @@ -1389,7 +1488,6 @@ pub const Subprocess = struct { globalThis.throw("out of memory", .{}); return .zero; }; - // When run synchronously, subprocess isn't garbage collected subprocess.* = Subprocess{ .globalThis = globalThis, @@ -1404,7 +1502,16 @@ pub const Subprocess = struct { .stderr = Readable.init(stdio[bun.STDERR_FD], stderr_pipe[0], jsc_vm.allocator, default_max_buffer_size), .on_exit_callback = if (on_exit_callback != .zero) JSC.Strong.create(on_exit_callback, globalThis) else .{}, .is_sync = is_sync, + .ipc = ipc_mode, + // will be assigned in the block below + .ipc_socket = socket, + .ipc_buffer = bun.ByteList{}, + .ipc_callback = if (ipc_callback != .zero) JSC.Strong.create(ipc_callback, globalThis) else undefined, }; + if (ipc_mode != .none) { + var ptr = socket.ext(*Subprocess); + ptr.?.* = subprocess; + } if (subprocess.stdin == .pipe) { subprocess.stdin.pipe.signal = JSC.WebCore.Signal.init(&subprocess.stdin); @@ -1610,6 +1717,7 @@ pub const Subprocess = struct { globalThis: *JSC.JSGlobalObject, this_jsvalue: JSC.JSValue, ) void { + log("onExit {d}, code={d}", .{ this.pid, if (this.exit_code) |e| @as(i32, @intCast(e)) else -1 }); defer this.updateHasPendingActivity(); this_jsvalue.ensureStillAlive(); this.has_waitpid_task = false; @@ -1715,7 +1823,6 @@ pub const Subprocess = struct { try actions.dup2(std_fileno, std_fileno); } }, - .ignore => { const flag = if (std_fileno == bun.STDIN_FD) @as(u32, os.O.RDONLY) else @as(u32, std.os.O.WRONLY); try actions.openZ(std_fileno, "/dev/null", flag, 0o664); @@ -1867,10 +1974,46 @@ pub const Subprocess = struct { .held = JSC.Strong.create(array_buffer.value, globalThis), }, }; + return true; } globalThis.throwInvalidArguments("stdio must be an array of 'inherit', 'ignore', or null", .{}); return false; } + + pub fn handleIPCMessage( + this: *Subprocess, + message: IPC.DecodedIPCMessage, + ) void { + switch (message) { + // In future versions we can read this in order to detect version mismatches, + // or disable future optimizations if the subprocess is old. + .version => |v| { + IPC.log("Child IPC version is {d}", .{v}); + }, + .data => |data| { + IPC.log("Received IPC message from child", .{}); + if (this.ipc_callback.get()) |cb| { + const result = cb.callWithThis( + this.globalThis, + this.this_jsvalue, + &[_]JSValue{data}, + ); + data.ensureStillAlive(); + if (result.isAnyError()) { + this.globalThis.bunVM().onUnhandledError(this.globalThis, result); + } + } + }, + } + } + + pub fn handleIPCClose(this: *Subprocess, _: IPC.Socket) void { + // uSocket is already freed so calling .close() on the socket can segfault + this.ipc = .none; + this.updateHasPendingActivity(); + } + + pub const IPCHandler = IPC.NewIPCHandler(Subprocess); }; diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index a1f5781d7..b94386ab3 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -230,7 +230,13 @@ void RequireFunctionPrototype::finishCreation(JSC::VM& vm) JSC::Identifier::fromString(vm, "main"_s), JSC::GetterSetter::create(vm, globalObject(), requireDotMainFunction, JSValue()), PropertyAttribute::Builtin | PropertyAttribute::Accessor | PropertyAttribute::ReadOnly | 0); - this->putDirect(vm, JSC::Identifier::fromString(vm, "extensions"_s), constructEmptyObject(globalObject()), 0); + + auto extensions = constructEmptyObject(globalObject()); + extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".js"_s), jsBoolean(true), 0); + extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".json"_s), jsBoolean(true), 0); + extensions->putDirect(vm, JSC::Identifier::fromString(vm, ".node"_s), jsBoolean(true), 0); + + this->putDirect(vm, JSC::Identifier::fromString(vm, "extensions"_s), extensions, 0); } JSC_DEFINE_CUSTOM_GETTER(getterFilename, (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index 328b9f940..c53235824 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -239,6 +239,20 @@ extern "C" EncodedJSValue functionImportMeta__resolveSyncPrivate(JSC::JSGlobalOb RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(JSC::JSValue {})); + if (!isESM) { + auto* global = jsDynamicCast<Zig::GlobalObject*>(globalObject); + if (LIKELY(global)) { + auto overrideHandler = global->m_nodeModuleOverriddenResolveFilename.get(); + if (UNLIKELY(overrideHandler)) { + ASSERT(overrideHandler.isCallable(globalObject)); + MarkedArgumentBuffer args; + args.append(moduleName); + args.append(from); + return JSValue::encode(JSC::call(globalObject, overrideHandler, JSC::getCallData(overrideHandler), JSC::jsUndefined(), args)); + } + } + } + auto result = Bun__resolveSync(globalObject, JSC::JSValue::encode(moduleName), JSValue::encode(from), isESM); if (!JSC::JSValue::decode(result).isString()) { diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp index 934fc9b6c..ad901b0e4 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -1429,12 +1429,12 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS { auto& vm = JSC::getVM(lexicalGlobalObject); auto scope = DECLARE_THROW_SCOPE(vm); - uint32_t offset = 0; - uint32_t length = castedThis->length(); - uint32_t byteLength = length; + uint32_t start = 0; + uint32_t end = castedThis->length(); + uint32_t byteLength = end; WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; - if (length == 0) + if (end == 0) return JSC::JSValue::encode(JSC::jsEmptyString(vm)); size_t argsCount = callFrame->argumentCount(); @@ -1443,66 +1443,33 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS JSC::JSValue arg2 = callFrame->argument(1); JSC::JSValue arg3 = callFrame->argument(2); - // This method could be called in following forms: - // - toString() - // - toString(encoding) - // - toString(encoding, start) - // - toString(encoding, start, end) - // - toString(offset, length) - // - toString(offset, length, encoding) if (argsCount == 0) - return jsBufferToString(vm, lexicalGlobalObject, castedThis, offset, length, encoding); + return jsBufferToString(vm, lexicalGlobalObject, castedThis, start, end, encoding); - if (arg1.isString()) { + if (!arg1.isUndefined()) { encoding = parseEncoding(lexicalGlobalObject, scope, arg1); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); + } - if (!arg3.isUndefined()) { - // length is end - length = std::min(byteLength, static_cast<uint32_t>(arg3.toInt32(lexicalGlobalObject))); - RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); - } - - int32_t istart = 0; - - if (!arg2.isUndefined()) { - istart = arg2.toInt32(lexicalGlobalObject); - RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); - } + if (!arg2.isUndefined()) { + int32_t istart = arg2.toInt32(lexicalGlobalObject); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); if (istart < 0) { throwTypeError(lexicalGlobalObject, scope, "Start must be a positive integer"_s); return JSC::JSValue::encode(jsUndefined()); } - offset = static_cast<uint32_t>(istart); - length = (length > offset) ? (length - offset) : 0; - } else { - - int32_t ioffset = 0; - - if (!arg1.isUndefined()) { - ioffset = arg1.toInt32(lexicalGlobalObject); - RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); - } - - if (ioffset < 0) { - throwTypeError(lexicalGlobalObject, scope, "Offset must be a positive integer"_s); - return JSC::JSValue::encode(jsUndefined()); - } - offset = static_cast<uint32_t>(ioffset); - length = (length > offset) ? (length - offset) : 0; - - if (!arg3.isUndefined()) { - encoding = parseEncoding(lexicalGlobalObject, scope, arg3); - RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); - } + start = static_cast<uint32_t>(istart); + } - if (!arg2.isUndefined()) - length = std::min(length, static_cast<uint32_t>(arg2.toInt32(lexicalGlobalObject))); + if (!arg3.isUndefined()) { + // length is end + end = std::min(byteLength, static_cast<uint32_t>(arg3.toInt32(lexicalGlobalObject))); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); } - return jsBufferToString(vm, lexicalGlobalObject, castedThis, offset, length, encoding); + return jsBufferToString(vm, lexicalGlobalObject, castedThis, start, end > start ? end - start : 0, encoding); } // DOMJIT makes it slower! TODO: investigate why diff --git a/src/bun.js/bindings/JSReadableHelper.cpp b/src/bun.js/bindings/JSReadableHelper.cpp deleted file mode 100644 index 0c459f329..000000000 --- a/src/bun.js/bindings/JSReadableHelper.cpp +++ /dev/null @@ -1,263 +0,0 @@ -#include "JSReadableHelper.h" -#include "JSReadableState.h" -#include "JSBufferList.h" -#include "JSBuffer.h" -#include "JSEventEmitter.h" -#include "JSStringDecoder.h" -#include "JavaScriptCore/Lookup.h" -#include "JavaScriptCore/ObjectConstructor.h" -#include "ZigGlobalObject.h" -#include "JSDOMOperation.h" -#include "JSDOMAttribute.h" -#include "headers.h" -#include "JSDOMConvertEnumeration.h" -#include "JavaScriptCore/StrongInlines.h" -#include "BunClientData.h" - -namespace WebCore { -using namespace JSC; - -#define JSReadableHelper_EXTRACT_STREAM_STATE \ - VM& vm = lexicalGlobalObject->vm(); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - \ - if (callFrame->argumentCount() < 2) { \ - throwTypeError(lexicalGlobalObject, throwScope, "Not enough arguments"_s); \ - return JSValue::encode(jsUndefined()); \ - } \ - \ - JSObject* stream = callFrame->uncheckedArgument(0).toObject(lexicalGlobalObject); \ - RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); \ - JSReadableState* state = jsCast<JSReadableState*>(callFrame->uncheckedArgument(1)); \ - if (!state) { \ - throwTypeError(lexicalGlobalObject, throwScope, "Second argument not ReadableState"_s); \ - return JSValue::encode(jsUndefined()); \ - } - -static bool callRead(JSValue stream, JSFunction* read, JSC::MarkedArgumentBuffer&& args, JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, EventEmitter& emitter) -{ - WTF::NakedPtr<JSC::Exception> exceptionPtr; - JSC::CallData callData = JSC::getCallData(read); - JSValue ret = call(lexicalGlobalObject, read, callData, JSValue(stream), WTFMove(args), exceptionPtr); - if (auto* exception = exceptionPtr.get()) { - JSC::Identifier errorEventName = JSC::Identifier::fromString(vm, "error"_s); - if (emitter.hasEventListeners(errorEventName)) { - args.clear(); - JSValue val = exception->value(); - if (!val) { - val = jsUndefined(); - } - args.append(val); - emitter.emitForBindings(errorEventName, args); - } else { - reportException(lexicalGlobalObject, exception); - } - return true; - } - - return !ret.isUndefinedOrNull(); -} - -JSC_DEFINE_HOST_FUNCTION(jsReadable_maybeReadMore, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - JSReadableHelper_EXTRACT_STREAM_STATE - - auto clientData - = WebCore::clientData(vm); - auto readIdentifier = clientData->builtinNames().readPublicName(); - auto read = stream->get(lexicalGlobalObject, readIdentifier); - - auto callData = JSC::getCallData(read); - if (callData.type == CallData::Type::None) { - throwException(lexicalGlobalObject, throwScope, createNotAFunctionError(lexicalGlobalObject, read)); - return JSValue::encode({}); - } - - auto* jsEmitter = jsEventEmitterCastFast(vm, lexicalGlobalObject, stream); - RETURN_IF_EXCEPTION(throwScope, {}); - if (UNLIKELY(!jsEmitter)) { - throwTypeError(lexicalGlobalObject, throwScope, "Stream must be an EventEmitter"_s); - return JSValue::encode(JSValue {}); - } - auto& emitter = jsEmitter->wrapped(); - - while ( - !state->getBool(JSReadableState::reading) && !state->getBool(JSReadableState::ended) && (state->m_length < state->m_highWaterMark || (state->m_flowing > 0 && state->m_length == 0))) { - int64_t len = state->m_length; - MarkedArgumentBuffer args; - args.append(jsNumber(0)); - - callRead(stream, jsCast<JSFunction*>(read), WTFMove(args), vm, lexicalGlobalObject, emitter); - - if (len == state->m_length) - break; - } - RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); -} - -void flow(JSGlobalObject* lexicalGlobalObject, JSObject* streamObj, JSReadableState* state) -{ - VM& vm = lexicalGlobalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); - - auto clientData = WebCore::clientData(vm); - auto readIdentifier = clientData->builtinNames().readPublicName(); - auto read = streamObj->get(lexicalGlobalObject, readIdentifier); - - auto callData = JSC::getCallData(read); - if (callData.type == CallData::Type::None) { - throwException(lexicalGlobalObject, throwScope, createNotAFunctionError(lexicalGlobalObject, read)); - return; - } - - if (state->m_flowing > 0) { - WebCore::EventEmitter& emitter = jsEventEmitterCastFast(vm, lexicalGlobalObject, streamObj)->wrapped(); - - while (state->m_flowing > 0) { - - if (!callRead(streamObj, jsCast<JSFunction*>(read), MarkedArgumentBuffer(), vm, lexicalGlobalObject, emitter)) { - break; - } - } - } -} - -JSC_DEFINE_HOST_FUNCTION(jsReadable_resume, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - JSReadableHelper_EXTRACT_STREAM_STATE - - auto* jsEmitterWrap - = jsEventEmitterCastFast(vm, lexicalGlobalObject, stream); - - if (UNLIKELY(!jsEmitterWrap)) { - throwTypeError(lexicalGlobalObject, throwScope, "Stream must be an EventEmitter"_s); - return JSValue::encode(JSValue {}); - } - - auto& emitter = jsEmitterWrap->wrapped(); - auto clientData = WebCore::clientData(vm); - auto readIdentifier = clientData->builtinNames().readPublicName(); - - if (!state->getBool(JSReadableState::reading)) { - // stream.read(0) - MarkedArgumentBuffer args; - args.append(jsNumber(0)); - - callRead(stream, jsCast<JSFunction*>(stream->get(lexicalGlobalObject, readIdentifier)), WTFMove(args), vm, lexicalGlobalObject, emitter); - } - - state->setBool(JSReadableState::resumeScheduled, true); - // stream.emit('resume') - auto eventType = clientData->builtinNames().resumePublicName(); - MarkedArgumentBuffer args; - - emitter.emitForBindings(eventType, args); - - flow(lexicalGlobalObject, stream, state); - - if (state->m_flowing > 0 && !state->getBool(JSReadableState::reading)) { - // stream.read(0) - auto read = stream->get(lexicalGlobalObject, readIdentifier); - auto callData = JSC::getCallData(read); - if (callData.type == CallData::Type::None) { - throwException(lexicalGlobalObject, throwScope, createNotAFunctionError(lexicalGlobalObject, read)); - return JSValue::encode(jsUndefined()); - } - MarkedArgumentBuffer args; - args.append(jsNumber(0)); - callRead(stream, jsCast<JSFunction*>(read), WTFMove(args), vm, lexicalGlobalObject, emitter); - } - RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); -} - -EncodedJSValue emitReadable_(JSGlobalObject* lexicalGlobalObject, JSObject* stream, JSReadableState* state) -{ - VM& vm = lexicalGlobalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); - JSValue errored = state->m_errored.get(); - if (!state->getBool(JSReadableState::destroyed) && !errored.toBoolean(lexicalGlobalObject) && (state->m_length || state->getBool(JSReadableState::ended))) { - // stream.emit('readable') - auto clientData = WebCore::clientData(vm); - - auto eventType = clientData->builtinNames().readablePublicName(); - MarkedArgumentBuffer args; - auto* emitter - = jsEventEmitterCastFast(vm, lexicalGlobalObject, stream); - if (UNLIKELY(!emitter)) { - throwTypeError(lexicalGlobalObject, throwScope, "Stream must be an EventEmitter"_s); - return JSValue::encode(JSValue {}); - } - emitter->wrapped().emitForBindings(eventType, args); - - state->setBool(JSReadableState::emittedReadable, false); - } - - state->setBool(JSReadableState::needReadable, state->m_flowing <= 0 && !state->getBool(JSReadableState::ended) && state->m_length <= state->m_highWaterMark); - flow(lexicalGlobalObject, stream, state); - return JSValue::encode(jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsReadable_emitReadable_, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - JSReadableHelper_EXTRACT_STREAM_STATE - - emitReadable_(lexicalGlobalObject, stream, state); - - RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); -} - -EncodedJSValue emitReadable(JSGlobalObject* lexicalGlobalObject, JSObject* stream, JSReadableState* state) -{ - VM& vm = lexicalGlobalObject->vm(); - - state->setBool(JSReadableState::needReadable, false); - if (!state->getBool(JSReadableState::emittedReadable)) { - state->setBool(JSReadableState::emittedReadable, true); - Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); - globalObject->queueMicrotask(JSValue(globalObject->emitReadableNextTickFunction()), JSValue(stream), JSValue(state), JSValue {}, JSValue {}); - } - return JSValue::encode(jsUndefined()); -} - -JSC_DEFINE_HOST_FUNCTION(jsReadable_emitReadable, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - JSReadableHelper_EXTRACT_STREAM_STATE - - RELEASE_AND_RETURN(throwScope, emitReadable(lexicalGlobalObject, stream, state)); -} - -JSC_DEFINE_HOST_FUNCTION(jsReadable_onEofChunk, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - JSReadableHelper_EXTRACT_STREAM_STATE - - if (state->getBool(JSReadableState::ended)) - RELEASE_AND_RETURN(throwScope, JSValue::encode(jsUndefined())); - - auto decoder = jsDynamicCast<JSStringDecoder*>(state->m_decoder.get()); - if (decoder) { - JSString* chunk = jsDynamicCast<JSString*>(decoder->end(vm, lexicalGlobalObject, nullptr, 0)); - if (chunk && chunk->length()) { - auto buffer = jsDynamicCast<JSBufferList*>(state->m_buffer.get()); - if (!buffer) { - throwTypeError(lexicalGlobalObject, throwScope, "Not buffer on stream"_s); - return JSValue::encode(jsUndefined()); - } - buffer->push(vm, JSValue(chunk)); - state->m_length += state->getBool(JSReadableState::objectMode) ? 1 : chunk->length(); - } - } - - state->setBool(JSReadableState::ended, true); - - if (state->getBool(JSReadableState::sync)) { - RELEASE_AND_RETURN(throwScope, emitReadable(lexicalGlobalObject, stream, state)); - } else { - state->setBool(JSReadableState::needReadable, false); - state->setBool(JSReadableState::emittedReadable, true); - RELEASE_AND_RETURN(throwScope, emitReadable_(lexicalGlobalObject, stream, state)); - } -} - -#undef JSReadableHelper_EXTRACT_STREAM_STATE - -} // namespace WebCore diff --git a/src/bun.js/bindings/JSReadableHelper.h b/src/bun.js/bindings/JSReadableHelper.h deleted file mode 100644 index 3e2554c2b..000000000 --- a/src/bun.js/bindings/JSReadableHelper.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "root.h" - -namespace WebCore { - -JSC_DECLARE_HOST_FUNCTION(jsReadable_maybeReadMore); -JSC_DECLARE_HOST_FUNCTION(jsReadable_resume); -JSC_DECLARE_HOST_FUNCTION(jsReadable_emitReadable); -JSC_DECLARE_HOST_FUNCTION(jsReadable_onEofChunk); -JSC_DECLARE_HOST_FUNCTION(jsReadable_emitReadable_); - -} // namespace WebCore diff --git a/src/bun.js/bindings/JSReadableState.cpp b/src/bun.js/bindings/JSReadableState.cpp deleted file mode 100644 index 1f3a36def..000000000 --- a/src/bun.js/bindings/JSReadableState.cpp +++ /dev/null @@ -1,426 +0,0 @@ -#include "JSReadableState.h" -#include "JSBufferList.h" -#include "JSBuffer.h" -#include "JavaScriptCore/Lookup.h" -#include "JavaScriptCore/ObjectConstructor.h" -#include "ZigGlobalObject.h" -#include "JSDOMOperation.h" -#include "JSDOMAttribute.h" -#include "headers.h" -#include "JSDOMConvertEnumeration.h" -#include "BunClientData.h" - -namespace WebCore { - -using namespace JSC; - -static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_pipesCount); - -int64_t getHighWaterMark(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options) -{ - auto throwScope = DECLARE_THROW_SCOPE(vm); - - // We must use getIfPropertyExists because: - // - it might be a getter - // - it might be from a super class - auto* clientData = WebCore::clientData(vm); - if (JSValue highWaterMarkVal = options->getIfPropertyExists(globalObject, clientData->builtinNames().highWaterMarkPublicName())) { - if (isDuplex && (highWaterMarkVal.isUndefined() || highWaterMarkVal.isNull())) { - highWaterMarkVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "readableObjectMode"_s)); - } - - if (highWaterMarkVal && highWaterMarkVal.isNumber()) { - return highWaterMarkVal.toInt32(globalObject); - } - } - - return -1; -} - -void JSReadableState::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options) -{ - Base::finishCreation(vm); - - if (options != nullptr) { - JSC::JSValue objectModeVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "objectMode"_s)); - if (isDuplex && !objectModeVal) { - objectModeVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "readableObjectMode"_s)); - } - if (objectModeVal && objectModeVal.toBoolean(globalObject)) - setBool(JSReadableState::Mask::objectMode, true); - } - - m_highWaterMark = getBool( - JSReadableState::Mask::objectMode) - ? 16 - : 16 * 1024; // default value - - if (options != nullptr) { - int64_t customHightWaterMark = getHighWaterMark(vm, globalObject, isDuplex, options); - if (customHightWaterMark >= 0) - m_highWaterMark = customHightWaterMark; - } - - m_buffer.set(vm, this, JSBufferList::create(vm, globalObject, reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSBufferListStructure())); - m_pipes.set(vm, this, JSC::constructEmptyArray(globalObject, nullptr, 0)); - - if (options != nullptr) { - JSC::JSValue emitCloseVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "emitClose"_s)); - if (!emitCloseVal || emitCloseVal.toBoolean(globalObject)) - setBool(JSReadableState::Mask::emitClose, true); - // Has it been destroyed. - JSC::JSValue autoDestroyVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "autoDestroy"_s)); - if (!autoDestroyVal || autoDestroyVal.toBoolean(globalObject)) - setBool(JSReadableState::Mask::autoDestroy, true); - } else { - setBool(JSReadableState::Mask::emitClose, true); - setBool(JSReadableState::Mask::autoDestroy, true); - } - - // Indicates whether the stream has finished destroying. - m_errored.set(vm, this, JSC::jsNull()); - - // Ref the piped dest which we need a drain event on it - // type: null | Writable | Set<Writable>. - if (options == nullptr) { - m_defaultEncoding.set(vm, this, JSC::jsString(vm, WTF::String("utf8"_s))); - } else { - if (JSC::JSValue defaultEncodingVal = getIfPropertyExists(globalObject, PropertyName(JSC::Identifier::fromString(vm, "defaultEncoding"_s)))) { - m_defaultEncoding.set(vm, this, defaultEncodingVal); - } else { - m_defaultEncoding.set(vm, this, JSC::jsString(vm, WTF::String("utf8"_s))); - } - } - - m_awaitDrainWriters.set(vm, this, JSC::jsNull()); - JSValue decodeValue = JSC::jsNull(); - JSValue encodingValue = JSC::jsNull(); - - if (options != nullptr) { - JSC::JSValue encodingVal = options->getIfPropertyExists(globalObject, JSC::Identifier::fromString(vm, "encoding"_s)); - if (encodingVal && encodingVal.isString()) { - auto constructor = reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSStringDecoder(); - auto constructData = JSC::getConstructData(constructor); - MarkedArgumentBuffer args; - args.append(encodingVal); - JSObject* decoder = JSC::construct(globalObject, constructor, constructData, args); - decodeValue = decoder; - encodingValue = encodingVal; - } - } - - m_decoder.set(vm, this, decodeValue); - m_encoding.set(vm, this, encodingValue); - - // ReadableState.constructed is set to false during construction when a _construct method is implemented - // this is here so that the ReadableState behavior tracks the behavior in node, and that calling Readable.read - // will work when we return early from construct because there is no Readable._construct implemented - // See: https://github.com/nodejs/node/blob/main/lib/internal/streams/readable.js - setBool(JSReadableState::Mask::constructed, true); -} - -const JSC::ClassInfo JSReadableState::s_info = { "ReadableState"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableState) }; - -JSC::GCClient::IsoSubspace* JSReadableState::subspaceForImpl(JSC::VM& vm) -{ - return WebCore::subspaceForImpl<JSReadableState, UseCustomHeapCellType::No>( - vm, - [](auto& spaces) { return spaces.m_clientSubspaceForReadableState.get(); }, - [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForReadableState = std::forward<decltype(space)>(space); }, - [](auto& spaces) { return spaces.m_subspaceForReadableState.get(); }, - [](auto& spaces, auto&& space) { spaces.m_subspaceForReadableState = std::forward<decltype(space)>(space); }); -} - -template<typename Visitor> -void JSReadableState::visitChildrenImpl(JSCell* cell, Visitor& visitor) -{ - JSReadableState* state = jsCast<JSReadableState*>(cell); - ASSERT_GC_OBJECT_INHERITS(state, info()); - Base::visitChildren(state, visitor); - visitor.append(state->m_buffer); - visitor.append(state->m_pipes); - visitor.append(state->m_errored); - visitor.append(state->m_defaultEncoding); - visitor.append(state->m_awaitDrainWriters); - visitor.append(state->m_decoder); - visitor.append(state->m_encoding); -} -DEFINE_VISIT_CHILDREN(JSReadableState); - -STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSReadableStatePrototype, JSReadableStatePrototype::Base); - -JSC_DEFINE_CUSTOM_GETTER(jsReadableState_pipesCount, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) -{ - auto& vm = JSC::getVM(lexicalGlobalObject); - auto throwScope = DECLARE_THROW_SCOPE(vm); - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); - if (!state) { - RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); - } - JSArray* pipes = JSC::jsDynamicCast<JSArray*>(state->m_pipes.get()); - if (!pipes) { - RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); - } - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(pipes->length()))); -} - -#define JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(NAME) \ - static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); \ - } \ - if (state->m_##NAME == 0) \ - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNull())); \ - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsBoolean(state->m_##NAME > 0))); \ - } \ - static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, false); \ - } \ - auto value = JSC::JSValue::decode(encodedValue); \ - state->m_##NAME = value.isNull() ? 0 : value.toBoolean(lexicalGlobalObject) ? 1 \ - : -1; \ - RELEASE_AND_RETURN(throwScope, true); \ - } - -JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(paused) - JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER(flowing) - -#undef JSReadableState_NULLABLE_BOOLEAN_GETTER_SETTER - -#define JSReadableState_NUMBER_GETTER_SETTER(NAME) \ - static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); \ - } \ - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(state->m_##NAME))); \ - } \ - \ - static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, false); \ - } \ - state->m_##NAME = JSC::JSValue::decode(encodedValue).toNumber(lexicalGlobalObject); \ - RETURN_IF_EXCEPTION(throwScope, false); \ - RELEASE_AND_RETURN(throwScope, true); \ - } - - JSReadableState_NUMBER_GETTER_SETTER(length) - JSReadableState_NUMBER_GETTER_SETTER(highWaterMark) - -#undef JSReadableState_NUMBER_GETTER_SETTER - -#define JSReadableState_BOOLEAN_GETTER_SETTER(NAME) \ - static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); \ - } \ - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsBoolean(state->getBool(JSReadableState::Mask::NAME)))); \ - } \ - \ - static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, false); \ - } \ - state->setBool(JSReadableState::Mask::NAME, JSC::JSValue::decode(encodedValue).toBoolean(lexicalGlobalObject)); \ - RELEASE_AND_RETURN(throwScope, true); \ - } - - JSReadableState_BOOLEAN_GETTER_SETTER(objectMode) - JSReadableState_BOOLEAN_GETTER_SETTER(ended) - JSReadableState_BOOLEAN_GETTER_SETTER(endEmitted) - JSReadableState_BOOLEAN_GETTER_SETTER(reading) - JSReadableState_BOOLEAN_GETTER_SETTER(constructed) - JSReadableState_BOOLEAN_GETTER_SETTER(sync) - JSReadableState_BOOLEAN_GETTER_SETTER(needReadable) - JSReadableState_BOOLEAN_GETTER_SETTER(emittedReadable) - JSReadableState_BOOLEAN_GETTER_SETTER(readableListening) - JSReadableState_BOOLEAN_GETTER_SETTER(resumeScheduled) - JSReadableState_BOOLEAN_GETTER_SETTER(errorEmitted) - JSReadableState_BOOLEAN_GETTER_SETTER(emitClose) - JSReadableState_BOOLEAN_GETTER_SETTER(autoDestroy) - JSReadableState_BOOLEAN_GETTER_SETTER(destroyed) - JSReadableState_BOOLEAN_GETTER_SETTER(closed) - JSReadableState_BOOLEAN_GETTER_SETTER(closeEmitted) - JSReadableState_BOOLEAN_GETTER_SETTER(multiAwaitDrain) - JSReadableState_BOOLEAN_GETTER_SETTER(readingMore) - JSReadableState_BOOLEAN_GETTER_SETTER(dataEmitted) - -#undef JSReadableState_BOOLEAN_GETTER_SETTER - -#define JSReadableState_JSVALUE_GETTER_SETTER(NAME) \ - static JSC_DECLARE_CUSTOM_GETTER(jsReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_GETTER(jsReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, JSC::JSValue::encode(JSC::jsUndefined())); \ - } \ - RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(state->m_##NAME.get())); \ - } \ - static JSC_DECLARE_CUSTOM_SETTER(setJSReadableState_##NAME); \ - JSC_DEFINE_CUSTOM_SETTER(setJSReadableState_##NAME, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) \ - { \ - auto& vm = JSC::getVM(lexicalGlobalObject); \ - auto throwScope = DECLARE_THROW_SCOPE(vm); \ - JSReadableState* state = JSC::jsDynamicCast<JSReadableState*>(JSValue::decode(thisValue)); \ - if (!state) { \ - RETURN_IF_EXCEPTION(throwScope, false); \ - } \ - auto value = JSC::JSValue::decode(encodedValue); \ - state->m_##NAME.set(vm, state, value); \ - RELEASE_AND_RETURN(throwScope, true); \ - } - - JSReadableState_JSVALUE_GETTER_SETTER(buffer) - JSReadableState_JSVALUE_GETTER_SETTER(pipes) - JSReadableState_JSVALUE_GETTER_SETTER(errored) - JSReadableState_JSVALUE_GETTER_SETTER(defaultEncoding) - JSReadableState_JSVALUE_GETTER_SETTER(awaitDrainWriters) - JSReadableState_JSVALUE_GETTER_SETTER(decoder) - JSReadableState_JSVALUE_GETTER_SETTER(encoding) - -#undef JSReadableState_JSVALUE_GETTER_SETTER - -#define JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(NAME) \ - { \ -#NAME ""_s, static_cast < unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, \ - { \ - HashTableValue::GetterSetterType, jsReadableState_##NAME, setJSReadableState_##NAME \ - } \ - } - - /* Hash table for prototype */ - static const HashTableValue JSReadableStatePrototypeTableValues[] - = { - { "pipesCount"_s, static_cast<unsigned>(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsReadableState_pipesCount, 0 } }, - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(paused), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(flowing), - - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(objectMode), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(ended), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(endEmitted), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(reading), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(constructed), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(sync), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(needReadable), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(emittedReadable), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(readableListening), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(resumeScheduled), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(errorEmitted), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(emitClose), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(autoDestroy), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(destroyed), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(closed), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(closeEmitted), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(multiAwaitDrain), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(readingMore), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(dataEmitted), - - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(length), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(highWaterMark), - - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(buffer), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(pipes), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(errored), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(defaultEncoding), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(awaitDrainWriters), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(decoder), - JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE(encoding), - }; - -#undef JSReadableState_GETTER_SETTER_HASH_TABLE_VALUE - -void JSReadableStatePrototype::finishCreation(VM& vm, JSC::JSGlobalObject* globalThis) -{ - Base::finishCreation(vm); - reifyStaticProperties(vm, JSReadableState::info(), JSReadableStatePrototypeTableValues, *this); - JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); -} - -const ClassInfo JSReadableStatePrototype::s_info = { "ReadableState"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableStatePrototype) }; - -void JSReadableStateConstructor::finishCreation(VM& vm, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype) -{ - Base::finishCreation(vm, 0, "ReadableState"_s, PropertyAdditionMode::WithoutStructureTransition); - putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontEnum | PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly); - ASSERT(inherits(info())); -} - -JSReadableStateConstructor* JSReadableStateConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSReadableStatePrototype* prototype) -{ - JSReadableStateConstructor* ptr = new (NotNull, JSC::allocateCell<JSReadableStateConstructor>(vm)) JSReadableStateConstructor(vm, structure, construct); - ptr->finishCreation(vm, globalObject, prototype); - return ptr; -} - -JSC::EncodedJSValue JSReadableStateConstructor::construct(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) -{ - JSC::VM& vm = lexicalGlobalObject->vm(); - auto throwScope = DECLARE_THROW_SCOPE(vm); - if (callFrame->argumentCount() < 3) { - throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - return JSValue::encode(jsUndefined()); - } - JSValue optionsVal = callFrame->uncheckedArgument(0); - JSValue streamVal = callFrame->uncheckedArgument(1); - JSValue isDuplexVal = callFrame->uncheckedArgument(2); - - bool isDuplex; - if (!isDuplexVal.isBoolean()) { - // change this to `stream instanceof Duplex` after native Duplex is implemented. - JSC::throwTypeError(lexicalGlobalObject, throwScope, "isDuplex should be boolean"_s); - return JSValue::encode(jsUndefined()); - } - isDuplex = isDuplexVal.toBoolean(lexicalGlobalObject); - RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - JSObject* options = nullptr; - if (optionsVal && optionsVal.isObject()) { - options = optionsVal.toObject(lexicalGlobalObject); - } - RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - - JSReadableState* stringDecoder = JSReadableState::create( - vm, lexicalGlobalObject, reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject)->JSReadableStateStructure(), isDuplex, options); - return JSC::JSValue::encode(stringDecoder); -} - -void JSReadableStateConstructor::initializeProperties(VM& vm, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype) -{ -} - -const ClassInfo JSReadableStateConstructor::s_info = { "ReadableState"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSReadableStateConstructor) }; - -} // namespace Zig diff --git a/src/bun.js/bindings/JSReadableState.h b/src/bun.js/bindings/JSReadableState.h deleted file mode 100644 index c67baebad..000000000 --- a/src/bun.js/bindings/JSReadableState.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include "root.h" -#include "BufferEncodingType.h" - -namespace WebCore { -using namespace JSC; - -class JSReadableState : public JSC::JSDestructibleObject { - using Base = JSC::JSDestructibleObject; - -public: - JSReadableState(JSC::VM& vm, JSC::Structure* structure) - : Base(vm, structure) - , m_paused(0) - { - } - - DECLARE_VISIT_CHILDREN; - DECLARE_INFO; - - static constexpr unsigned StructureFlags = Base::StructureFlags; - - template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) - { - if constexpr (mode == JSC::SubspaceAccess::Concurrently) - return nullptr; - return subspaceForImpl(vm); - } - - static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm); - - 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()); - } - - static JSReadableState* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, bool isDuplex, JSObject* options) - { - JSReadableState* accessor = new (NotNull, JSC::allocateCell<JSReadableState>(vm)) JSReadableState(vm, structure); - accessor->finishCreation(vm, globalObject, isDuplex, options); - return accessor; - } - - void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject, bool isDuplex, JSObject* options); - static void destroy(JSCell*) {} - - enum Mask : uint32_t { - objectMode = 1 << 0, - emitClose = 1 << 1, - autoDestroy = 1 << 2, - ended = 1 << 3, - endEmitted = 1 << 4, - reading = 1 << 5, - constructed = 1 << 6, - sync = 1 << 7, - needReadable = 1 << 8, - emittedReadable = 1 << 9, - readableListening = 1 << 10, - resumeScheduled = 1 << 11, - errorEmitted = 1 << 12, - destroyed = 1 << 13, - closed = 1 << 14, - closeEmitted = 1 << 15, - multiAwaitDrain = 1 << 16, - readingMore = 1 << 17, - dataEmitted = 1 << 18, - }; - - constexpr bool getBool(Mask mask) { return m_bools.contains(mask); } - constexpr void setBool(Mask mask, bool val) - { - m_bools.set(mask, val); - } - - // 0 for null, 1 for true, -1 for false - int8_t m_paused = 0; - int8_t m_flowing = 0; - - WTF::OptionSet<Mask> m_bools; - - int64_t m_length = 0; - int64_t m_highWaterMark; - - mutable WriteBarrier<Unknown> m_buffer; - mutable WriteBarrier<Unknown> m_pipes; - mutable WriteBarrier<Unknown> m_errored; - mutable WriteBarrier<Unknown> m_defaultEncoding; - mutable WriteBarrier<Unknown> m_awaitDrainWriters; - mutable WriteBarrier<Unknown> m_decoder; - mutable WriteBarrier<Unknown> m_encoding; -}; - -class JSReadableStatePrototype : public JSC::JSNonFinalObject { -public: - using Base = JSC::JSNonFinalObject; - static JSReadableStatePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) - { - JSReadableStatePrototype* ptr = new (NotNull, JSC::allocateCell<JSReadableStatePrototype>(vm)) JSReadableStatePrototype(vm, 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: - JSReadableStatePrototype(JSC::VM& vm, JSC::Structure* structure) - : Base(vm, structure) - { - } - - void finishCreation(JSC::VM&, JSC::JSGlobalObject*); -}; - -class JSReadableStateConstructor final : public JSC::InternalFunction { -public: - using Base = JSC::InternalFunction; - static JSReadableStateConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure, JSReadableStatePrototype* 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()); - } - - void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype); - - // Must be defined for each specialization class. - static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); - DECLARE_EXPORT_INFO; - -private: - JSReadableStateConstructor(JSC::VM& vm, JSC::Structure* structure, JSC::NativeFunction nativeFunction) - : Base(vm, structure, nativeFunction, nativeFunction) - { - } - - void finishCreation(JSC::VM&, JSC::JSGlobalObject* globalObject, JSReadableStatePrototype* prototype); -}; - -} diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 252d00075..282cf6460 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -76,7 +76,10 @@ extern "C" uint8_t Bun__getExitCode(void*); extern "C" uint8_t Bun__setExitCode(void*, uint8_t); extern "C" void* Bun__getVM(); extern "C" Zig::GlobalObject* Bun__getDefaultGlobal(); +extern "C" bool Bun__GlobalObject__hasIPC(JSGlobalObject*); extern "C" const char* Bun__githubURL; +extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__Process__send); +extern "C" JSC_DECLARE_HOST_FUNCTION(Bun__Process__disconnect); static void dispatchExitInternal(JSC::JSGlobalObject* globalObject, Process* process, int exitCode) { @@ -525,6 +528,21 @@ static void loadSignalNumberMap() static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded) { + if (eventName.string() == "message"_s) { + if (isAdded) { + if (Bun__GlobalObject__hasIPC(eventEmitter.scriptExecutionContext()->jsGlobalObject()) + && eventEmitter.listenerCount(eventName) == 1) { + eventEmitter.scriptExecutionContext()->refEventLoop(); + eventEmitter.m_hasIPCRef = true; + } + } else { + if (eventEmitter.listenerCount(eventName) == 0 && eventEmitter.m_hasIPCRef) { + eventEmitter.scriptExecutionContext()->unrefEventLoop(); + } + } + return; + } + loadSignalNumberMap(); static std::once_flag signalNumberToNameMapOnceFlag; @@ -739,6 +757,20 @@ JSC_DEFINE_CUSTOM_SETTER(setProcessExitCode, (JSC::JSGlobalObject * lexicalGloba return true; } +JSC_DEFINE_CUSTOM_GETTER(processConnected, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName name)) +{ + Process* process = jsDynamicCast<Process*>(JSValue::decode(thisValue)); + if (!process) { + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(jsBoolean(Bun__GlobalObject__hasIPC(process->globalObject()))); +} +JSC_DEFINE_CUSTOM_SETTER(setProcessConnected, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName)) +{ + return false; +} + static JSValue constructVersions(VM& vm, JSObject* processObject) { auto* globalObject = processObject->globalObject(); @@ -912,6 +944,26 @@ static JSValue constructStdin(VM& vm, JSObject* processObject) RELEASE_AND_RETURN(scope, result); } +static JSValue constructProcessSend(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + if (Bun__GlobalObject__hasIPC(globalObject)) { + return JSC::JSFunction::create(vm, globalObject, 1, String("send"_s), Bun__Process__send, ImplementationVisibility::Public); + } else { + return jsNumber(4); + } +} + +static JSValue constructProcessDisconnect(VM& vm, JSObject* processObject) +{ + auto* globalObject = processObject->globalObject(); + if (Bun__GlobalObject__hasIPC(globalObject)) { + return JSC::JSFunction::create(vm, globalObject, 1, String("disconnect"_s), Bun__Process__disconnect, ImplementationVisibility::Public); + } else { + return jsUndefined(); + } +} + static JSValue constructPid(VM& vm, JSObject* processObject) { return jsNumber(getpid()); @@ -1687,6 +1739,29 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionKill, return JSValue::encode(jsUndefined()); } +extern "C" void Process__emitMessageEvent(Zig::GlobalObject* global, EncodedJSValue value) +{ + auto* process = static_cast<Process*>(global->processObject()); + auto& vm = global->vm(); + auto ident = Identifier::fromString(vm, "message"_s); + if (process->wrapped().hasEventListeners(ident)) { + JSC::MarkedArgumentBuffer args; + args.append(JSValue::decode(value)); + process->wrapped().emit(ident, args); + } +} + +extern "C" void Process__emitDisconnectEvent(Zig::GlobalObject* global) +{ + auto* process = static_cast<Process*>(global->processObject()); + auto& vm = global->vm(); + auto ident = Identifier::fromString(vm, "disconnect"_s); + if (process->wrapped().hasEventListeners(ident)) { + JSC::MarkedArgumentBuffer args; + process->wrapped().emit(ident, args); + } +} + /* Source for Process.lut.h @begin processObjectTable abort Process_functionAbort Function 1 @@ -1699,9 +1774,11 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionKill, browser constructBrowser PropertyCallback chdir Process_functionChdir Function 1 config constructProcessConfigObject PropertyCallback + connected processConnected CustomAccessor cpuUsage Process_functionCpuUsage Function 1 cwd Process_functionCwd Function 1 debugPort processDebugPort CustomAccessor + disconnect constructProcessDisconnect PropertyCallback dlopen Process_functionDlopen Function 1 emitWarning Process_emitWarning Function 1 env constructEnv PropertyCallback @@ -1731,6 +1808,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionKill, release constructProcessReleaseObject PropertyCallback revision constructRevision PropertyCallback setSourceMapsEnabled Process_stubEmptyFunction Function 1 + send constructProcessSend PropertyCallback stderr constructStderr PropertyCallback stdin constructStdin PropertyCallback stdout constructStdout PropertyCallback diff --git a/src/bun.js/bindings/Process.lut.h b/src/bun.js/bindings/Process.lut.h index 4086fb19e..dda54ff01 100644 --- a/src/bun.js/bindings/Process.lut.h +++ b/src/bun.js/bindings/Process.lut.h @@ -1,6 +1,6 @@ // File generated via `make generate-builtins` -static const struct CompactHashIndex processObjectTableIndex[143] = { - { 44, -1 }, +static const struct CompactHashIndex processObjectTableIndex[267] = { + { 47, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, @@ -11,13 +11,12 @@ static const struct CompactHashIndex processObjectTableIndex[143] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 15, 129 }, { -1, -1 }, { -1, -1 }, - { 18, 139 }, { -1, -1 }, - { 46, -1 }, + { 20, -1 }, { -1, -1 }, + { 49, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, @@ -33,37 +32,166 @@ static const struct CompactHashIndex processObjectTableIndex[143] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 3, 142 }, - { 1, 128 }, { -1, -1 }, - { 60, -1 }, { -1, -1 }, - { 10, -1 }, + { 8, -1 }, + { -1, -1 }, + { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 32, -1 }, { -1, -1 }, { -1, -1 }, + { 34, -1 }, { -1, -1 }, { -1, -1 }, - { 53, -1 }, - { 27, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 29, -1 }, + { 13, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 59, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 55, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 43, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 0, -1 }, + { 28, -1 }, + { 37, -1 }, + { 42, -1 }, + { -1, -1 }, + { 25, -1 }, { 12, -1 }, { -1, -1 }, + { -1, -1 }, + { 62, -1 }, + { -1, -1 }, + { -1, -1 }, + { 33, -1 }, + { 44, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 26, -1 }, + { -1, -1 }, + { -1, -1 }, + { 22, -1 }, + { -1, -1 }, + { 5, -1 }, + { -1, -1 }, + { 64, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 27, 261 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 23, 262 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 50, 265 }, + { -1, -1 }, { 19, -1 }, { -1, -1 }, - { 14, 138 }, { -1, -1 }, - { 37, -1 }, { -1, -1 }, - { 39, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 17, 257 }, + { -1, -1 }, + { 14, -1 }, + { 57, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 3, 266 }, + { 1, -1 }, + { -1, -1 }, + { 63, -1 }, + { -1, -1 }, + { 11, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, { 56, -1 }, - { 36, -1 }, - { 6, 140 }, { -1, -1 }, - { 52, -1 }, + { -1, -1 }, + { -1, -1 }, + { 10, 256 }, + { -1, -1 }, + { 16, 263 }, + { -1, -1 }, + { 39, -1 }, + { -1, -1 }, + { 41, -1 }, + { -1, -1 }, + { 38, -1 }, + { 6, 264 }, + { -1, -1 }, + { -1, -1 }, { 4, -1 }, - { 48, -1 }, + { 51, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, @@ -75,77 +203,73 @@ static const struct CompactHashIndex processObjectTableIndex[143] = { { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 41, -1 }, { -1, -1 }, - { 29, 133 }, { -1, -1 }, - { 0, -1 }, - { 26, 136 }, - { 16, 130 }, - { 40, -1 }, + { 31, 260 }, + { -1, -1 }, + { -1, -1 }, + { 48, -1 }, + { 18, 258 }, + { -1, -1 }, { -1, -1 }, - { 23, -1 }, - { 11, -1 }, { -1, -1 }, { -1, -1 }, - { 59, -1 }, { -1, -1 }, { -1, -1 }, - { 31, 137 }, { -1, -1 }, - { 30, -1 }, - { 22, -1 }, { -1, -1 }, { -1, -1 }, + { 53, -1 }, + { -1, -1 }, + { 32, -1 }, { 24, -1 }, { -1, -1 }, { -1, -1 }, - { 20, -1 }, { -1, -1 }, - { 5, -1 }, { -1, -1 }, - { 61, -1 }, - { 49, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 13, 131 }, + { -1, -1 }, + { -1, -1 }, + { 52, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 15, 259 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { 9, -1 }, - { 25, 134 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 21, 135 }, { -1, -1 }, { -1, -1 }, { -1, -1 }, - { 47, 141 }, { -1, -1 }, - { 17, -1 }, - { 8, -1 }, - { 28, -1 }, - { 33, 132 }, - { 34, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { -1, -1 }, + { 21, -1 }, + { 30, -1 }, { 35, -1 }, - { 38, -1 }, - { 42, -1 }, - { 43, -1 }, + { 36, -1 }, + { 40, -1 }, { 45, -1 }, - { 50, -1 }, - { 51, -1 }, + { 46, -1 }, { 54, -1 }, - { 55, -1 }, - { 57, -1 }, { 58, -1 }, + { 60, -1 }, + { 61, -1 }, }; -static const struct HashTableValue processObjectTableValues[62] = { +static const struct HashTableValue processObjectTableValues[65] = { { "abort"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionAbort, 1 } }, { "allowedNodeEnvironmentFlags"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, Process_stubEmptySet } }, { "arch"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructArch } }, @@ -156,9 +280,11 @@ static const struct HashTableValue processObjectTableValues[62] = { { "browser"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructBrowser } }, { "chdir"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionChdir, 1 } }, { "config"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessConfigObject } }, + { "connected"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processConnected, setProcessConnected } }, { "cpuUsage"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionCpuUsage, 1 } }, { "cwd"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionCwd, 1 } }, { "debugPort"_s, static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, processDebugPort, setProcessDebugPort } }, + { "disconnect"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessDisconnect } }, { "dlopen"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionDlopen, 1 } }, { "emitWarning"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_emitWarning, 1 } }, { "env"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructEnv } }, @@ -188,6 +314,7 @@ static const struct HashTableValue processObjectTableValues[62] = { { "release"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessReleaseObject } }, { "revision"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructRevision } }, { "setSourceMapsEnabled"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_stubEmptyFunction, 1 } }, + { "send"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessSend } }, { "stderr"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStderr } }, { "stdin"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStdin } }, { "stdout"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructStdout } }, @@ -211,4 +338,4 @@ static const struct HashTableValue processObjectTableValues[62] = { }; static const struct HashTable processObjectTable = - { 62, 127, true, nullptr, processObjectTableValues, processObjectTableIndex }; + { 65, 255, true, nullptr, processObjectTableValues, processObjectTableIndex }; diff --git a/src/bun.js/bindings/Serialization.cpp b/src/bun.js/bindings/Serialization.cpp new file mode 100644 index 000000000..89937ebbb --- /dev/null +++ b/src/bun.js/bindings/Serialization.cpp @@ -0,0 +1,49 @@ +#include "root.h" +#include "headers-handwritten.h" +#include "ExceptionOr.h" +#include "MessagePort.h" +#include "SerializedScriptValue.h" +#include "JSDOMExceptionHandling.h" + +using namespace JSC; +using namespace WebCore; + +/// This is used for Bun.spawn() IPC because otherwise we would have to copy the data once to get it to zig, then write it. +/// Returns `true` on success, `false` on failure + throws a JS error. +extern "C" bool Bun__serializeJSValueForSubprocess(JSGlobalObject* globalObject, EncodedJSValue encodedValue, int fd) +{ + JSValue value = JSValue::decode(encodedValue); + + Vector<JSC::Strong<JSC::JSObject>> transferList; + Vector<RefPtr<MessagePort>> dummyPorts; + ExceptionOr<Ref<SerializedScriptValue>> serialized = SerializedScriptValue::create(*globalObject, value, WTFMove(transferList), + dummyPorts); + + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (serialized.hasException()) { + WebCore::propagateException(*globalObject, scope, + serialized.releaseException()); + RELEASE_AND_RETURN(scope, false); + } + + auto serializedValue = serialized.releaseReturnValue(); + auto bytes = serializedValue.ptr()->wireBytes(); + + uint8_t id = 2; // IPCMessageType.SerializedMessage + write(fd, &id, sizeof(uint8_t)); + uint32_t size = bytes.size(); + write(fd, &size, sizeof(uint32_t)); + write(fd, bytes.data(), size); + + RELEASE_AND_RETURN(scope, true); +} + +extern "C" EncodedJSValue Bun__JSValue__deserialize(JSGlobalObject* globalObject, const uint8_t* bytes, size_t size) +{ + Vector<uint8_t> vector(bytes, size); + /// ?! did i just give ownership of these bytes to JSC? + auto scriptValue = SerializedScriptValue::createFromWireBytes(WTFMove(vector)); + return JSValue::encode(scriptValue->deserialize(*globalObject, globalObject)); +}
\ No newline at end of file diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp index 5a10a05d3..ec2add296 100644 --- a/src/bun.js/bindings/ZigGeneratedClasses.cpp +++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp @@ -23215,6 +23215,9 @@ JSC_DECLARE_CUSTOM_GETTER(SubprocessPrototype__readableGetterWrap); extern "C" EncodedJSValue SubprocessPrototype__doRef(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); JSC_DECLARE_HOST_FUNCTION(SubprocessPrototype__refCallback); +extern "C" EncodedJSValue SubprocessPrototype__doSend(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); +JSC_DECLARE_HOST_FUNCTION(SubprocessPrototype__sendCallback); + extern "C" JSC::EncodedJSValue SubprocessPrototype__getSignalCode(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject); JSC_DECLARE_CUSTOM_GETTER(SubprocessPrototype__signalCodeGetterWrap); @@ -23243,6 +23246,7 @@ static const HashTableValue JSSubprocessPrototypeTableValues[] = { { "pid"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, SubprocessPrototype__pidGetterWrap, 0 } }, { "readable"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, SubprocessPrototype__readableGetterWrap, 0 } }, { "ref"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, SubprocessPrototype__refCallback, 0 } }, + { "send"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, SubprocessPrototype__sendCallback, 1 } }, { "signalCode"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, SubprocessPrototype__signalCodeGetterWrap, 0 } }, { "stderr"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, SubprocessPrototype__stderrGetterWrap, 0 } }, { "stdin"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, SubprocessPrototype__stdinGetterWrap, 0 } }, @@ -23400,6 +23404,34 @@ JSC_DEFINE_HOST_FUNCTION(SubprocessPrototype__refCallback, (JSGlobalObject * lex return SubprocessPrototype__doRef(thisObject->wrapped(), lexicalGlobalObject, callFrame); } +JSC_DEFINE_HOST_FUNCTION(SubprocessPrototype__sendCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + auto& vm = lexicalGlobalObject->vm(); + + JSSubprocess* thisObject = jsDynamicCast<JSSubprocess*>(callFrame->thisValue()); + + if (UNLIKELY(!thisObject)) { + auto throwScope = DECLARE_THROW_SCOPE(vm); + throwVMTypeError(lexicalGlobalObject, throwScope, "Expected 'this' to be instanceof Subprocess"_s); + return JSValue::encode({}); + } + + JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject); + +#ifdef BUN_DEBUG + /** View the file name of the JS file that called this function + * from a debugger */ + SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); + const char* fileName = sourceOrigin.string().utf8().data(); + static const char* lastFileName = nullptr; + if (lastFileName != fileName) { + lastFileName = fileName; + } +#endif + + return SubprocessPrototype__doSend(thisObject->wrapped(), lexicalGlobalObject, callFrame); +} + JSC_DEFINE_CUSTOM_GETTER(SubprocessPrototype__signalCodeGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { auto& vm = lexicalGlobalObject->vm(); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 286084b4d..091cc0b29 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -90,8 +90,6 @@ #include "JSCloseEvent.h" #include "JSFetchHeaders.h" #include "JSStringDecoder.h" -#include "JSReadableState.h" -#include "JSReadableHelper.h" #include "Process.h" #include "AsyncContextFrame.h" @@ -1628,7 +1626,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, switch (callFrame->argumentCount()) { case 0: { - JSC::throwTypeError(globalObject, scope, "lazyLoad needs 1 argument (a string)"_s); + JSC::throwTypeError(globalObject, scope, "$lazy needs 1 argument (a string)"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1637,7 +1635,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, if (moduleName.isNumber()) { switch (moduleName.toInt32(globalObject)) { case 0: { - JSC::throwTypeError(globalObject, scope, "lazyLoad expects a string"_s); + JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1654,7 +1652,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, default: { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSC::throwTypeError(globalObject, scope, "lazyLoad expects a string"_s); + JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1663,7 +1661,7 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, auto string = moduleName.toWTFString(globalObject); if (string.isNull()) { - JSC::throwTypeError(globalObject, scope, "lazyLoad expects a string"_s); + JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); scope.release(); return JSC::JSValue::encode(JSC::JSValue {}); } @@ -1673,7 +1671,6 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, } if (string == "worker_threads"_s) { - JSValue workerData = jsUndefined(); JSValue threadId = jsNumber(0); @@ -1708,27 +1705,6 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, JSFunction::create(vm, globalObject, 1, fileURLToPathString, functionFileURLToPath, ImplementationVisibility::Public, NoIntrinsic)); } - if (string == "bun:stream"_s) { - auto* obj = constructEmptyObject(globalObject); - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "BufferList"_s)), reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSBufferList(), 0); - obj->putDirect(vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "ReadableState"_s)), reinterpret_cast<Zig::GlobalObject*>(globalObject)->JSReadableState(), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "maybeReadMore"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "maybeReadMore"_s, jsReadable_maybeReadMore, ImplementationVisibility::Public), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "resume"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "resume"_s, jsReadable_resume, ImplementationVisibility::Public), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "emitReadable"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "emitReadable"_s, jsReadable_emitReadable, ImplementationVisibility::Public), 0); - obj->putDirect( - vm, JSC::PropertyName(JSC::Identifier::fromString(vm, "onEofChunk"_s)), - JSC::JSFunction::create(vm, globalObject, 0, "onEofChunk"_s, jsReadable_onEofChunk, ImplementationVisibility::Public), 0); - return JSValue::encode(obj); - } - if (string == "events"_s) { - return JSValue::encode(WebCore::JSEventEmitter::getConstructor(vm, globalObject)); - } if (string == "internal/tls"_s) { auto* obj = constructEmptyObject(globalObject); @@ -1759,9 +1735,9 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, return JSValue::encode(obj); } - if (string == "masqueradesAsUndefined"_s) { - return JSValue::encode(InternalFunction::createFunctionThatMasqueradesAsUndefined(vm, globalObject, 0, String(), functionCallNotImplemented)); - } + // if (string == "masqueradesAsUndefined"_s) { + // return JSValue::encode(InternalFunction::createFunctionThatMasqueradesAsUndefined(vm, globalObject, 0, String(), functionCallNotImplemented)); + // } if (string == "vm"_s) { auto* obj = constructEmptyObject(globalObject); @@ -1818,9 +1794,9 @@ static JSC_DEFINE_HOST_FUNCTION(functionLazyLoad, return JSC::JSValue::encode(obj); } - return JSC::JSValue::encode(JSC::jsUndefined()); - - break; + JSC::throwTypeError(globalObject, scope, "$lazy expects a string"_s); + scope.release(); + return JSC::JSValue::encode(JSC::JSValue {}); } } } @@ -2360,6 +2336,7 @@ private: functionNoop, ImplementationVisibility::Public, NoIntrinsic, functionNoop, &DOMJITSignatureForPerformanceNow); this->putDirect(vm, JSC::Identifier::fromString(vm, "mark"_s), noopNotImplemented, JSC::PropertyAttribute::DOMJITFunction | JSC::PropertyAttribute::Function); + this->putDirect(vm, JSC::Identifier::fromString(vm, "markResourceTiming"_s), noopNotImplemented, JSC::PropertyAttribute::DOMJITFunction | JSC::PropertyAttribute::Function); this->putDirect(vm, JSC::Identifier::fromString(vm, "measure"_s), noopNotImplemented, JSC::PropertyAttribute::DOMJITFunction | JSC::PropertyAttribute::Function); this->putDirect( @@ -3004,10 +2981,6 @@ void GlobalObject::finishCreation(VM& vm) [](const Initializer<JSFunction>& init) { init.set(JSFunction::create(init.vm, init.owner, 4, "performMicrotask"_s, jsFunctionPerformMicrotask, ImplementationVisibility::Public)); }); - m_emitReadableNextTickFunction.initLater( - [](const Initializer<JSFunction>& init) { - init.set(JSFunction::create(init.vm, init.owner, 4, "emitReadable"_s, WebCore::jsReadable_emitReadable_, ImplementationVisibility::Public)); - }); m_bunSleepThenCallback.initLater( [](const Initializer<JSFunction>& init) { @@ -3322,18 +3295,6 @@ void GlobalObject::finishCreation(VM& vm) init.setConstructor(constructor); }); - m_JSReadableStateClassStructure.initLater( - [](LazyClassStructure::Initializer& init) { - auto* prototype = JSReadableStatePrototype::create( - init.vm, init.global, JSReadableStatePrototype::createStructure(init.vm, init.global, init.global->objectPrototype())); - auto* structure = JSReadableState::createStructure(init.vm, init.global, prototype); - auto* constructor = JSReadableStateConstructor::create( - init.vm, init.global, JSReadableStateConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype); - init.setPrototype(prototype); - init.setStructure(structure); - init.setConstructor(constructor); - }); - m_JSFFIFunctionStructure.initLater( [](LazyClassStructure::Initializer& init) { init.setStructure(Zig::JSFFIFunction::createStructure(init.vm, init.global, init.global->functionPrototype())); @@ -4091,6 +4052,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->m_readableStreamToJSON); visitor.append(thisObject->m_readableStreamToText); visitor.append(thisObject->m_readableStreamToFormData); + visitor.append(thisObject->m_nodeModuleOverriddenResolveFilename); visitor.append(thisObject->m_JSBlobSetterValue); visitor.append(thisObject->m_JSBroadcastChannelSetterValue); @@ -4122,7 +4084,6 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_JSFileSinkClassStructure.visit(visitor); thisObject->m_JSHTTPResponseSinkClassStructure.visit(visitor); thisObject->m_JSHTTPSResponseSinkClassStructure.visit(visitor); - thisObject->m_JSReadableStateClassStructure.visit(visitor); thisObject->m_JSStringDecoderClassStructure.visit(visitor); thisObject->m_NapiClassStructure.visit(visitor); thisObject->m_JSBufferClassStructure.visit(visitor); @@ -4148,7 +4109,6 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_subtleCryptoObject.visit(visitor); thisObject->m_JSHTTPResponseController.visit(visitor); thisObject->m_callSiteStructure.visit(visitor); - thisObject->m_emitReadableNextTickFunction.visit(visitor); thisObject->m_JSBufferSubclassStructure.visit(visitor); thisObject->m_cryptoObject.visit(visitor); thisObject->m_JSDOMFileConstructor.visit(visitor); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index e622016de..7377e6693 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -238,10 +238,6 @@ public: JSC::JSObject* JSStringDecoder() { return m_JSStringDecoderClassStructure.constructorInitializedOnMainThread(this); } JSC::JSValue JSStringDecoderPrototype() { return m_JSStringDecoderClassStructure.prototypeInitializedOnMainThread(this); } - JSC::Structure* JSReadableStateStructure() { return m_JSReadableStateClassStructure.getInitializedOnMainThread(this); } - JSC::JSObject* JSReadableState() { return m_JSReadableStateClassStructure.constructorInitializedOnMainThread(this); } - JSC::JSValue JSReadableStatePrototype() { return m_JSReadableStateClassStructure.prototypeInitializedOnMainThread(this); } - JSC::Structure* NodeVMScriptStructure() { return m_NodeVMScriptClassStructure.getInitializedOnMainThread(this); } JSC::JSObject* NodeVMScript() { return m_NodeVMScriptClassStructure.constructorInitializedOnMainThread(this); } JSC::JSValue NodeVMScriptPrototype() { return m_NodeVMScriptClassStructure.prototypeInitializedOnMainThread(this); } @@ -261,8 +257,6 @@ public: JSC::JSFunction* utilInspectStylizeColorFunction() { return m_utilInspectStylizeColorFunction.getInitializedOnMainThread(this); } JSC::JSFunction* utilInspectStylizeNoColorFunction() { return m_utilInspectStylizeNoColorFunction.getInitializedOnMainThread(this); } - JSC::JSFunction* emitReadableNextTickFunction() { return m_emitReadableNextTickFunction.getInitializedOnMainThread(this); } - JSObject* requireFunctionUnbound() { return m_requireFunctionUnbound.getInitializedOnMainThread(this); } JSObject* requireResolveFunctionUnbound() { return m_requireResolveFunctionUnbound.getInitializedOnMainThread(this); } Bun::InternalModuleRegistry* internalModuleRegistry() { return m_internalModuleRegistry.getInitializedOnMainThread(this); } @@ -385,6 +379,10 @@ public: mutable WriteBarrier<JSFunction> m_readableStreamToText; mutable WriteBarrier<JSFunction> m_readableStreamToFormData; + // This is set when doing `require('module')._resolveFilename = ...` + // a hack used by Next.js to inject their versions of webpack and react + mutable WriteBarrier<JSFunction> m_nodeModuleOverriddenResolveFilename; + mutable WriteBarrier<Unknown> m_nextTickQueue; mutable WriteBarrier<Unknown> m_BunCommonJSModuleValue; mutable WriteBarrier<Unknown> m_JSBroadcastChannelSetterValue; @@ -502,7 +500,6 @@ private: LazyClassStructure m_JSFileSinkClassStructure; LazyClassStructure m_JSHTTPResponseSinkClassStructure; LazyClassStructure m_JSHTTPSResponseSinkClassStructure; - LazyClassStructure m_JSReadableStateClassStructure; LazyClassStructure m_JSStringDecoderClassStructure; LazyClassStructure m_NapiClassStructure; LazyClassStructure m_callSiteStructure; @@ -526,7 +523,7 @@ private: LazyProperty<JSGlobalObject, JSFunction> m_utilInspectFunction; LazyProperty<JSGlobalObject, JSFunction> m_utilInspectStylizeColorFunction; LazyProperty<JSGlobalObject, JSFunction> m_utilInspectStylizeNoColorFunction; - LazyProperty<JSGlobalObject, JSFunction> m_emitReadableNextTickFunction; + LazyProperty<JSGlobalObject, JSMap> m_lazyReadableStreamPrototypeMap; LazyProperty<JSGlobalObject, JSMap> m_requireMap; LazyProperty<JSGlobalObject, Structure> m_encodeIntoObjectStructure; diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 532d2bc68..1a82fc5e6 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -4917,6 +4917,13 @@ pub const JSValue = enum(JSValueReprInt) { JSC.markBinding(@src()); return AsyncContextFrame__withAsyncContextIfNeeded(global, this); } + + extern "c" fn Bun__JSValue__deserialize(global: *JSGlobalObject, data: [*]const u8, len: isize) JSValue; + + /// Deserializes a JSValue from a serialized buffer. Zig version of `import('bun:jsc').deserialize` + pub inline fn deserialize(bytes: []const u8, global: *JSGlobalObject) JSValue { + return Bun__JSValue__deserialize(global, bytes.ptr, @intCast(bytes.len)); + } }; extern "c" fn AsyncContextFrame__withAsyncContextIfNeeded(global: *JSGlobalObject, callback: JSValue) JSValue; diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig index e7e16e889..329506718 100644 --- a/src/bun.js/bindings/generated_classes.zig +++ b/src/bun.js/bindings/generated_classes.zig @@ -6140,6 +6140,8 @@ pub const JSSubprocess = struct { if (@TypeOf(Subprocess.doRef) != CallbackType) @compileLog("Expected Subprocess.doRef to be a callback but received " ++ @typeName(@TypeOf(Subprocess.doRef))); + if (@TypeOf(Subprocess.doSend) != CallbackType) + @compileLog("Expected Subprocess.doSend to be a callback but received " ++ @typeName(@TypeOf(Subprocess.doSend))); if (@TypeOf(Subprocess.getSignalCode) != GetterType) @compileLog("Expected Subprocess.getSignalCode to be a getter"); @@ -6159,6 +6161,7 @@ pub const JSSubprocess = struct { if (!JSC.is_bindgen) { @export(Subprocess.doRef, .{ .name = "SubprocessPrototype__doRef" }); + @export(Subprocess.doSend, .{ .name = "SubprocessPrototype__doSend" }); @export(Subprocess.doUnref, .{ .name = "SubprocessPrototype__doUnref" }); @export(Subprocess.finalize, .{ .name = "SubprocessClass__finalize" }); @export(Subprocess.getExitCode, .{ .name = "SubprocessPrototype__getExitCode" }); diff --git a/src/bun.js/bindings/webcore/EventEmitter.h b/src/bun.js/bindings/webcore/EventEmitter.h index 8db59c188..23687d43a 100644 --- a/src/bun.js/bindings/webcore/EventEmitter.h +++ b/src/bun.js/bindings/webcore/EventEmitter.h @@ -94,6 +94,8 @@ public: } } + bool m_hasIPCRef { false }; + private: EventEmitter(ScriptExecutionContext& context) : ContextDestructionObserver(&context) diff --git a/src/bun.js/ipc.zig b/src/bun.js/ipc.zig new file mode 100644 index 000000000..05b9d683b --- /dev/null +++ b/src/bun.js/ipc.zig @@ -0,0 +1,238 @@ +const uws = @import("../deps/uws.zig"); +const bun = @import("root").bun; +const Environment = bun.Environment; +const Global = bun.Global; +const strings = bun.strings; +const string = bun.string; +const Output = @import("root").bun.Output; +const MutableString = @import("root").bun.MutableString; +const std = @import("std"); +const Allocator = std.mem.Allocator; +const JSC = @import("root").bun.JSC; +const JSValue = JSC.JSValue; +const JSGlobalObject = JSC.JSGlobalObject; + +pub const log = Output.scoped(.IPC, false); + +pub const ipcHeaderLength = @sizeOf(u8) + @sizeOf(u32); +pub const ipcVersion = 1; + +pub const DecodedIPCMessage = union(enum) { + version: u32, + data: JSValue, +}; + +pub const DecodeIPCMessageResult = struct { + bytes_consumed: u32, + message: DecodedIPCMessage, +}; + +pub const IPCDecodeError = error{ NotEnoughBytes, InvalidFormat }; + +pub const IPCMessageType = enum(u8) { + Version = 1, + SerializedMessage = 2, + _, +}; + +/// Given potentially unfinished buffer `data`, attempt to decode and process a message from it. +/// Returns `NotEnoughBytes` if there werent enough bytes +/// Returns `InvalidFormat` if the message was invalid, probably close the socket in this case +/// otherwise returns the number of bytes consumed. +pub fn decodeIPCMessage( + data: []const u8, + globalThis: *JSC.JSGlobalObject, +) IPCDecodeError!DecodeIPCMessageResult { + if (data.len < ipcHeaderLength) { + return IPCDecodeError.NotEnoughBytes; + } + + const message_type: IPCMessageType = @enumFromInt(data[0]); + const message_len: u32 = @as(*align(1) const u32, @ptrCast(data[1 .. @sizeOf(u32) + 1])).*; + + log("Received IPC message type {d} ({s}) len {d}", .{ + @intFromEnum(message_type), + std.enums.tagName(IPCMessageType, message_type) orelse "unknown", + message_len, + }); + + switch (message_type) { + .Version => { + return .{ + .bytes_consumed = ipcHeaderLength, + .message = .{ .version = message_len }, + }; + }, + .SerializedMessage => { + if (data.len < (ipcHeaderLength + message_len)) { + return IPCDecodeError.NotEnoughBytes; + } + + const message = data[ipcHeaderLength .. ipcHeaderLength + message_len]; + const deserialized = JSValue.deserialize(message, globalThis); + + if (deserialized == .zero) { + return IPCDecodeError.InvalidFormat; + } + + return .{ + .bytes_consumed = ipcHeaderLength + message_len, + .message = .{ .data = deserialized }, + }; + }, + else => { + return IPCDecodeError.InvalidFormat; + }, + } +} + +pub const Socket = uws.NewSocketHandler(false); + +/// This type is shared between VirtualMachine and Subprocess for their respective IPC handlers +/// +/// `Context` must be a struct that implements this interface: +/// struct { +/// globalThis: ?*JSGlobalObject, +/// ipc_buffer: bun.ByteList, +/// +/// fn handleIPCMessage(*Context, DecodedIPCMessage) void +/// fn handleIPCClose(*Context, Socket) void +/// } +pub fn NewIPCHandler(comptime Context: type) type { + return struct { + pub fn onOpen( + _: *Context, + socket: Socket, + ) void { + // Write the version message + const Data = extern struct { + type: IPCMessageType align(1) = .Version, + version: u32 align(1) = ipcVersion, + }; + const data: []const u8 = comptime @as([@sizeOf(Data)]u8, @bitCast(Data{}))[0..]; + _ = socket.write(data, false); + socket.flush(); + } + pub fn onClose( + this: *Context, + socket: Socket, + _: c_int, + _: ?*anyopaque, + ) void { + // ?! does uSockets .close call onClose? + log("onClose\n", .{}); + this.handleIPCClose(socket); + } + // extern fn getpid() i32; + pub fn onData( + this: *Context, + socket: Socket, + data_: []const u8, + ) void { + var data = data_; + log("onData {}", .{std.fmt.fmtSliceHexLower(data)}); + + // if (comptime Context == bun.JSC.VirtualMachine.IPCInstance) { + // logDataOnly("{d} -> '{}'", .{ getpid(), std.fmt.fmtSliceHexLower(data) }); + // } + + // In the VirtualMachine case, `globalThis` is an optional, in case + // the vm is freed before the socket closes. + var globalThis = switch (@typeInfo(@TypeOf(this.globalThis))) { + .Pointer => this.globalThis, + .Optional => brk: { + if (this.globalThis) |global| { + break :brk global; + } + this.handleIPCClose(socket); + socket.close(0, null); + return; + }, + else => @panic("Unexpected globalThis type: " ++ @typeName(@TypeOf(this.globalThis))), + }; + + // Decode the message with just the temporary buffer, and if that + // fails (not enough bytes) then we allocate to .ipc_buffer + if (this.ipc_buffer.len == 0) { + while (true) { + const result = decodeIPCMessage(data, globalThis) catch |e| switch (e) { + error.NotEnoughBytes => { + _ = this.ipc_buffer.write(bun.default_allocator, data) catch @panic("OOM"); + log("hit NotEnoughBytes", .{}); + return; + }, + error.InvalidFormat => { + Output.printErrorln("InvalidFormatError during IPC message handling", .{}); + this.handleIPCClose(socket); + socket.close(0, null); + return; + }, + }; + + this.handleIPCMessage(result.message); + + if (result.bytes_consumed < data.len) { + data = data[result.bytes_consumed..]; + } else { + return; + } + } + } + + _ = this.ipc_buffer.write(bun.default_allocator, data) catch @panic("OOM"); + + var slice = this.ipc_buffer.slice(); + while (true) { + const result = decodeIPCMessage(slice, globalThis) catch |e| switch (e) { + error.NotEnoughBytes => { + // copy the remaining bytes to the start of the buffer + std.mem.copyForwards(u8, this.ipc_buffer.ptr[0..slice.len], slice); + this.ipc_buffer.len = @truncate(slice.len); + log("hit NotEnoughBytes2", .{}); + return; + }, + error.InvalidFormat => { + Output.printErrorln("InvalidFormatError during IPC message handling", .{}); + this.handleIPCClose(socket); + socket.close(0, null); + return; + }, + }; + + this.handleIPCMessage(result.message); + + if (result.bytes_consumed < slice.len) { + slice = slice[result.bytes_consumed..]; + } else { + // clear the buffer + this.ipc_buffer.len = 0; + return; + } + } + } + + pub fn onWritable( + _: *Context, + _: Socket, + ) void {} + pub fn onTimeout( + _: *Context, + _: Socket, + ) void {} + pub fn onConnectError( + _: *Context, + _: Socket, + _: c_int, + ) void {} + pub fn onEnd( + _: *Context, + _: Socket, + ) void {} + }; +} + +/// This is used for Bun.spawn() IPC because otherwise we would have to copy the data once to get it to zig, then write it. +/// Returns `true` on success, `false` on failure + throws a JS error. +extern fn Bun__serializeJSValueForSubprocess(global: *JSC.JSGlobalObject, value: JSValue, fd: bun.FileDescriptor) bool; + +pub const serializeJSValueForSubprocess = Bun__serializeJSValueForSubprocess; diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 212e6be71..6fd4cf557 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -84,6 +84,7 @@ const EventLoop = JSC.EventLoop; const PendingResolution = @import("../resolver/resolver.zig").PendingResolution; const ThreadSafeFunction = JSC.napi.ThreadSafeFunction; const PackageManager = @import("../install/install.zig").PackageManager; +const IPC = @import("ipc.zig"); const ModuleLoader = JSC.ModuleLoader; const FetchFlags = JSC.FetchFlags; @@ -259,7 +260,6 @@ pub const SavedSourceMap = struct { const uws = @import("root").bun.uws; pub export fn Bun__getDefaultGlobal() *JSGlobalObject { - _ = @sizeOf(JSC.VirtualMachine) + 1; return JSC.VirtualMachine.get().global; } @@ -280,20 +280,41 @@ export fn Bun__readOriginTimerStart(vm: *JSC.VirtualMachine) f64 { return @as(f64, @floatCast((@as(f64, @floatFromInt(vm.origin_timestamp)) + JSC.VirtualMachine.origin_relative_epoch) / 1_000_000.0)); } -// comptime { -// if (!JSC.is_bindgen) { -// _ = Bun__getDefaultGlobal; -// _ = Bun__getVM; -// _ = Bun__drainMicrotasks; -// _ = Bun__queueTask; -// _ = Bun__queueTaskConcurrently; -// _ = Bun__handleRejectedPromise; -// _ = Bun__readOriginTimer; -// _ = Bun__onDidAppendPlugin; -// _ = Bun__readOriginTimerStart; -// _ = Bun__reportUnhandledError; -// } -// } +pub export fn Bun__GlobalObject__hasIPC(global: *JSC.JSGlobalObject) bool { + return global.bunVM().ipc != null; +} + +pub export fn Bun__Process__send( + globalObject: *JSGlobalObject, + callFrame: *JSC.CallFrame, +) JSValue { + if (callFrame.argumentsCount() < 1) { + globalObject.throwInvalidArguments("process.send requires at least one argument", .{}); + return .zero; + } + var vm = globalObject.bunVM(); + if (vm.ipc) |ipc| { + const fd = ipc.socket.fd(); + const success = IPC.serializeJSValueForSubprocess( + globalObject, + callFrame.argument(0), + fd, + ); + return if (success) .undefined else .zero; + } else { + globalObject.throw("IPC Socket is no longer open.", .{}); + return .zero; + } +} + +pub export fn Bun__Process__disconnect( + globalObject: *JSGlobalObject, + callFrame: *JSC.CallFrame, +) JSValue { + _ = callFrame; + _ = globalObject; + return .undefined; +} /// This function is called on the main thread /// The bunVM() call will assert this @@ -485,7 +506,6 @@ pub const VirtualMachine = struct { is_us_loop_entered: bool = false, pending_internal_promise: *JSC.JSInternalPromise = undefined, auto_install_dependencies: bool = false, - load_builtins_from_path: []const u8 = "", onUnhandledRejection: *const OnUnhandledRejection = defaultOnUnhandledRejection, onUnhandledRejectionCtx: ?*anyopaque = null, @@ -500,6 +520,7 @@ pub const VirtualMachine = struct { gc_controller: JSC.GarbageCollectionController = .{}, worker: ?*JSC.WebWorker = null, + ipc: ?*IPCInstance = null, debugger: ?Debugger = null, has_started_debugger: bool = false, @@ -643,12 +664,15 @@ pub const VirtualMachine = struct { pub fn loadExtraEnv(this: *VirtualMachine) void { var map = this.bundler.env.map; - if (map.get("BUN_SHOW_BUN_STACKFRAMES") != null) + if (map.get("BUN_SHOW_BUN_STACKFRAMES") != null) { this.hide_bun_stackframes = false; + } - if (map.get("BUN_OVERRIDE_MODULE_PATH")) |override_path| { - if (override_path.len > 0) { - this.load_builtins_from_path = override_path; + if (map.map.fetchSwapRemove("BUN_INTERNAL_IPC_FD")) |kv| { + if (std.fmt.parseInt(i32, kv.value, 10) catch null) |fd| { + this.initIPCInstance(fd); + } else { + Output.printErrorln("Failed to parse BUN_INTERNAL_IPC_FD", .{}); } } @@ -2712,6 +2736,69 @@ pub const VirtualMachine = struct { } } + extern fn Process__emitMessageEvent(global: *JSGlobalObject, value: JSValue) void; + extern fn Process__emitDisconnectEvent(global: *JSGlobalObject) void; + + pub const IPCInstance = struct { + globalThis: ?*JSGlobalObject, + socket: IPC.Socket, + uws_context: *uws.SocketContext, + ipc_buffer: bun.ByteList, + + pub fn handleIPCMessage( + this: *IPCInstance, + message: IPC.DecodedIPCMessage, + ) void { + switch (message) { + // In future versions we can read this in order to detect version mismatches, + // or disable future optimizations if the subprocess is old. + .version => |v| { + IPC.log("Parent IPC version is {d}", .{v}); + }, + .data => |data| { + IPC.log("Received IPC message from parent", .{}); + if (this.globalThis) |global| { + Process__emitMessageEvent(global, data); + } + }, + } + } + + pub fn handleIPCClose(this: *IPCInstance, _: IPC.Socket) void { + if (this.globalThis) |global| { + var vm = global.bunVM(); + vm.ipc = null; + Process__emitDisconnectEvent(global); + } + uws.us_socket_context_free(0, this.uws_context); + bun.default_allocator.destroy(this); + } + + pub const Handlers = IPC.NewIPCHandler(IPCInstance); + }; + + pub fn initIPCInstance(this: *VirtualMachine, fd: i32) void { + this.event_loop.ensureWaker(); + const context = uws.us_create_socket_context(0, this.event_loop_handle.?, @sizeOf(usize), .{}).?; + IPC.Socket.configure(context, true, *IPCInstance, IPCInstance.Handlers); + + const socket = uws.newSocketFromFd(context, @sizeOf(*IPCInstance), fd) orelse { + uws.us_socket_context_free(0, context); + Output.prettyWarnln("Failed to initialize IPC connection to parent", .{}); + return; + }; + + var instance = bun.default_allocator.create(IPCInstance) catch @panic("OOM"); + instance.* = .{ + .globalThis = this.global, + .socket = socket, + .uws_context = context, + .ipc_buffer = bun.ByteList{}, + }; + var ptr = socket.ext(*IPCInstance); + ptr.?.* = instance; + this.ipc = instance; + } comptime { if (!JSC.is_bindgen) _ = Bun__remapStackFramePositions; diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 0c3313a46..f5e5cde72 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -2282,67 +2282,69 @@ pub const HardcodedModule = enum { pub const Map = bun.ComptimeStringMap( HardcodedModule, .{ - .{ "buffer", HardcodedModule.@"node:buffer" }, .{ "bun", HardcodedModule.bun }, .{ "bun:ffi", HardcodedModule.@"bun:ffi" }, .{ "bun:jsc", HardcodedModule.@"bun:jsc" }, .{ "bun:main", HardcodedModule.@"bun:main" }, .{ "bun:sqlite", HardcodedModule.@"bun:sqlite" }, .{ "detect-libc", HardcodedModule.@"detect-libc" }, - .{ "node:assert", HardcodedModule.@"node:assert" }, - .{ "node:assert/strict", HardcodedModule.@"node:assert/strict" }, - .{ "node:async_hooks", HardcodedModule.@"node:async_hooks" }, - .{ "node:buffer", HardcodedModule.@"node:buffer" }, - .{ "node:child_process", HardcodedModule.@"node:child_process" }, - .{ "node:cluster", HardcodedModule.@"node:cluster" }, - .{ "node:console", HardcodedModule.@"node:console" }, - .{ "node:constants", HardcodedModule.@"node:constants" }, - .{ "node:crypto", HardcodedModule.@"node:crypto" }, - .{ "node:dgram", HardcodedModule.@"node:dgram" }, - .{ "node:diagnostics_channel", HardcodedModule.@"node:diagnostics_channel" }, - .{ "node:dns", HardcodedModule.@"node:dns" }, - .{ "node:dns/promises", HardcodedModule.@"node:dns/promises" }, - .{ "node:domain", HardcodedModule.@"node:domain" }, - .{ "node:events", HardcodedModule.@"node:events" }, - .{ "node:fs", HardcodedModule.@"node:fs" }, - .{ "node:fs/promises", HardcodedModule.@"node:fs/promises" }, - .{ "node:http", HardcodedModule.@"node:http" }, - .{ "node:http2", HardcodedModule.@"node:http2" }, - .{ "node:https", HardcodedModule.@"node:https" }, - .{ "node:inspector", HardcodedModule.@"node:inspector" }, - .{ "node:module", HardcodedModule.@"node:module" }, - .{ "node:net", HardcodedModule.@"node:net" }, - .{ "node:os", HardcodedModule.@"node:os" }, - .{ "node:path", HardcodedModule.@"node:path" }, - .{ "node:path/posix", HardcodedModule.@"node:path/posix" }, - .{ "node:path/win32", HardcodedModule.@"node:path/win32" }, - .{ "node:punycode", HardcodedModule.@"node:punycode" }, - .{ "node:perf_hooks", HardcodedModule.@"node:perf_hooks" }, - .{ "node:process", HardcodedModule.@"node:process" }, - .{ "node:querystring", HardcodedModule.@"node:querystring" }, .{ "node-fetch", HardcodedModule.@"node-fetch" }, .{ "isomorphic-fetch", HardcodedModule.@"isomorphic-fetch" }, + + .{ "assert", HardcodedModule.@"node:assert" }, + .{ "assert/strict", HardcodedModule.@"node:assert/strict" }, + .{ "async_hooks", HardcodedModule.@"node:async_hooks" }, + .{ "buffer", HardcodedModule.@"node:buffer" }, + .{ "child_process", HardcodedModule.@"node:child_process" }, + .{ "cluster", HardcodedModule.@"node:cluster" }, + .{ "console", HardcodedModule.@"node:console" }, + .{ "constants", HardcodedModule.@"node:constants" }, + .{ "crypto", HardcodedModule.@"node:crypto" }, + .{ "dgram", HardcodedModule.@"node:dgram" }, + .{ "diagnostics_channel", HardcodedModule.@"node:diagnostics_channel" }, + .{ "dns", HardcodedModule.@"node:dns" }, + .{ "dns/promises", HardcodedModule.@"node:dns/promises" }, + .{ "domain", HardcodedModule.@"node:domain" }, + .{ "events", HardcodedModule.@"node:events" }, + .{ "fs", HardcodedModule.@"node:fs" }, + .{ "fs/promises", HardcodedModule.@"node:fs/promises" }, + .{ "http", HardcodedModule.@"node:http" }, + .{ "http2", HardcodedModule.@"node:http2" }, + .{ "https", HardcodedModule.@"node:https" }, + .{ "inspector", HardcodedModule.@"node:inspector" }, + .{ "module", HardcodedModule.@"node:module" }, + .{ "net", HardcodedModule.@"node:net" }, + .{ "os", HardcodedModule.@"node:os" }, + .{ "path", HardcodedModule.@"node:path" }, + .{ "path/posix", HardcodedModule.@"node:path/posix" }, + .{ "path/win32", HardcodedModule.@"node:path/win32" }, + .{ "punycode", HardcodedModule.@"node:punycode" }, + .{ "perf_hooks", HardcodedModule.@"node:perf_hooks" }, + .{ "process", HardcodedModule.@"node:process" }, + .{ "querystring", HardcodedModule.@"node:querystring" }, .{ "node:readline", HardcodedModule.@"node:readline" }, - .{ "node:readline/promises", HardcodedModule.@"node:readline/promises" }, - .{ "node:repl", HardcodedModule.@"node:repl" }, - .{ "node:stream", HardcodedModule.@"node:stream" }, - .{ "node:stream/consumers", HardcodedModule.@"node:stream/consumers" }, - .{ "node:stream/promises", HardcodedModule.@"node:stream/promises" }, - .{ "node:stream/web", HardcodedModule.@"node:stream/web" }, - .{ "node:string_decoder", HardcodedModule.@"node:string_decoder" }, - .{ "node:timers", HardcodedModule.@"node:timers" }, - .{ "node:timers/promises", HardcodedModule.@"node:timers/promises" }, - .{ "node:tls", HardcodedModule.@"node:tls" }, - .{ "node:trace_events", HardcodedModule.@"node:trace_events" }, - .{ "node:tty", HardcodedModule.@"node:tty" }, - .{ "node:url", HardcodedModule.@"node:url" }, - .{ "node:util", HardcodedModule.@"node:util" }, - .{ "node:util/types", HardcodedModule.@"node:util/types" }, - .{ "node:v8", HardcodedModule.@"node:v8" }, - .{ "node:vm", HardcodedModule.@"node:vm" }, - .{ "node:wasi", HardcodedModule.@"node:wasi" }, - .{ "node:worker_threads", HardcodedModule.@"node:worker_threads" }, - .{ "node:zlib", HardcodedModule.@"node:zlib" }, + .{ "readline", HardcodedModule.@"node:readline" }, + .{ "readline/promises", HardcodedModule.@"node:readline/promises" }, + .{ "repl", HardcodedModule.@"node:repl" }, + .{ "stream", HardcodedModule.@"node:stream" }, + .{ "stream/consumers", HardcodedModule.@"node:stream/consumers" }, + .{ "stream/promises", HardcodedModule.@"node:stream/promises" }, + .{ "stream/web", HardcodedModule.@"node:stream/web" }, + .{ "string_decoder", HardcodedModule.@"node:string_decoder" }, + .{ "timers", HardcodedModule.@"node:timers" }, + .{ "timers/promises", HardcodedModule.@"node:timers/promises" }, + .{ "tls", HardcodedModule.@"node:tls" }, + .{ "trace_events", HardcodedModule.@"node:trace_events" }, + .{ "tty", HardcodedModule.@"node:tty" }, + .{ "url", HardcodedModule.@"node:url" }, + .{ "util", HardcodedModule.@"node:util" }, + .{ "util/types", HardcodedModule.@"node:util/types" }, + .{ "v8", HardcodedModule.@"node:v8" }, + .{ "vm", HardcodedModule.@"node:vm" }, + .{ "wasi", HardcodedModule.@"node:wasi" }, + .{ "worker_threads", HardcodedModule.@"node:worker_threads" }, + .{ "zlib", HardcodedModule.@"node:zlib" }, + .{ "undici", HardcodedModule.undici }, .{ "ws", HardcodedModule.ws }, .{ "@vercel/fetch", HardcodedModule.@"@vercel/fetch" }, @@ -2358,138 +2360,136 @@ pub const HardcodedModule = enum { pub const Aliases = struct { // Used by both Bun and Node. const common_alias_kvs = .{ - .{ "node:assert", .{ .path = "node:assert" } }, - .{ "node:assert/strict", .{ .path = "node:assert/strict" } }, - .{ "node:async_hooks", .{ .path = "node:async_hooks" } }, - .{ "node:buffer", .{ .path = "node:buffer" } }, - .{ "node:child_process", .{ .path = "node:child_process" } }, - .{ "node:cluster", .{ .path = "node:cluster" } }, - .{ "node:console", .{ .path = "node:console" } }, - .{ "node:constants", .{ .path = "node:constants" } }, - .{ "node:crypto", .{ .path = "node:crypto" } }, - .{ "node:dgram", .{ .path = "node:dgram" } }, - .{ "node:diagnostics_channel", .{ .path = "node:diagnostics_channel" } }, - .{ "node:dns", .{ .path = "node:dns" } }, - .{ "node:dns/promises", .{ .path = "node:dns/promises" } }, - .{ "node:domain", .{ .path = "node:domain" } }, - .{ "node:events", .{ .path = "node:events" } }, - .{ "node:fs", .{ .path = "node:fs" } }, - .{ "node:fs/promises", .{ .path = "node:fs/promises" } }, - .{ "node:http", .{ .path = "node:http" } }, - .{ "node:http2", .{ .path = "node:http2" } }, - .{ "node:https", .{ .path = "node:https" } }, - .{ "node:inspector", .{ .path = "node:inspector" } }, - .{ "node:module", .{ .path = "node:module" } }, - .{ "node:net", .{ .path = "node:net" } }, - .{ "node:os", .{ .path = "node:os" } }, - .{ "node:path", .{ .path = "node:path" } }, - .{ "node:path/posix", .{ .path = "node:path/posix" } }, - .{ "node:path/win32", .{ .path = "node:path/win32" } }, - .{ "node:perf_hooks", .{ .path = "node:perf_hooks" } }, - .{ "node:process", .{ .path = "node:process" } }, - .{ "node:punycode", .{ .path = "node:punycode" } }, - .{ "node:querystring", .{ .path = "node:querystring" } }, - .{ "node:readline", .{ .path = "node:readline" } }, - .{ "node:readline/promises", .{ .path = "node:readline/promises" } }, - .{ "node:repl", .{ .path = "node:repl" } }, - .{ "node:stream", .{ .path = "node:stream" } }, - .{ "node:stream/consumers", .{ .path = "node:stream/consumers" } }, - .{ "node:stream/promises", .{ .path = "node:stream/promises" } }, - .{ "node:stream/web", .{ .path = "node:stream/web" } }, - .{ "node:string_decoder", .{ .path = "node:string_decoder" } }, - .{ "node:timers", .{ .path = "node:timers" } }, - .{ "node:timers/promises", .{ .path = "node:timers/promises" } }, - .{ "node:tls", .{ .path = "node:tls" } }, - .{ "node:trace_events", .{ .path = "node:trace_events" } }, - .{ "node:tty", .{ .path = "node:tty" } }, - .{ "node:url", .{ .path = "node:url" } }, - .{ "node:util", .{ .path = "node:util" } }, - .{ "node:util/types", .{ .path = "node:util/types" } }, - .{ "node:v8", .{ .path = "node:v8" } }, - .{ "node:vm", .{ .path = "node:vm" } }, - .{ "node:wasi", .{ .path = "node:wasi" } }, - .{ "node:worker_threads", .{ .path = "node:worker_threads" } }, - .{ "node:zlib", .{ .path = "node:zlib" } }, - - .{ "assert", .{ .path = "node:assert" } }, - .{ "assert/strict", .{ .path = "node:assert/strict" } }, - .{ "async_hooks", .{ .path = "node:async_hooks" } }, - .{ "buffer", .{ .path = "node:buffer" } }, - .{ "child_process", .{ .path = "node:child_process" } }, - .{ "cluster", .{ .path = "node:cluster" } }, - .{ "console", .{ .path = "node:console" } }, - .{ "constants", .{ .path = "node:constants" } }, - .{ "crypto", .{ .path = "node:crypto" } }, - .{ "dgram", .{ .path = "node:dgram" } }, - .{ "diagnostics_channel", .{ .path = "node:diagnostics_channel" } }, - .{ "dns", .{ .path = "node:dns" } }, - .{ "dns/promises", .{ .path = "node:dns/promises" } }, - .{ "domain", .{ .path = "node:domain" } }, - .{ "events", .{ .path = "node:events" } }, - .{ "fs", .{ .path = "node:fs" } }, - .{ "fs/promises", .{ .path = "node:fs/promises" } }, - .{ "http", .{ .path = "node:http" } }, - .{ "http2", .{ .path = "node:http2" } }, - .{ "https", .{ .path = "node:https" } }, - .{ "inspector", .{ .path = "node:inspector" } }, - .{ "module", .{ .path = "node:module" } }, - .{ "net", .{ .path = "node:net" } }, - .{ "os", .{ .path = "node:os" } }, - .{ "path", .{ .path = "node:path" } }, - .{ "path/posix", .{ .path = "node:path/posix" } }, - .{ "path/win32", .{ .path = "node:path/win32" } }, - .{ "perf_hooks", .{ .path = "node:perf_hooks" } }, - .{ "process", .{ .path = "node:process" } }, - .{ "punycode", .{ .path = "node:punycode" } }, - .{ "querystring", .{ .path = "node:querystring" } }, - .{ "readline", .{ .path = "node:readline" } }, - .{ "readline/promises", .{ .path = "node:readline/promises" } }, - .{ "repl", .{ .path = "node:repl" } }, - .{ "stream", .{ .path = "node:stream" } }, - .{ "stream/consumers", .{ .path = "node:stream/consumers" } }, - .{ "stream/promises", .{ .path = "node:stream/promises" } }, - .{ "stream/web", .{ .path = "node:stream/web" } }, - .{ "string_decoder", .{ .path = "node:string_decoder" } }, - .{ "timers", .{ .path = "node:timers" } }, - .{ "timers/promises", .{ .path = "node:timers/promises" } }, - .{ "tls", .{ .path = "node:tls" } }, - .{ "trace_events", .{ .path = "node:trace_events" } }, - .{ "tty", .{ .path = "node:tty" } }, - .{ "url", .{ .path = "node:url" } }, - .{ "util", .{ .path = "node:util" } }, - .{ "util/types", .{ .path = "node:util/types" } }, - .{ "v8", .{ .path = "node:v8" } }, - .{ "vm", .{ .path = "node:vm" } }, - .{ "wasi", .{ .path = "node:wasi" } }, - .{ "worker_threads", .{ .path = "node:worker_threads" } }, - .{ "zlib", .{ .path = "node:zlib" } }, + .{ "node:assert", .{ .path = "assert" } }, + .{ "node:assert/strict", .{ .path = "assert/strict" } }, + .{ "node:async_hooks", .{ .path = "async_hooks" } }, + .{ "node:buffer", .{ .path = "buffer" } }, + .{ "node:child_process", .{ .path = "child_process" } }, + .{ "node:cluster", .{ .path = "cluster" } }, + .{ "node:console", .{ .path = "console" } }, + .{ "node:constants", .{ .path = "constants" } }, + .{ "node:crypto", .{ .path = "crypto" } }, + .{ "node:dgram", .{ .path = "dgram" } }, + .{ "node:diagnostics_channel", .{ .path = "diagnostics_channel" } }, + .{ "node:dns", .{ .path = "dns" } }, + .{ "node:dns/promises", .{ .path = "dns/promises" } }, + .{ "node:domain", .{ .path = "domain" } }, + .{ "node:events", .{ .path = "events" } }, + .{ "node:fs", .{ .path = "fs" } }, + .{ "node:fs/promises", .{ .path = "fs/promises" } }, + .{ "node:http", .{ .path = "http" } }, + .{ "node:http2", .{ .path = "http2" } }, + .{ "node:https", .{ .path = "https" } }, + .{ "node:inspector", .{ .path = "inspector" } }, + .{ "node:module", .{ .path = "module" } }, + .{ "node:net", .{ .path = "net" } }, + .{ "node:os", .{ .path = "os" } }, + .{ "node:path", .{ .path = "path" } }, + .{ "node:path/posix", .{ .path = "path/posix" } }, + .{ "node:path/win32", .{ .path = "path/win32" } }, + .{ "node:perf_hooks", .{ .path = "perf_hooks" } }, + .{ "node:process", .{ .path = "process" } }, + .{ "node:punycode", .{ .path = "punycode" } }, + .{ "node:querystring", .{ .path = "querystring" } }, + .{ "node:readline", .{ .path = "readline" } }, + .{ "node:readline/promises", .{ .path = "readline/promises" } }, + .{ "node:repl", .{ .path = "repl" } }, + .{ "node:stream", .{ .path = "stream" } }, + .{ "node:stream/consumers", .{ .path = "stream/consumers" } }, + .{ "node:stream/promises", .{ .path = "stream/promises" } }, + .{ "node:stream/web", .{ .path = "stream/web" } }, + .{ "node:string_decoder", .{ .path = "string_decoder" } }, + .{ "node:timers", .{ .path = "timers" } }, + .{ "node:timers/promises", .{ .path = "timers/promises" } }, + .{ "node:tls", .{ .path = "tls" } }, + .{ "node:trace_events", .{ .path = "trace_events" } }, + .{ "node:tty", .{ .path = "tty" } }, + .{ "node:url", .{ .path = "url" } }, + .{ "node:util", .{ .path = "util" } }, + .{ "node:util/types", .{ .path = "util/types" } }, + .{ "node:v8", .{ .path = "v8" } }, + .{ "node:vm", .{ .path = "vm" } }, + .{ "node:wasi", .{ .path = "wasi" } }, + .{ "node:worker_threads", .{ .path = "worker_threads" } }, + .{ "node:zlib", .{ .path = "zlib" } }, + + .{ "assert", .{ .path = "assert" } }, + .{ "assert/strict", .{ .path = "assert/strict" } }, + .{ "async_hooks", .{ .path = "async_hooks" } }, + .{ "buffer", .{ .path = "buffer" } }, + .{ "child_process", .{ .path = "child_process" } }, + .{ "cluster", .{ .path = "cluster" } }, + .{ "console", .{ .path = "console" } }, + .{ "constants", .{ .path = "constants" } }, + .{ "crypto", .{ .path = "crypto" } }, + .{ "dgram", .{ .path = "dgram" } }, + .{ "diagnostics_channel", .{ .path = "diagnostics_channel" } }, + .{ "dns", .{ .path = "dns" } }, + .{ "dns/promises", .{ .path = "dns/promises" } }, + .{ "domain", .{ .path = "domain" } }, + .{ "events", .{ .path = "events" } }, + .{ "fs", .{ .path = "fs" } }, + .{ "fs/promises", .{ .path = "fs/promises" } }, + .{ "http", .{ .path = "http" } }, + .{ "http2", .{ .path = "http2" } }, + .{ "https", .{ .path = "https" } }, + .{ "inspector", .{ .path = "inspector" } }, + .{ "module", .{ .path = "module" } }, + .{ "net", .{ .path = "net" } }, + .{ "os", .{ .path = "os" } }, + .{ "path", .{ .path = "path" } }, + .{ "path/posix", .{ .path = "path/posix" } }, + .{ "path/win32", .{ .path = "path/win32" } }, + .{ "perf_hooks", .{ .path = "perf_hooks" } }, + .{ "process", .{ .path = "process" } }, + .{ "punycode", .{ .path = "punycode" } }, + .{ "querystring", .{ .path = "querystring" } }, + .{ "readline", .{ .path = "readline" } }, + .{ "readline/promises", .{ .path = "readline/promises" } }, + .{ "repl", .{ .path = "repl" } }, + .{ "stream", .{ .path = "stream" } }, + .{ "stream/consumers", .{ .path = "stream/consumers" } }, + .{ "stream/promises", .{ .path = "stream/promises" } }, + .{ "stream/web", .{ .path = "stream/web" } }, + .{ "string_decoder", .{ .path = "string_decoder" } }, + .{ "timers", .{ .path = "timers" } }, + .{ "timers/promises", .{ .path = "timers/promises" } }, + .{ "tls", .{ .path = "tls" } }, + .{ "trace_events", .{ .path = "trace_events" } }, + .{ "tty", .{ .path = "tty" } }, + .{ "url", .{ .path = "url" } }, + .{ "util", .{ .path = "util" } }, + .{ "util/types", .{ .path = "util/types" } }, + .{ "v8", .{ .path = "v8" } }, + .{ "vm", .{ .path = "vm" } }, + .{ "wasi", .{ .path = "wasi" } }, + .{ "worker_threads", .{ .path = "worker_threads" } }, + .{ "zlib", .{ .path = "zlib" } }, // It implements the same interface - .{ "sys", .{ .path = "node:util" } }, - .{ "node:sys", .{ .path = "node:util" } }, + .{ "sys", .{ .path = "util" } }, + .{ "node:sys", .{ .path = "util" } }, // These are returned in builtinModules, but probably not many packages use them // so we will just alias them. - .{ "_http_agent", .{ .path = "node:http" } }, - .{ "_http_client", .{ .path = "node:http" } }, - .{ "_http_common", .{ .path = "node:http" } }, - .{ "_http_incoming", .{ .path = "node:http" } }, - .{ "_http_outgoing", .{ .path = "node:http" } }, - .{ "_http_server", .{ .path = "node:http" } }, - .{ "_stream_duplex", .{ .path = "node:stream" } }, - .{ "_stream_passthrough", .{ .path = "node:stream" } }, - .{ "_stream_readable", .{ .path = "node:stream" } }, - .{ "_stream_transform", .{ .path = "node:stream" } }, - .{ "_stream_writable", .{ .path = "node:stream" } }, - .{ "_stream_wrap", .{ .path = "node:stream" } }, - .{ "_tls_wrap", .{ .path = "node:tls" } }, - .{ "_tls_common", .{ .path = "node:tls" } }, - - // Older versions of `readable-stream` is incompatible with latest - // version of Node.js Stream API, which `bun` implements - // .{ "readable-stream", .{ .path = "node:stream" } }, - // .{ "readable-stream/consumer", .{ .path = "node:stream/consumers" } }, - // .{ "readable-stream/web", .{ .path = "node:stream/web" } }, + .{ "_http_agent", .{ .path = "http" } }, + .{ "_http_client", .{ .path = "http" } }, + .{ "_http_common", .{ .path = "http" } }, + .{ "_http_incoming", .{ .path = "http" } }, + .{ "_http_outgoing", .{ .path = "http" } }, + .{ "_http_server", .{ .path = "http" } }, + .{ "_stream_duplex", .{ .path = "stream" } }, + .{ "_stream_passthrough", .{ .path = "stream" } }, + .{ "_stream_readable", .{ .path = "stream" } }, + .{ "_stream_transform", .{ .path = "stream" } }, + .{ "_stream_writable", .{ .path = "stream" } }, + .{ "_stream_wrap", .{ .path = "stream" } }, + .{ "_tls_wrap", .{ .path = "tls" } }, + .{ "_tls_common", .{ .path = "tls" } }, + + .{ "next/dist/compiled/ws", .{ .path = "ws" } }, + .{ "next/dist/compiled/node-fetch", .{ .path = "node-fetch" } }, + .{ "next/dist/compiled/undici", .{ .path = "undici" } }, }; const bun_extra_alias_kvs = .{ @@ -2511,13 +2511,13 @@ pub const HardcodedModule = enum { .{ "ws", .{ .path = "ws" } }, .{ "ws/lib/websocket", .{ .path = "ws" } }, - .{ "inspector/promises", .{ .path = "node:inspector" } }, - .{ "node:inspector/promises", .{ .path = "node:inspector" } }, + .{ "inspector/promises", .{ .path = "inspector" } }, + .{ "node:inspector/promises", .{ .path = "inspector" } }, }; const node_alias_kvs = .{ - .{ "inspector/promises", .{ .path = "node:inspector/promises" } }, - .{ "node:inspector/promises", .{ .path = "node:inspector/promises" } }, + .{ "inspector/promises", .{ .path = "inspector/promises" } }, + .{ "node:inspector/promises", .{ .path = "inspector/promises" } }, .{ "node:test", .{ .path = "node:test" } }, }; diff --git a/src/bun.js/modules/NodeModuleModule.h b/src/bun.js/modules/NodeModuleModule.h index 7c8f6ee14..b3c34eb5e 100644 --- a/src/bun.js/modules/NodeModuleModule.h +++ b/src/bun.js/modules/NodeModuleModule.h @@ -270,6 +270,40 @@ template <std::size_t N, class T> consteval std::size_t countof(T (&)[N]) { return N; } +JSC_DEFINE_CUSTOM_GETTER(get_resolveFilename, (JSGlobalObject * globalObject, + EncodedJSValue thisValue, + PropertyName propertyName)) { + auto override = static_cast<Zig::GlobalObject *>(globalObject) + ->m_nodeModuleOverriddenResolveFilename.get(); + if (override) { + return JSValue::encode(override); + } + // Instead of storing the original function on the global object and have + // those extra bytes, just have it be a property alias. + JSObject *thisObject = JSValue::decode(thisValue).getObject(); + if (!thisObject) + return JSValue::encode(jsUndefined()); + auto &vm = globalObject->vm(); + return JSValue::encode(thisObject->getDirect( + vm, Identifier::fromString(vm, "__resolveFilename"_s))); +} + +JSC_DEFINE_CUSTOM_SETTER(set_resolveFilename, + (JSGlobalObject * globalObject, + EncodedJSValue thisValue, EncodedJSValue value, + PropertyName propertyName)) { + auto valueJS = JSValue::decode(value); + if (valueJS.isCell()) { + if (auto fn = jsDynamicCast<JSFunction *>(valueJS.asCell())) { + static_cast<Zig::GlobalObject *>(globalObject) + ->m_nodeModuleOverriddenResolveFilename.set(globalObject->vm(), + globalObject, fn); + return true; + } + } + return false; +} + namespace Zig { DEFINE_NATIVE_MODULE(NodeModule) { @@ -303,6 +337,14 @@ DEFINE_NATIVE_MODULE(NodeModule) { put(Identifier::fromString(vm, "Module"_s), defaultObject); + defaultObject->putDirectCustomAccessor( + vm, JSC::Identifier::fromString(vm, "_resolveFilename"_s), + JSC::CustomGetterSetter::create(vm, get_resolveFilename, + set_resolveFilename), + JSC::PropertyAttribute::CustomAccessor | 0); + putNativeFn(Identifier::fromString(vm, "__resolveFilename"_s), + jsFunctionResolveFileName); + putNativeFn(Identifier::fromString(vm, "createRequire"_s), jsFunctionNodeModuleCreateRequire); putNativeFn(Identifier::fromString(vm, "paths"_s), @@ -314,8 +356,6 @@ DEFINE_NATIVE_MODULE(NodeModule) { putNativeFn(Identifier::fromString(vm, "SourceMap"_s), jsFunctionSourceMap); putNativeFn(Identifier::fromString(vm, "isBuiltin"_s), jsFunctionIsBuiltinModule); - putNativeFn(Identifier::fromString(vm, "_resolveFilename"_s), - jsFunctionResolveFileName); putNativeFn(Identifier::fromString(vm, "_nodeModulePaths"_s), Resolver__nodeModulePathsForJS); putNativeFn(Identifier::fromString(vm, "wrap"_s), jsFunctionWrap); diff --git a/src/bun.js/node/node_fs_watcher.zig b/src/bun.js/node/node_fs_watcher.zig index d6d2f8a2f..ccb1a987f 100644 --- a/src/bun.js/node/node_fs_watcher.zig +++ b/src/bun.js/node/node_fs_watcher.zig @@ -351,11 +351,11 @@ pub const FSWatcher = struct { // already aborted? if (s.aborted()) { // safely abort next tick - var current_task: FSWatchTask = .{ + this.current_task = .{ .ctx = this, }; - current_task.append("", .abort, false); - current_task.enqueue(); + this.current_task.append("", .abort, false); + this.current_task.enqueue(); } else { // watch for abortion this.signal = s.listen(FSWatcher, this, FSWatcher.emitAbort); @@ -587,7 +587,10 @@ pub const FSWatcher = struct { errdefer ctx.deinit(); - ctx.path_watcher = try PathWatcher.watch(vm, file_path_z, args.recursive, onPathUpdate, onUpdateEnd, bun.cast(*anyopaque, ctx)); + ctx.path_watcher = if (args.signal == null or !args.signal.?.aborted()) + try PathWatcher.watch(vm, file_path_z, args.recursive, onPathUpdate, onUpdateEnd, bun.cast(*anyopaque, ctx)) + else + null; ctx.initJS(args.listener); return ctx; } diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 328b71b60..51af22052 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -2480,7 +2480,7 @@ pub const Process = struct { } vm.onExit(); - std.os.exit(code); + bun.Global.exit(code); } pub export const Bun__version: [*:0]const u8 = "v" ++ bun.Global.package_json_version; diff --git a/src/bun.js/rare_data.zig b/src/bun.js/rare_data.zig index 706826033..44e482049 100644 --- a/src/bun.js/rare_data.zig +++ b/src/bun.js/rare_data.zig @@ -11,6 +11,8 @@ const bun = @import("root").bun; const WebSocketClientMask = @import("../http/websocket_http_client.zig").Mask; const UUID = @import("./uuid.zig"); const StatWatcherScheduler = @import("./node/node_fs_stat_watcher.zig").StatWatcherScheduler; +const IPC = @import("./ipc.zig"); +const uws = @import("root").bun.uws; boring_ssl_engine: ?*BoringSSL.ENGINE = null, editor_context: EditorContext = EditorContext{}, @@ -31,6 +33,8 @@ file_polls_: ?*JSC.FilePoll.Store = null, global_dns_data: ?*JSC.DNS.GlobalData = null, +spawn_ipc_usockets_context: ?*uws.SocketContext = null, + mime_types: ?bun.HTTP.MimeType.Map = null, node_fs_stat_watcher_scheduler: ?*StatWatcherScheduler = null, @@ -310,6 +314,20 @@ pub fn stdin(rare: *RareData) *Blob.Store { }; } +const Subprocess = @import("./api/bun/subprocess.zig").Subprocess; + +pub fn spawnIPCContext(rare: *RareData, vm: *JSC.VirtualMachine) *uws.SocketContext { + if (rare.spawn_ipc_usockets_context) |ctx| { + return ctx; + } + + var opts: uws.us_socket_context_options_t = .{}; + const ctx = uws.us_create_socket_context(0, vm.event_loop_handle.?, @sizeOf(usize), opts).?; + IPC.Socket.configure(ctx, true, *Subprocess, Subprocess.IPCHandler); + rare.spawn_ipc_usockets_context = ctx; + return ctx; +} + pub fn globalDNSResolver(rare: *RareData, vm: *JSC.VirtualMachine) *JSC.DNS.DNSResolver { if (rare.global_dns_data == null) { rare.global_dns_data = JSC.DNS.GlobalData.init(vm.allocator, vm); |