From b4eb9ed3ff43c6d8aaad548e395feb554685e3ca Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Mon, 17 Feb 2025 17:59:34 -0800 Subject: [PATCH] feat(node/net): add SocketAddress (in C++) --- src/bun.js/bindings/JSSocketAddress.cpp | 238 ++++++++++++++++-- src/bun.js/bindings/JSSocketAddress.h | 119 ++++++++- .../bindings/JSSocketAddressConstructor.cpp | 105 ++++++++ .../bindings/JSSocketAddressConstructor.h | 49 ++++ .../bindings/JSSocketAddressPrototype.cpp | 13 + .../bindings/JSSocketAddressPrototype.h | 42 ++++ src/bun.js/bindings/NodeValidator.cpp | 22 +- src/bun.js/bindings/NodeValidator.h | 1 + src/bun.js/bindings/ZigGlobalObject.cpp | 20 +- src/bun.js/bindings/ZigGlobalObject.h | 8 +- .../bindings/webcore/DOMClientIsoSubspaces.h | 1 + src/bun.js/bindings/webcore/DOMIsoSubspaces.h | 1 + src/js/builtins/BunBuiltinNames.h | 1 + 13 files changed, 577 insertions(+), 43 deletions(-) create mode 100644 src/bun.js/bindings/JSSocketAddressConstructor.cpp create mode 100644 src/bun.js/bindings/JSSocketAddressConstructor.h create mode 100644 src/bun.js/bindings/JSSocketAddressPrototype.cpp create mode 100644 src/bun.js/bindings/JSSocketAddressPrototype.h diff --git a/src/bun.js/bindings/JSSocketAddress.cpp b/src/bun.js/bindings/JSSocketAddress.cpp index 1815073397..8ae1dfd6f3 100644 --- a/src/bun.js/bindings/JSSocketAddress.cpp +++ b/src/bun.js/bindings/JSSocketAddress.cpp @@ -1,63 +1,255 @@ -#include "JSSocketAddress.h" -#include "ZigGlobalObject.h" +#include "BunClientData.h" +#include "JavaScriptCore/JSCast.h" #include "JavaScriptCore/JSObjectInlines.h" #include "JavaScriptCore/ObjectConstructor.h" -#include "JavaScriptCore/JSCast.h" +#include "ZigGlobalObject.h" +#include "JavaScriptCore/JSCell.h" +#include "ErrorCode.h" + +#include "JSSocketAddress.h" +#include "JSSocketAddressConstructor.h" +#include "JSSocketAddressPrototype.h" using namespace JSC; namespace Bun { -namespace JSSocketAddress { -// Using a structure with inlined offsets should be more lightweight than a class. -Structure* createStructure(VM& vm, JSGlobalObject* globalObject) +static constexpr PropertyOffset addressOffset = 0; +static constexpr PropertyOffset addressFamilyOffset = 1; +static constexpr PropertyOffset portOffset = 2; +static constexpr PropertyOffset flowLabelOffset = 3; + +inline JSC::JSString* JSSocketAddress::address() const { - JSC::Structure* structure = globalObject->structureCache().emptyObjectStructureForPrototype( + auto value = this->getDirect(addressOffset); + JSC::JSString* str = jsCast(value); + return str; + // return value.getString(globalObject()); +} + +inline uint8_t JSSocketAddress::addressFamily() const +{ + uint32_t af = this->getDirect(addressFamilyOffset).asUInt32(); + ASSERT(af == AF_INET6 || af == AF_INET); + return af; +} + +inline in_port_t JSSocketAddress::port() const +{ + auto port = this->getDirect(portOffset).asUInt32(); + ASSERT(port <= 0xFFFF); + return port; +} + +inline uint32_t JSSocketAddress::flowLabel() const +{ + return this->getDirect(flowLabelOffset).asUInt32(); +} + +// ============================================================================= + +JSSocketAddress* JSSocketAddress::create(JSC::VM& vm, + JSC::JSGlobalObject* globalObject, + JSC::Structure* structure, + JSC::JSString* address, + in_port_t port, + bool isIPv6) +{ + return create(vm, globalObject, structure, address, port, isIPv6 ? AF_INET6 : AF_INET, 0); +} + +JSSocketAddress* JSSocketAddress::create(JSC::VM& vm, + JSC::JSGlobalObject* globalObject, + JSC::Structure* structure, + JSC::JSString* address, + in_port_t port, + uint8_t addressFamily, // AF_INET | AF_INET6 + uint32_t flowLabel) +{ + static const NeverDestroyed IPv4 = MAKE_STATIC_STRING_IMPL("IPv4"); + static const NeverDestroyed IPv6 = MAKE_STATIC_STRING_IMPL("IPv6"); + + auto scope = DECLARE_THROW_SCOPE(vm); + + address_t addr; + + const char* address_bytes = address->value(globalObject)->ascii().data(); + switch (inet_pton(addressFamily, address_bytes, &addr)) { + case 1: // ok + break; + case 0: // invalid address + // node throws ERR_INVALID_ADDRESS + Bun::throwError(globalObject, scope, ErrorCode::ERR_INVALID_IP_ADDRESS, "Invalid address"_s); + return nullptr; + case -1: // syserr + // TODO: how to handle system errors? + Bun::throwError(globalObject, scope, ErrorCode::ERR_INVALID_IP_ADDRESS, "Invalid address"_s); + return nullptr; + default: + __builtin_unreachable(); + } + + auto* af_str = jsString(vm, addressFamily == AF_INET6 ? IPv6 : IPv4); + + JSSocketAddress* ptr = new (NotNull, JSC::allocateCell(vm)) JSSocketAddress(vm, structure); + ptr->m_address = addr; + ptr->finishCreation(vm); + + ptr->putDirectOffset(vm, flowLabelOffset, jsNumber(flowLabel)); + ptr->putDirectOffset(vm, addressOffset, address); + ptr->putDirectOffset(vm, addressFamilyOffset, af_str); + ptr->putDirectOffset(vm, portOffset, jsNumber(port)); + return ptr; +} + +void JSSocketAddress::destroy(JSC::JSCell* cell) +{ + auto* thisObject = jsCast(cell); + thisObject->~JSSocketAddress(); +} + +JSC::GCClient::IsoSubspace* JSSocketAddress::subspaceForImpl(JSC::VM& vm) +{ + // return WebCore::subspaceForImpl( + // vm, + // [](auto& spaces) { return spaces.m_clientSubspaceForJSSocketAddress.get(); }, + // [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSSocketAddress = std::forward(space); }, + // [](auto& spaces) { return spaces.m_subspaceForJSSocketAddress.get(); }, + // [](auto& spaces, auto&& space) { spaces.m_subspaceForJSSocketAddress + // = std::forward(space); }); + return &vm.plainObjectSpace(); +} + +JSC::JSObject* JSSocketAddress::createPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + auto* structure = JSSocketAddressPrototype::createStructure(vm, globalObject, globalObject->objectPrototype()); + structure->setMayBePrototype(true); + return JSSocketAddressPrototype::create(vm, globalObject, structure); +} + +JSC::Structure* JSSocketAddress::createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) +{ + auto* structure = JSC::Structure::create(vm, globalObject, - globalObject->objectPrototype(), - 3); + prototype, + JSC::TypeInfo(JSC::ObjectType, StructureFlags), + info(), + NonArray, + 4); JSC::PropertyOffset offset; + // TODO: add identifiers to CommonIdentifiers? structure = structure->addPropertyTransition( vm, structure, JSC::Identifier::fromString(vm, "address"_s), - 0, + static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete), offset); + ASSERT(offset == addressOffset); structure = structure->addPropertyTransition( vm, structure, JSC::Identifier::fromString(vm, "family"_s), - 0, + static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete), offset); + ASSERT(offset == addressFamilyOffset); structure = structure->addPropertyTransition( vm, structure, JSC::Identifier::fromString(vm, "port"_s), - 0, + static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete), offset); + ASSERT(offset == portOffset); + + structure->addPropertyTransition( + vm, + structure, + JSC::Identifier::fromString(vm, "flowLabel"_s), + static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::DontDelete), + offset); + ASSERT(offset == flowLabelOffset); return structure; } -} // namespace JSSocketAddress +JSSocketAddress::~JSSocketAddress() +{ +} + +void JSSocketAddress::finishCreation(JSC::VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); + // initializeProperties(vm, globalObject, prototype); + // TODO: idk how to get a globalobject here + // this->m_address.initLater([](const LazyProperty::Initializer& init) { + // auto af = init->owner->addressFamily(); + // auto address = init->owner->address(); + // address.value() + // address.value(init->vm.) + // }); + // ASSERT(inherits(info())); + // reifyStaticProperties(vm, JSSocketAddress::info(), + // JSSocketAddressPrototypeTableValues, *this); + // JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +// void JSSocketAddress::initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSObject* prototype) + +const ClassInfo JSSocketAddress::s_info + = { + "SocketAddress"_s, + &Base::s_info, + nullptr, + nullptr, + CREATE_METHOD_TABLE(JSSocketAddress) + }; + +template +void JSSocketAddress::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + JSSocketAddress* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} +DEFINE_VISIT_CHILDREN(JSSocketAddress); + +template +void JSSocketAddress::visitAdditionalChildren(Visitor& visitor) +{ + JSSocketAddress* thisObject = this; + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + // TODO: do properties added via putDirectOffset need visiting? + // visitor.append(thisObject->m_address); +} +DEFINE_VISIT_ADDITIONAL_CHILDREN(JSSocketAddress); + +template +void JSSocketAddress::visitOutputConstraintsImpl(JSCell* cell, Visitor& visitor) +{ + + auto* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitOutputConstraints(thisObject, visitor); + thisObject->visitAdditionalChildren(visitor); +} +DEFINE_VISIT_OUTPUT_CONSTRAINTS(JSSocketAddress); + } // namespace Bun extern "C" JSObject* JSSocketAddress__create(JSGlobalObject* globalObject, JSString* value, int32_t port, bool isIPv6) { - static const NeverDestroyed IPv4 = MAKE_STATIC_STRING_IMPL("IPv4"); - static const NeverDestroyed IPv6 = MAKE_STATIC_STRING_IMPL("IPv6"); - - VM& vm = globalObject->vm(); - auto* global = jsCast(globalObject); + auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSObject* thisObject = constructEmptyObject(vm, global->JSSocketAddressStructure()); - thisObject->putDirectOffset(vm, 0, value); - thisObject->putDirectOffset(vm, 1, isIPv6 ? jsString(vm, IPv6) : jsString(vm, IPv4)); - thisObject->putDirectOffset(vm, 2, jsNumber(port)); + if (UNLIKELY(port < 0 || port > std::numeric_limits::max())) { + throwRangeError(global, scope, "Port out of range"_s); + return nullptr; + } - return thisObject; + return Bun::JSSocketAddress::create(globalObject->vm(), globalObject, global->JSSocketAddressStructure(), value, port, isIPv6 ? AF_INET6 : AF_INET, 0); } diff --git a/src/bun.js/bindings/JSSocketAddress.h b/src/bun.js/bindings/JSSocketAddress.h index 77bdca5d4f..e6eeb5a456 100644 --- a/src/bun.js/bindings/JSSocketAddress.h +++ b/src/bun.js/bindings/JSSocketAddress.h @@ -3,14 +3,127 @@ #include "root.h" #include "JavaScriptCore/JSObjectInlines.h" +extern "C" { +#if OS(WINDOWS) +#include // in_addr - https://learn.microsoft.com/en-us/windows/win32/api/winsock2/ +#include // in6_addr - https://learn.microsoft.com/en-us/windows/win32/api/ws2def/ +#include // inet_ntop, inet_pton - https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/ +#include // AF_INET, AF_INET6 +typedef union address { + struct in_addr ipv4; + struct in6_addr ipv6; +} address_t; +#define in_port_t USHORT +#else +#include // in_addr, in6_addr +#include // inet_pton, inet_ntop +typedef union address { + struct in_addr ipv4; + struct in6_addr ipv6; +} address_t; +#endif +} + using namespace JSC; namespace Bun { -namespace JSSocketAddress { -Structure* createStructure(VM& vm, JSGlobalObject* globalObject); +/// `SocketAddress` is written in Zig +// struct SocketAddress; + +// class JSSocketAddress : public JSC::JSDestructibleObject { +// public: +// using Base = JSC::JSDestructibleObject; +// using DOMWrapped = SocketAddress; +// static J Structure* createStructure(VM& vm, JSGlobalObject* globalObject); + +// }; // class JSSocketAddress + +class JSSocketAddress final : public JSC::JSObject { +public: + using Base = JSC::JSObject; + static constexpr unsigned StructureFlags = Base::StructureFlags; + // static constexpr JSC::DestructionMode needsDestruction = NeedsDestruction; + + /// Native SocketAddress used in/by Zig code. + // SocketAddress* m_sockaddr { nullptr }; + // SocketAddress* m_address + // uint8_t m_address[16]; + // LazyProperty m_address; + address_t m_address; + JSC::JSString* address() const; + uint8_t addressFamily() const; + in_port_t port() const; + uint32_t flowLabel() const; + + /// Returns `nullptr` if the address is invalid. A js exception will be thrown. + static JSSocketAddress* create(JSC::VM& vm, + JSC::JSGlobalObject* globalObject, + JSC::Structure* structure, + JSC::JSString* address, + in_port_t port, + bool isIPv6); + + /// Returns `nullptr` if the address is invalid. A js exception will be thrown. + static JSSocketAddress* create(JSC::VM& vm, + JSC::JSGlobalObject* globalObject, + JSC::Structure* structure, + JSC::JSString* address, + in_port_t port, + uint8_t addressFamily, // AF_INET | AF_INET6 + uint32_t flowLabel); + + static void destroy(JSC::JSCell*); + + template 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 JSObject* createPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject); + // static JSObject* createConstructor(JSC::VM& vm, JSC::JSGlobalObject* globalObject); + 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()); + // } + + // void detach() + // { + // this->sockaddr + // } + + // static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&); + // static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSSocketAddress, m_ctx); } + + // /** + // * Estimated size of the object from Zig including the JS wrapper. + // */ + // static size_t estimatedSize(JSC::JSCell* cell, JSC::VM& vm); + + // /** + // * Memory cost of the object from Zig, without necessarily having a JS wrapper alive. + // */ + // static size_t memoryCost(void* ptr); + + JSSocketAddress(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } + ~JSSocketAddress(); + + void finishCreation(JSC::VM&); + + DECLARE_EXPORT_INFO; + + DECLARE_VISIT_CHILDREN; + template void visitAdditionalChildren(Visitor&); + DECLARE_VISIT_OUTPUT_CONSTRAINTS; + +}; // class JSSocketAddress -} // namespace JSSocketAddress } // namespace Bun extern "C" JSObject* JSSocketAddress__create(JSGlobalObject* globalObject, JSString* value, int port, bool isIPv6); diff --git a/src/bun.js/bindings/JSSocketAddressConstructor.cpp b/src/bun.js/bindings/JSSocketAddressConstructor.cpp new file mode 100644 index 0000000000..868e2eaf3a --- /dev/null +++ b/src/bun.js/bindings/JSSocketAddressConstructor.cpp @@ -0,0 +1,105 @@ +#include "JSSocketAddressConstructor.h" +#include "JSSocketAddress.h" +#include "JavaScriptCore/Lookup.h" +#include "NodeValidator.h" +#include "ZigGlobalObject.h" + +using namespace JSC; +namespace Bun { + +const ClassInfo JSSocketAddressConstructor::s_info = { + "SocketAddressConstructor"_s, + &Base::s_info, + nullptr, + nullptr, + CREATE_METHOD_TABLE(JSSocketAddressConstructor) +}; +// todo +// static const JSSocketAddressConstructorTableValues[] = { +// { "isSocketAddress"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsScketAddressConstructorFunction_isSocketAddress, 1 }, +// { "parse"_s, static_cast(JSC::PropertyAttribute::Function), JSC::NoIntrinsic, { JSC::HashTableValue::NativeFunctionType, jsScketAddressConstructorFunction_parse, 1 } }, +// }; + +// void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* global, JSSocketAddressPrototype* prototype) +// { +// } + +JSSocketAddressConstructor* JSSocketAddressConstructor::create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) +{ + JSSocketAddressConstructor* ptr = new (NotNull, JSC::allocateCell(vm)) JSSocketAddressConstructor(vm, structure); + ptr->finishCreation(vm); + return ptr; +} + +// new SocketAddress(AF, address, port?, flowLabel?) +JSC::EncodedJSValue JSSocketAddressConstructor::construct(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) +{ + Zig::GlobalObject* global = reinterpret_cast(globalObject); + static const NeverDestroyed port_name = MAKE_STATIC_STRING_IMPL("port"); + auto& vm = global->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue af_arg = callFrame->argument(0); + JSValue address_arg = callFrame->argument(1); + JSValue port_arg = callFrame->argument(2); + JSValue flowLabel_arg = callFrame->argument(3); + + // addressFamily + V::validateUint32(scope, global, af_arg, "addressFamily"_s, jsBoolean(false)); + RETURN_IF_EXCEPTION(scope, {}); + uint32_t af = af_arg.toUInt32(global); + if (UNLIKELY(af != AF_INET && af != AF_INET6)) { + throwTypeError(global, scope, "Invalid address family"_s); + return encodedJSUndefined(); + } + + // address + V::validateString(scope, global, address_arg, "address"_s); + RETURN_IF_EXCEPTION(scope, encodedJSUndefined()); + JSC::JSString* address = jsCast(address_arg); + + // port + in_port_t port = 0; + if (LIKELY(!port_arg.isUndefined())) { + V::validatePort(scope, global, port_arg, jsString(vm, port_name), true); + RETURN_IF_EXCEPTION(scope, encodedJSUndefined()); + uint32_t port32 = port_arg.toUInt32(global); + ASSERT(port32 <= std::numeric_limits().max()); + port = static_cast(port32); + } + + // flowLabel + uint32_t flowLabel = 0; + if (UNLIKELY(!flowLabel_arg.isUndefined())) { + V::validateUint32(scope, global, flowLabel_arg, "flowlabel"_s, jsBoolean(false)); + RETURN_IF_EXCEPTION(scope, encodedJSUndefined()); + flowLabel = flowLabel_arg.toUInt32(global); + } + + auto* structure = global->JSSocketAddressStructure(); + auto* sockaddr = JSSocketAddress::create(vm, global, structure, address, port, af, flowLabel); + // throws if inet_pton fails + RETURN_IF_EXCEPTION(scope, encodedJSUndefined()); + return JSValue::encode(sockaddr); +} + +JSC::EncodedJSValue JSSocketAddressConstructor::call(JSC::JSGlobalObject* global, JSC::CallFrame* callFrame) +{ + auto scope = DECLARE_THROW_SCOPE(global->vm()); + throwTypeError(global, scope, "Cannot construct SocketAddress"_s); + return encodedJSUndefined(); +} + +JSSocketAddressConstructor::JSSocketAddressConstructor(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure, call, construct) +{ +} + +// TODO: reifyStaticProperties +// void JSSocketAddressConstructor::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* global, JSSocketAddressPrototype* prototype) +// { +// Base::finishCreation(vm); +// reifyStaticProperties(vm, JSSocketAddress::info(), JSSocketAddressConstructorTableValues, *this); +// } + +} // namespace Bun diff --git a/src/bun.js/bindings/JSSocketAddressConstructor.h b/src/bun.js/bindings/JSSocketAddressConstructor.h new file mode 100644 index 0000000000..363bbb0351 --- /dev/null +++ b/src/bun.js/bindings/JSSocketAddressConstructor.h @@ -0,0 +1,49 @@ +#pragma once + +#include "root.h" +#include "JSSocketAddressPrototype.h" + +namespace Bun { + +class JSSocketAddressConstructor final : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + static JSSocketAddressConstructor* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure); + + static constexpr unsigned StructureFlags = Base::StructureFlags; + static constexpr JSC::DestructionMode needsDestruction = JSC::DoesNotNeedDestruction; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSObject* prototype) + { + return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::InternalFunctionType, StructureFlags), info()); + } + + template static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return &vm.internalFunctionSpace(); + // TODO: use separate subspace?? + + // return WebCore::subspaceForImpl( + // vm, + // [](auto& spaces) { return spaces.m_clientSubspaceForBunClassConstructor.get(); }, + // [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForBunClassConstructor = std::forward(space); }, + // [](auto& spaces) { return spaces.m_subspaceForBunClassConstructor.get(); }, + // [](auto& spaces, auto&& space) { spaces.m_subspaceForBunClassConstructor = std::forward(space); }); + } + + // void initializeProperties(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSSocketAddressPrototype* prototype); + + // Must be defined for each specialization class. + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES construct(JSC::JSGlobalObject*, JSC::CallFrame*); + static JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES call(JSC::JSGlobalObject*, JSC::CallFrame*); + + DECLARE_EXPORT_INFO; + +protected: + JSSocketAddressConstructor(JSC::VM& vm, JSC::Structure* structure); + DECLARE_DEFAULT_FINISH_CREATION; +}; + +} // namespace Bun diff --git a/src/bun.js/bindings/JSSocketAddressPrototype.cpp b/src/bun.js/bindings/JSSocketAddressPrototype.cpp new file mode 100644 index 0000000000..42fd570a6a --- /dev/null +++ b/src/bun.js/bindings/JSSocketAddressPrototype.cpp @@ -0,0 +1,13 @@ +#include "JSSocketAddressPrototype.h" + +// const ClassInfo JSX509CertificatePrototype::s_info = { "X509Certificate"_s, +// &Base::s_info, nullptr, nullptr, +// CREATE_METHOD_TABLE(JSX509CertificatePrototype) }; + +using namespace JSC; + +namespace Bun { + +const ClassInfo JSSocketAddressPrototype::s_info = { "SocketAddress"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSSocketAddressPrototype) }; + +} // namespace Bun diff --git a/src/bun.js/bindings/JSSocketAddressPrototype.h b/src/bun.js/bindings/JSSocketAddressPrototype.h new file mode 100644 index 0000000000..ae70561ce8 --- /dev/null +++ b/src/bun.js/bindings/JSSocketAddressPrototype.h @@ -0,0 +1,42 @@ + +#pragma once + +#include "root.h" + +namespace Bun { + +class JSSocketAddressPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + + static JSSocketAddressPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSSocketAddressPrototype* ptr = new (NotNull, JSC::allocateCell(vm)) JSSocketAddressPrototype(vm, globalObject, structure); + ptr->finishCreation(vm); + return ptr; + } + + DECLARE_INFO; + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSSocketAddressPrototype, Base); + 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()); + } + +protected: + JSSocketAddressPrototype(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + : Base(vm, structure) + { + } + + // void finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject) + // void finishCreation(JSC::VM& vm) { Base::finishCreation(vm); } + DECLARE_DEFAULT_FINISH_CREATION; +}; + +} // namespace Bun diff --git a/src/bun.js/bindings/NodeValidator.cpp b/src/bun.js/bindings/NodeValidator.cpp index 088a03a3b5..8c0b260537 100644 --- a/src/bun.js/bindings/NodeValidator.cpp +++ b/src/bun.js/bindings/NodeValidator.cpp @@ -254,7 +254,13 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validatePort, (JSC::JSGlobalObject * globalO if (allowZero.isUndefined()) allowZero = jsBoolean(true); auto allowZero_b = allowZero.toBoolean(globalObject); - if (!port.isNumber() && !port.isString()) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); + + return V::validatePort(scope, globalObject, port, name, allowZero_b); +} + +JSC::EncodedJSValue V::validatePort(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue port, JSValue name, bool allowZero) +{ + if (!port.isNumber() && !port.isString()) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); if (port.isString()) { auto port_str = port.getString(globalObject); @@ -290,19 +296,19 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validatePort, (JSC::JSGlobalObject * globalO return false; }); if (trimmed.length() == 0) { - return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); + return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); } } auto port_num = port.toNumber(globalObject); RETURN_IF_EXCEPTION(scope, {}); - if (std::isnan(port_num)) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); - if (std::isinf(port_num)) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); - if (std::fmod(port_num, 1.0) != 0) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); - if (port_num < 0) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); - if (port_num > 0xffff) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); - if (port_num == 0 && !allowZero_b) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); + if (std::isnan(port_num)) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); + if (std::isinf(port_num)) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); + if (std::fmod(port_num, 1.0) != 0) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); + if (port_num < 0) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); + if (port_num > 0xffff) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); + if (port_num == 0 && !allowZero) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero); return JSValue::encode(port); } diff --git a/src/bun.js/bindings/NodeValidator.h b/src/bun.js/bindings/NodeValidator.h index 26593243d6..2c0d75a1dd 100644 --- a/src/bun.js/bindings/NodeValidator.h +++ b/src/bun.js/bindings/NodeValidator.h @@ -38,6 +38,7 @@ JSC::EncodedJSValue validateArray(JSC::ThrowScope& scope, JSC::JSGlobalObject* g JSC::EncodedJSValue validateArray(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, ASCIILiteral name, JSValue minLength); JSC::EncodedJSValue validateUint32(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name, JSValue positive); JSC::EncodedJSValue validateUint32(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, ASCIILiteral name, JSValue positive); +JSC::EncodedJSValue validatePort(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue port, JSC::JSValue name, bool allowZero); } diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 0bb72daecb..872251d984 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -115,6 +115,8 @@ #include "JSReadableStreamDefaultReader.h" #include "JSSink.h" #include "JSSocketAddress.h" +#include "JSSocketAddressConstructor.h" +#include "JSSocketAddressPrototype.h" #include "JSSQLStatement.h" #include "JSStringDecoder.h" #include "JSTextEncoder.h" @@ -2914,11 +2916,6 @@ void GlobalObject::finishCreation(VM& vm) init.vm, reinterpret_cast(init.owner))); }); - m_JSSocketAddressStructure.initLater( - [](const Initializer& init) { - init.set(JSSocketAddress::createStructure(init.vm, init.owner)); - }); - m_errorConstructorPrepareStackTraceInternalValue.initLater( [](const Initializer& init) { init.set(JSFunction::create(init.vm, init.owner, 2, "ErrorPrepareStackTrace"_s, jsFunctionDefaultErrorPrepareStackTrace, ImplementationVisibility::Public)); @@ -3286,6 +3283,17 @@ void GlobalObject::finishCreation(VM& vm) init.setConstructor(constructor); }); + m_JSSocketAddressClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + auto* prototype = JSSocketAddressPrototype::create(init.vm, init.global, JSSocketAddressPrototype::createStructure(init.vm, init.global, init.global->objectPrototype())); + auto* structure = JSSocketAddress::createStructure(init.vm, init.global, prototype); + auto* constructor = JSSocketAddressConstructor::create( + init.vm, init.global, JSSocketAddressConstructor::createStructure(init.vm, init.global, init.global->functionPrototype())); + init.setPrototype(prototype); + init.setStructure(structure); + init.setConstructor(constructor); + }); + m_JSBufferClassStructure.initLater( [](LazyClassStructure::Initializer& init) { auto prototype = WebCore::createBufferPrototype(init.vm, init.global); @@ -3890,8 +3898,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_JSHTTPSResponseControllerPrototype.visit(visitor); thisObject->m_JSHTTPSResponseSinkClassStructure.visit(visitor); thisObject->m_JSNetworkSinkClassStructure.visit(visitor); + thisObject->m_JSSocketAddressClassStructure.visit(visitor); thisObject->m_JSFetchTaskletChunkedRequestControllerPrototype.visit(visitor); - thisObject->m_JSSocketAddressStructure.visit(visitor); thisObject->m_JSSQLStatementStructure.visit(visitor); thisObject->m_V8GlobalInternals.visit(visitor); thisObject->m_JSStringDecoderClassStructure.visit(visitor); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index b34cb5aec5..640a3d03b9 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -216,6 +216,10 @@ public: JSC::JSValue NetworkSinkPrototype() const { return m_JSNetworkSinkClassStructure.prototypeInitializedOnMainThread(this); } JSC::JSValue JSReadableNetworkSinkControllerPrototype() const { return m_JSFetchTaskletChunkedRequestControllerPrototype.getInitializedOnMainThread(this); } + JSC::Structure* JSSocketAddressStructure() const { return m_JSSocketAddressClassStructure.getInitializedOnMainThread(this); } + JSC::JSObject* JSSocketAdddress() { return m_JSSocketAddressClassStructure.constructorInitializedOnMainThread(this); } + JSC::JSValue JSSocketAddressPrototype() const { return m_JSSocketAddressClassStructure.prototypeInitializedOnMainThread(this); } + JSC::Structure* JSBufferListStructure() const { return m_JSBufferListClassStructure.getInitializedOnMainThread(this); } JSC::JSObject* JSBufferList() { return m_JSBufferListClassStructure.constructorInitializedOnMainThread(this); } JSC::JSValue JSBufferListPrototype() const { return m_JSBufferListClassStructure.prototypeInitializedOnMainThread(this); } @@ -261,8 +265,6 @@ public: Structure* ImportMetaObjectStructure() const { return m_importMetaObjectStructure.getInitializedOnMainThread(this); } Structure* AsyncContextFrameStructure() const { return m_asyncBoundFunctionStructure.getInitializedOnMainThread(this); } - Structure* JSSocketAddressStructure() const { return m_JSSocketAddressStructure.getInitializedOnMainThread(this); } - JSWeakMap* vmModuleContextMap() const { return m_vmModuleContextMap.getInitializedOnMainThread(this); } Structure* NapiExternalStructure() const { return m_NapiExternalStructure.getInitializedOnMainThread(this); } @@ -531,6 +533,7 @@ public: LazyClassStructure m_JSHTTPResponseSinkClassStructure; LazyClassStructure m_JSHTTPSResponseSinkClassStructure; LazyClassStructure m_JSNetworkSinkClassStructure; + LazyClassStructure m_JSSocketAddressClassStructure; LazyClassStructure m_JSStringDecoderClassStructure; LazyClassStructure m_NapiClassStructure; @@ -575,7 +578,6 @@ public: LazyProperty m_cachedNodeVMGlobalObjectStructure; LazyProperty m_cachedGlobalProxyStructure; LazyProperty m_commonJSModuleObjectStructure; - LazyProperty m_JSSocketAddressStructure; LazyProperty m_memoryFootprintStructure; LazyProperty m_requireFunctionUnbound; LazyProperty m_requireResolveFunctionUnbound; diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index e5d24cb951..8ce90cc49e 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -61,6 +61,7 @@ public: std::unique_ptr m_clientSubspaceForJSS3Bucket; std::unique_ptr m_clientSubspaceForJSS3File; std::unique_ptr m_clientSubspaceForJSX509Certificate; + std::unique_ptr m_clientSubspaceForJSSocketAddress; #include "ZigGeneratedClasses+DOMClientIsoSubspaces.h" /* --- bun --- */ diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index bc995592e7..e333ee49fe 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -61,6 +61,7 @@ public: std::unique_ptr m_subspaceForJSS3Bucket; std::unique_ptr m_subspaceForJSS3File; std::unique_ptr m_subspaceForJSX509Certificate; + std::unique_ptr m_subspaceForJSSocketAddress; #include "ZigGeneratedClasses+DOMIsoSubspaces.h" /*-- BUN --*/ diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index 1878612950..95d5856d4d 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -27,6 +27,7 @@ using namespace JSC; macro(abortSteps) \ macro(addAbortAlgorithmToSignal) \ macro(addEventListener) \ + macro(address) \ macro(appendFromJS) \ macro(argv) \ macro(assignToStream) \