diff --git a/.cursor/rules/javascriptcore-class.mdc b/.cursor/rules/javascriptcore-class.mdc new file mode 100644 index 0000000000..62fedefb56 --- /dev/null +++ b/.cursor/rules/javascriptcore-class.mdc @@ -0,0 +1,378 @@ +--- +description: JavaScript class implemented in C++ +globs: *.cpp +--- + +# Implementing JavaScript classes in C++ + +If there is a publicly accessible Constructor and Prototype, then there are 3 classes: + +- IF there are C++ class members we need a destructor, so `class Foo : public JSC::DestructibleObject`, if no C++ class fields (only JS properties) then we don't need a class at all usually. We can instead use JSC::constructEmptyObject(vm, structure) and `putDirectOffset` like in [NodeFSBinding.cpp](mdc:src/bun.js/bindings/NodeFSBinding.cpp). +- class FooPrototype : public JSC::JSNonFinalObject +- class FooConstructor : public JSC::InternalFunction + +If there is no publicly accessible Constructor, just the Prototype and the class is necessary. In some cases, we can avoid the prototype entirely (but that's rare). + +If there are C++ fields on the Foo class, the Foo class will need an iso subspace added to [DOMClientIsoSubspaces.h](mdc:src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h) and [DOMIsoSubspaces.h](mdc:src/bun.js/bindings/webcore/DOMIsoSubspaces.h). Prototype and Constructor do not need subspaces. + +Usually you'll need to #include "root.h" at the top of C++ files or you'll get lint errors. + +Generally, defining the subspace looks like this: +```c++ + +class Foo : public JSC::DestructibleObject { + +// ... + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + if constexpr (mode == JSC::SubspaceAccess::Concurrently) + return nullptr; + return WebCore::subspaceForImpl( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceFor${MyClassT}.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceFor${MyClassT} = std::forward(space); }, + [](auto& spaces) { return spaces.m_subspaceFo${MyClassT}.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceFor${MyClassT} = std::forward(space); }); + } + + +``` + +It's better to put it in the .cpp file instead of the .h file, when possible. + +## Defining properties + +Define properties on the prototype. Use a const HashTableValues like this: +```C++ +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckEmail); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckHost); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckIP); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckIssued); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncCheckPrivateKey); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncToJSON); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncToLegacyObject); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncToString); +static JSC_DECLARE_HOST_FUNCTION(jsX509CertificateProtoFuncVerify); + +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_ca); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_fingerprint); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_fingerprint256); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_fingerprint512); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_subject); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_subjectAltName); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_infoAccess); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_keyUsage); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_issuer); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_issuerCertificate); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_publicKey); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_raw); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_serialNumber); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validFrom); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validTo); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validFromDate); +static JSC_DECLARE_CUSTOM_GETTER(jsX509CertificateGetter_validToDate); + +static const HashTableValue JSX509CertificatePrototypeTableValues[] = { + { "ca"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_ca, 0 } }, + { "checkEmail"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckEmail, 2 } }, + { "checkHost"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckHost, 2 } }, + { "checkIP"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckIP, 1 } }, + { "checkIssued"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckIssued, 1 } }, + { "checkPrivateKey"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncCheckPrivateKey, 1 } }, + { "fingerprint"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_fingerprint, 0 } }, + { "fingerprint256"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_fingerprint256, 0 } }, + { "fingerprint512"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_fingerprint512, 0 } }, + { "infoAccess"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_infoAccess, 0 } }, + { "issuer"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_issuer, 0 } }, + { "issuerCertificate"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_issuerCertificate, 0 } }, + { "keyUsage"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_keyUsage, 0 } }, + { "publicKey"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_publicKey, 0 } }, + { "raw"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_raw, 0 } }, + { "serialNumber"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_serialNumber, 0 } }, + { "subject"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_subject, 0 } }, + { "subjectAltName"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_subjectAltName, 0 } }, + { "toJSON"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncToJSON, 0 } }, + { "toLegacyObject"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncToLegacyObject, 0 } }, + { "toString"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncToString, 0 } }, + { "validFrom"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validFrom, 0 } }, + { "validFromDate"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessorOrValue), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validFromDate, 0 } }, + { "validTo"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validTo, 0 } }, + { "validToDate"_s, static_cast(PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessorOrValue), NoIntrinsic, { HashTableValue::GetterSetterType, jsX509CertificateGetter_validToDate, 0 } }, + { "verify"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsX509CertificateProtoFuncVerify, 1 } }, +}; +``` + +### Creating a prototype class + +Follow a pattern like this: + +```c++ +class JSX509CertificatePrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static constexpr unsigned StructureFlags = Base::StructureFlags; + + static JSX509CertificatePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSX509CertificatePrototype* prototype = new (NotNull, allocateCell(vm)) JSX509CertificatePrototype(vm, structure); + prototype->finishCreation(vm); + return prototype; + } + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + + DECLARE_INFO; + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + structure->setMayBePrototype(true); + return structure; + } + +private: + JSX509CertificatePrototype(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM& vm); +}; + +const ClassInfo JSX509CertificatePrototype::s_info = { "X509Certificate"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSX509CertificatePrototype) }; + +void JSX509CertificatePrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + reifyStaticProperties(vm, JSX509Certificate::info(), JSX509CertificatePrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); +} + +} // namespace Bun +``` + +### Getter definition: +```C++ + +JSC_DEFINE_CUSTOM_GETTER(jsX509CertificateGetter_ca, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSX509Certificate* thisObject = jsDynamicCast(JSValue::decode(thisValue)); + if (UNLIKELY(!thisObject)) { + Bun::throwThisTypeError(*globalObject, scope, "JSX509Certificate"_s, "ca"_s); + return {}; + } + + return JSValue::encode(jsBoolean(thisObject->view().isCA())); +} +``` + +### Setter definition + +```C++ +JSC_DEFINE_CUSTOM_SETTER(jsImportMetaObjectSetter_require, (JSGlobalObject * jsGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue encodedValue, PropertyName propertyName)) +{ + ImportMetaObject* thisObject = jsDynamicCast(JSValue::decode(thisValue)); + if (UNLIKELY(!thisObject)) + return false; + + JSValue value = JSValue::decode(encodedValue); + if (!value.isCell()) { + // TODO: + return true; + } + + thisObject->requireProperty.set(thisObject->vm(), thisObject, value.asCell()); + return true; +} +``` + +### Function definition + +```C++ +JSC_DEFINE_HOST_FUNCTION(jsX509CertificateProtoFuncToJSON, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + auto *thisObject = jsDynamicCast(callFrame->thisValue()); + if (UNLIKELY(!thisObject)) { + Bun::throwThisTypeError(*globalObject, scope, "MyClass"_s, "myFunctionName"_s); + return {}; + } + + return JSValue::encode(functionThatReturnsJSValue(vm, globalObject, thisObject)); +} +``` + + +### Constructor definition + +```C++ + +JSC_DECLARE_HOST_FUNCTION(callStats); +JSC_DECLARE_HOST_FUNCTION(constructStats); + +class JSStatsConstructor final : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + static constexpr unsigned StructureFlags = Base::StructureFlags; + + static JSStatsConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* prototype) + { + JSStatsConstructor* constructor = new (NotNull, JSC::allocateCell(vm)) JSStatsConstructor(vm, structure); + constructor->finishCreation(vm, prototype); + return constructor; + } + + DECLARE_INFO; + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.internalFunctionSpace(); + } + + 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()); + } + +private: + JSStatsConstructor(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure, callStats, constructStats) + { + } + + void finishCreation(JSC::VM& vm, JSC::JSObject* prototype) + { + Base::finishCreation(vm, 0, "Stats"_s); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + } +}; +``` + + +### Structure caching + +If there's a class, prototype, and constructor: + +1. Add the `JSC::LazyClassStructure` to [ZigGlobalObject.h](mdc:src/bun.js/bindings/ZigGlobalObject.h) +2. Initialize the class structure in [ZigGlobalObject.cpp](mdc:src/bun.js/bindings/ZigGlobalObject.cpp) in `void GlobalObject::finishCreation(VM& vm)` +3. Visit the class structure in visitChildren in [ZigGlobalObject.cpp](mdc:src/bun.js/bindings/ZigGlobalObject.cpp) in `void GlobalObject::visitChildrenImpl` + +```c++ + + m_JSStatsBigIntClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + Bun::initJSBigIntStatsClassStructure(init); + }); +``` + +If there's only a class, use `JSC::LazyProperty` instead of `JSC::LazyClassStructure`. + +Then, implement the function that creates the structure: +```c++ +void setupX509CertificateClassStructure(LazyClassStructure::Initializer& init) +{ + auto* prototypeStructure = JSX509CertificatePrototype::createStructure(init.vm, init.global, init.global->objectPrototype()); + auto* prototype = JSX509CertificatePrototype::create(init.vm, init.global, prototypeStructure); + + auto* constructorStructure = JSX509CertificateConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()); + + auto* constructor = JSX509CertificateConstructor::create(init.vm, init.global, constructorStructure, prototype); + + auto* structure = JSX509Certificate::createStructure(init.vm, init.global, prototype); + init.setPrototype(prototype); + init.setStructure(structure); + init.setConstructor(constructor); +} +``` + +Then, use the structure by calling `globalObject.m_myStructureName.get(globalObject)` + +```C++ +JSC_DEFINE_HOST_FUNCTION(x509CertificateConstructorConstruct, (JSGlobalObject * globalObject, CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (!callFrame->argumentCount()) { + Bun::throwError(globalObject, scope, ErrorCode::ERR_MISSING_ARGS, "X509Certificate constructor requires at least one argument"_s); + return {}; + } + + JSValue arg = callFrame->uncheckedArgument(0); + if (!arg.isCell()) { + Bun::throwError(globalObject, scope, ErrorCode::ERR_INVALID_ARG_TYPE, "X509Certificate constructor argument must be a Buffer, TypedArray, or string"_s); + return {}; + } + + auto* zigGlobalObject = defaultGlobalObject(globalObject); + Structure* structure = zigGlobalObject->m_JSX509CertificateClassStructure.get(zigGlobalObject); + JSValue newTarget = callFrame->newTarget(); + if (UNLIKELY(zigGlobalObject->m_JSX509CertificateClassStructure.constructor(zigGlobalObject) != newTarget)) { + auto scope = DECLARE_THROW_SCOPE(vm); + if (!newTarget) { + throwTypeError(globalObject, scope, "Class constructor X509Certificate cannot be invoked without 'new'"_s); + return {}; + } + + auto* functionGlobalObject = defaultGlobalObject(getFunctionRealm(globalObject, newTarget.getObject())); + RETURN_IF_EXCEPTION(scope, {}); + structure = InternalFunction::createSubclassStructure( + globalObject, newTarget.getObject(), functionGlobalObject->NodeVMScriptStructure()); + scope.release(); + } + + return JSValue::encode(createX509Certificate(vm, globalObject, structure, arg)); +} +``` + +### Expose to Zig + +To expose the constructor to zig: + +```c++ +extern "C" JSC::EncodedJSValue Bun__JSBigIntStatsObjectConstructor(Zig::GlobalObject* globalobject) +{ + return JSValue::encode(globalobject->m_JSStatsBigIntClassStructure.constructor(globalobject)); +} +``` + +Zig: +```zig +extern "C" fn Bun__JSBigIntStatsObjectConstructor(*JSC.JSGlobalObject) JSC.JSValue; +pub const getBigIntStatsConstructor = Bun__JSBigIntStatsObjectConstructor; +``` + +To create an object (instance) of a JS class defined in C++ from Zig, follow the __toJS convention like this: +```c++ +// X509* is whatever we need to create the object +extern "C" EncodedJSValue Bun__X509__toJS(Zig::GlobalObject* globalObject, X509* cert) +{ + // ... implementation details + auto* structure = globalObject->m_JSX509CertificateClassStructure.get(globalObject); + return JSValue::encode(JSX509Certificate::create(globalObject->vm(), structure, globalObject, WTFMove(cert))); +} +``` + +And from Zig: +```zig +const X509 = opaque { + // ... class + + extern fn Bun__X509__toJS(*JSC.JSGlobalObject, *X509) JSC.JSValue; + + pub fn toJS(this: *X509, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + return Bun__X509__toJS(globalObject, this); + } +}; +``` diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index c95530bccb..db0594382e 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -72,6 +72,18 @@ pub fn toJS(globalObject: *JSC.JSGlobalObject, comptime ValueType: type, value: }, JSC.JSValue => return if (Type != ValueType) value.* else value, + inline []const u16, []const u32, []const i16, []const i8, []const i32, []const f32 => { + var array = JSC.JSValue.createEmptyArray(globalObject, value.len); + for (value, 0..) |item, i| { + array.putIndex( + globalObject, + @truncate(i), + JSC.jsNumber(item), + ); + } + return array; + }, + else => { // Recursion can stack overflow here @@ -80,7 +92,7 @@ pub fn toJS(globalObject: *JSC.JSGlobalObject, comptime ValueType: type, value: var array = JSC.JSValue.createEmptyArray(globalObject, value.len); for (value, 0..) |*item, i| { - const res = toJS(globalObject, *const Child, item, lifetime); + const res = toJS(globalObject, *Child, item, lifetime); if (res == .zero) return .zero; array.putIndex( globalObject, diff --git a/src/bun.js/bindings/JSX509CertificatePrototype.h b/src/bun.js/bindings/JSX509CertificatePrototype.h index 4328cdd140..84614be24b 100644 --- a/src/bun.js/bindings/JSX509CertificatePrototype.h +++ b/src/bun.js/bindings/JSX509CertificatePrototype.h @@ -10,9 +10,9 @@ class VM; namespace Bun { -class JSX509CertificatePrototype final : public JSC::JSObject { +class JSX509CertificatePrototype final : public JSC::JSNonFinalObject { public: - using Base = JSC::JSObject; + using Base = JSC::JSNonFinalObject; static constexpr unsigned StructureFlags = Base::StructureFlags; static JSX509CertificatePrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) diff --git a/src/bun.js/bindings/NodeDirent.cpp b/src/bun.js/bindings/NodeDirent.cpp new file mode 100644 index 0000000000..043d09e617 --- /dev/null +++ b/src/bun.js/bindings/NodeDirent.cpp @@ -0,0 +1,372 @@ +#include "ErrorCode.h" +#include "headers-handwritten.h" +#include "root.h" + +#include "JavaScriptCore/FunctionPrototype.h" +#include "JavaScriptCore/LazyClassStructure.h" +#include "JavaScriptCore/LazyClassStructureInlines.h" +#include "JavaScriptCore/VMTrapsInlines.h" +#include "BunBuiltinNames.h" +#include "JavaScriptCore/ArgList.h" +#include "JavaScriptCore/JSType.h" +#include "JavaScriptCore/ObjectInitializationScope.h" + +#include "JavaScriptCore/ObjectConstructor.h" +#include +#include +#include +#include +#include +#include +#include +#include "ZigGlobalObject.h" + +namespace Bun { + +using namespace JSC; +using namespace WebCore; + +JSC_DECLARE_HOST_FUNCTION(callDirent); +JSC_DECLARE_HOST_FUNCTION(constructDirent); + +static JSC_DECLARE_HOST_FUNCTION(jsDirentProtoFuncIsBlockDevice); +static JSC_DECLARE_HOST_FUNCTION(jsDirentProtoFuncIsCharacterDevice); +static JSC_DECLARE_HOST_FUNCTION(jsDirentProtoFuncIsDirectory); +static JSC_DECLARE_HOST_FUNCTION(jsDirentProtoFuncIsFIFO); +static JSC_DECLARE_HOST_FUNCTION(jsDirentProtoFuncIsFile); +static JSC_DECLARE_HOST_FUNCTION(jsDirentProtoFuncIsSocket); +static JSC_DECLARE_HOST_FUNCTION(jsDirentProtoFuncIsSymbolicLink); + +static const HashTableValue JSDirentPrototypeTableValues[] = { + { "isBlockDevice"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDirentProtoFuncIsBlockDevice, 0 } }, + { "isCharacterDevice"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDirentProtoFuncIsCharacterDevice, 0 } }, + { "isDirectory"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDirentProtoFuncIsDirectory, 0 } }, + { "isFIFO"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDirentProtoFuncIsFIFO, 0 } }, + { "isFile"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDirentProtoFuncIsFile, 0 } }, + { "isSocket"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDirentProtoFuncIsSocket, 0 } }, + { "isSymbolicLink"_s, static_cast(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsDirentProtoFuncIsSymbolicLink, 0 } }, +}; + +static Structure* getStructure(Zig::GlobalObject* globalObject) +{ + return globalObject->m_JSDirentClassStructure.get(globalObject); +} + +// Prototype class +class JSDirentPrototype final : public JSC::JSNonFinalObject { +public: + using Base = JSC::JSNonFinalObject; + static constexpr unsigned StructureFlags = Base::StructureFlags; + + static JSDirentPrototype* create(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::Structure* structure) + { + JSDirentPrototype* prototype = new (NotNull, JSC::allocateCell(vm)) JSDirentPrototype(vm, structure); + prototype->finishCreation(vm); + return prototype; + } + + DECLARE_INFO; + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.plainObjectSpace(); + } + + static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype) + { + auto* structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); + structure->setMayBePrototype(true); + return structure; + } + +private: + JSDirentPrototype(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure) + { + } + + void finishCreation(JSC::VM& vm) + { + Base::finishCreation(vm); + reifyStaticProperties(vm, JSDirentPrototype::info(), JSDirentPrototypeTableValues, *this); + JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); + } +}; + +// Constructor class +class JSDirentConstructor final : public JSC::InternalFunction { +public: + using Base = JSC::InternalFunction; + static constexpr unsigned StructureFlags = Base::StructureFlags; + + static JSDirentConstructor* create(JSC::VM& vm, JSC::Structure* structure, JSC::JSObject* prototype) + { + JSDirentConstructor* constructor = new (NotNull, JSC::allocateCell(vm)) JSDirentConstructor(vm, structure); + constructor->finishCreation(vm, prototype); + return constructor; + } + + DECLARE_INFO; + + template + static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm) + { + return &vm.internalFunctionSpace(); + } + + 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()); + } + +private: + JSDirentConstructor(JSC::VM& vm, JSC::Structure* structure) + : Base(vm, structure, callDirent, constructDirent) + { + } + + void finishCreation(JSC::VM& vm, JSC::JSObject* prototype) + { + Base::finishCreation(vm, 3, "Dirent"_s); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); + } +}; + +JSC::Structure* createJSDirentObjectStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + auto* prototype = JSDirentPrototype::create(vm, globalObject, JSDirentPrototype::createStructure(vm, globalObject, globalObject->objectPrototype())); + auto structure = JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::FinalObjectType, 0), JSFinalObject::info(), NonArray, 4); + + // Add property transitions for all dirent fields + PropertyOffset offset = 0; + structure = structure->addPropertyTransition(vm, structure, vm.propertyNames->name, 0, offset); + structure = structure->addPropertyTransition(vm, structure, Bun::builtinNames(vm).pathPublicName(), 0, offset); + structure = structure->addPropertyTransition(vm, structure, Bun::builtinNames(vm).dataPrivateName(), 0, offset); + structure = structure->addPropertyTransition(vm, structure, Identifier::fromString(vm, "parentPath"_s), 0, offset); + + return structure; +} + +JSC_DEFINE_HOST_FUNCTION(callDirent, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return Bun::throwError(globalObject, scope, ErrorCode::ERR_ILLEGAL_CONSTRUCTOR, "Dirent constructor cannot be called as a function"_s); +} + +JSC_DEFINE_HOST_FUNCTION(constructDirent, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSValue name = callFrame->argument(0); + JSValue type = callFrame->argument(1); + JSValue path = callFrame->argument(2); + + auto* zigGlobalObject = defaultGlobalObject(globalObject); + Structure* structure = zigGlobalObject->m_JSDirentClassStructure.get(zigGlobalObject); + auto* originalStructure = structure; + JSValue newTarget = callFrame->newTarget(); + if (UNLIKELY(zigGlobalObject->m_JSDirentClassStructure.constructor(zigGlobalObject) != newTarget)) { + auto scope = DECLARE_THROW_SCOPE(vm); + if (!newTarget) { + throwTypeError(globalObject, scope, "Class constructor Dirent cannot be invoked without 'new'"_s); + return {}; + } + + auto* functionGlobalObject = defaultGlobalObject(getFunctionRealm(globalObject, newTarget.getObject())); + RETURN_IF_EXCEPTION(scope, {}); + structure = InternalFunction::createSubclassStructure( + globalObject, newTarget.getObject(), functionGlobalObject->m_JSDirentClassStructure.get(functionGlobalObject)); + scope.release(); + } + + auto* object = JSC::JSFinalObject::create(vm, structure); + if (structure->id() != originalStructure->id()) { + object->putDirect(vm, vm.propertyNames->name, name, 0); + object->putDirect(vm, Bun::builtinNames(vm).pathPublicName(), path, 0); + object->putDirect(vm, Bun::builtinNames(vm).dataPrivateName(), type, 0); + object->putDirect(vm, Identifier::fromString(vm, "parentPath"_s), path, 0); + } else { + object->putDirectOffset(vm, 0, name); + object->putDirectOffset(vm, 1, path); + object->putDirectOffset(vm, 2, type); + object->putDirectOffset(vm, 3, path); + } + + return JSValue::encode(object); +} + +static inline int32_t getType(JSC::VM& vm, JSValue value, Zig::GlobalObject* globalObject) +{ + JSObject* object = value.getObject(); + if (UNLIKELY(!object)) { + return std::numeric_limits::max(); + } + auto* structure = getStructure(globalObject); + JSValue type; + if (structure->id() != object->structure()->id()) { + type = object->get(globalObject, Bun::builtinNames(vm).dataPrivateName()); + if (UNLIKELY(!type)) { + return std::numeric_limits::max(); + } + } else { + type = object->getDirect(2); + } + + if (type.isAnyInt()) { + return type.toInt32(globalObject); + } + + return std::numeric_limits::max(); +} + +enum class DirEntType : int32_t { + // These have to match up with uv_dirent_type_t + Unknown = 0, + File = 1, + Directory = 2, + SymLink = 3, + NamedPipe = 4, + UnixDomainSocket = 5, + CharacterDevice = 6, + BlockDevice = 7, + Whiteout = 0, + Door = 0, + EventPort = 0 +}; + +JSC_DEFINE_HOST_FUNCTION(jsDirentProtoFuncIsBlockDevice, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t type = getType(vm, callFrame->thisValue(), defaultGlobalObject(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + return JSValue::encode(jsBoolean(type == static_cast(DirEntType::BlockDevice))); +} + +JSC_DEFINE_HOST_FUNCTION(jsDirentProtoFuncIsCharacterDevice, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t type = getType(vm, callFrame->thisValue(), defaultGlobalObject(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + return JSValue::encode(jsBoolean(type == static_cast(DirEntType::CharacterDevice))); +} + +JSC_DEFINE_HOST_FUNCTION(jsDirentProtoFuncIsDirectory, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t type = getType(vm, callFrame->thisValue(), defaultGlobalObject(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + return JSValue::encode(jsBoolean(type == static_cast(DirEntType::Directory))); +} + +JSC_DEFINE_HOST_FUNCTION(jsDirentProtoFuncIsFIFO, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t type = getType(vm, callFrame->thisValue(), defaultGlobalObject(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + return JSValue::encode(jsBoolean(type == static_cast(DirEntType::NamedPipe) || type == static_cast(DirEntType::EventPort))); +} + +JSC_DEFINE_HOST_FUNCTION(jsDirentProtoFuncIsFile, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t type = getType(vm, callFrame->thisValue(), defaultGlobalObject(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + return JSValue::encode(jsBoolean(type == static_cast(DirEntType::File))); +} + +JSC_DEFINE_HOST_FUNCTION(jsDirentProtoFuncIsSocket, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t type = getType(vm, callFrame->thisValue(), defaultGlobalObject(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + return JSValue::encode(jsBoolean(type == static_cast(DirEntType::UnixDomainSocket))); +} + +JSC_DEFINE_HOST_FUNCTION(jsDirentProtoFuncIsSymbolicLink, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + int32_t type = getType(vm, callFrame->thisValue(), defaultGlobalObject(globalObject)); + RETURN_IF_EXCEPTION(scope, {}); + + return JSValue::encode(jsBoolean(type == static_cast(DirEntType::SymLink))); +} + +void initJSDirentClassStructure(JSC::LazyClassStructure::Initializer& init) +{ + auto* structure = createJSDirentObjectStructure(init.vm, init.global); + auto* prototype = structure->storedPrototypeObject(); + auto* constructor = JSDirentConstructor::create(init.vm, JSDirentConstructor::createStructure(init.vm, init.global, init.global->functionPrototype()), prototype); + init.setPrototype(prototype); + init.setStructure(structure); + init.setConstructor(constructor); +} + +extern "C" JSC::EncodedJSValue Bun__JSDirentObjectConstructor(Zig::GlobalObject* globalobject) +{ + return JSValue::encode(globalobject->m_JSDirentClassStructure.constructor(globalobject)); +} + +extern "C" JSC::EncodedJSValue Bun__Dirent__toJS(Zig::GlobalObject* globalObject, int type, BunString* name, BunString* path, JSString** previousPath) +{ + auto& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* structure = globalObject->m_JSDirentClassStructure.get(globalObject); + auto* object = JSC::JSFinalObject::create(vm, structure); + JSString* pathValue = nullptr; + if (path && path->tag == BunStringTag::WTFStringImpl && previousPath && *previousPath && (*previousPath)->length() == path->impl.wtf->length()) { + auto view = (*previousPath)->view(globalObject); + if (view == path->impl.wtf) { + pathValue = *previousPath; + + // Decrement the ref count of the previous path + auto pathString = path->transferToWTFString(); + } + } + + if (!pathValue) { + auto pathString = path->transferToWTFString(); + pathValue = jsString(vm, WTFMove(pathString)); + if (previousPath) { + *previousPath = pathValue; + } + } + + auto nameString = name->transferToWTFString(); + auto nameValue = jsString(vm, WTFMove(nameString)); + auto typeValue = jsNumber(type); + object->putDirectOffset(vm, 0, nameValue); + object->putDirectOffset(vm, 1, pathValue); + object->putDirectOffset(vm, 2, typeValue); + object->putDirectOffset(vm, 3, pathValue); + + return JSValue::encode(object); +} + +const ClassInfo JSDirentPrototype::s_info = { "Dirent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDirentPrototype) }; +const ClassInfo JSDirentConstructor::s_info = { "Dirent"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSDirentConstructor) }; + +} // namespace Bun diff --git a/src/bun.js/bindings/NodeDirent.h b/src/bun.js/bindings/NodeDirent.h new file mode 100644 index 0000000000..6cfcf5bdf0 --- /dev/null +++ b/src/bun.js/bindings/NodeDirent.h @@ -0,0 +1,9 @@ +#pragma once + +#include "JavaScriptCore/LazyClassStructure.h" + +namespace Bun { + +void initJSDirentClassStructure(JSC::LazyClassStructure::Initializer& init); + +} // namespace Bun diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 55b14857fc..619dbd2fbb 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -166,6 +166,7 @@ #endif #include "NodeFSBinding.h" +#include "NodeDirent.h" #if !OS(WINDOWS) #include @@ -2785,6 +2786,12 @@ void GlobalObject::finishCreation(VM& vm) m_http2_commongStrings.initialize(); Bun::addNodeModuleConstructorProperties(vm, this); + + m_JSDirentClassStructure.initLater( + [](LazyClassStructure::Initializer& init) { + Bun::initJSDirentClassStructure(init); + }); + m_JSX509CertificateClassStructure.initLater([](LazyClassStructure::Initializer& init) { setupX509CertificateClassStructure(init); }); @@ -3840,6 +3847,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) thisObject->m_memoryFootprintStructure.visit(visitor); thisObject->m_JSStatsClassStructure.visit(visitor); thisObject->m_JSStatsBigIntClassStructure.visit(visitor); + thisObject->m_JSDirentClassStructure.visit(visitor); thisObject->m_NapiClassStructure.visit(visitor); thisObject->m_NapiExternalStructure.visit(visitor); thisObject->m_NAPIFunctionStructure.visit(visitor); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index da6abf2ccf..8aefbaa730 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -483,8 +483,9 @@ public: LazyProperty m_JSS3FileStructure; LazyProperty m_S3ErrorStructure; - LazyClassStructure m_JSStatsClassStructure; - LazyClassStructure m_JSStatsBigIntClassStructure; + JSC::LazyClassStructure m_JSStatsClassStructure; + JSC::LazyClassStructure m_JSStatsBigIntClassStructure; + JSC::LazyClassStructure m_JSDirentClassStructure; JSObject* cryptoObject() const { return m_cryptoObject.getInitializedOnMainThread(this); } JSObject* JSDOMFileConstructor() const { return m_JSDOMFileConstructor.getInitializedOnMainThread(this); } diff --git a/src/bun.js/bindings/generated_classes_list.zig b/src/bun.js/bindings/generated_classes_list.zig index 15bfb27776..d179e7059b 100644 --- a/src/bun.js/bindings/generated_classes_list.zig +++ b/src/bun.js/bindings/generated_classes_list.zig @@ -12,7 +12,6 @@ pub const Classes = struct { pub const EndTag = JSC.Cloudflare.EndTag; pub const AttributeIterator = JSC.Cloudflare.AttributeIterator; pub const CryptoHasher = JSC.API.Bun.Crypto.CryptoHasher; - pub const Dirent = JSC.Node.Dirent; pub const Expect = JSC.Expect.Expect; pub const ExpectAny = JSC.Expect.ExpectAny; pub const ExpectAnything = JSC.Expect.ExpectAnything; diff --git a/src/bun.js/node/node.classes.ts b/src/bun.js/node/node.classes.ts index 2d711f943a..bee94131c8 100644 --- a/src/bun.js/node/node.classes.ts +++ b/src/bun.js/node/node.classes.ts @@ -161,55 +161,6 @@ export default [ }, values: ["arguments", "callback"], }), - define({ - name: "Dirent", - construct: true, - finalize: true, - - klass: {}, - - // TODO: generate-classes needs to handle Object.create properly when - // functions are used. The functions need a fallback implementation to use - // getters. - supportsObjectCreate: true, - - proto: { - isBlockDevice: { - fn: "isBlockDevice", - length: 0, - }, - isCharacterDevice: { - fn: "isCharacterDevice", - length: 0, - }, - isDirectory: { - fn: "isDirectory", - length: 0, - }, - isFIFO: { - fn: "isFIFO", - length: 0, - }, - isFile: { - fn: "isFile", - length: 0, - }, - isSocket: { - fn: "isSocket", - length: 0, - }, - isSymbolicLink: { - fn: "isSymbolicLink", - length: 0, - }, - }, - - own: { - name: "getName", - path: "getPath", - parentPath: "getPath", - }, - }), define({ name: "NodeJSFS", construct: true, diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 582086447f..74c1791180 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -3308,7 +3308,18 @@ const Return = struct { switch (this) { .with_file_types => { defer bun.default_allocator.free(this.with_file_types); - return JSC.toJS(globalObject, []Dirent, this.with_file_types, .temporary); + var array = JSC.JSValue.createEmptyArray(globalObject, this.with_file_types.len); + var previous_jsstring: ?*JSC.JSString = null; + for (this.with_file_types, 0..) |*item, i| { + const res = item.toJSNewlyCreated(globalObject, &previous_jsstring); + if (res == .zero) return .zero; + array.putIndex( + globalObject, + @truncate(i), + res, + ); + } + return array; }, .buffers => { defer bun.default_allocator.free(this.buffers); diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 7e465d9623..5939857dee 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -1837,113 +1837,43 @@ pub const Dirent = struct { kind: Kind, pub const Kind = std.fs.File.Kind; - pub usingnamespace JSC.Codegen.JSDirent; - pub usingnamespace bun.New(@This()); - pub fn constructor(global: *JSC.JSGlobalObject, call_frame: *JSC.CallFrame) bun.JSError!*Dirent { - const name_js, const type_js, const path_js = call_frame.argumentsAsArray(3); + extern fn Bun__JSDirentObjectConstructor(*JSC.JSGlobalObject) JSC.JSValue; + pub const getConstructor = Bun__JSDirentObjectConstructor; - const name = try name_js.toBunString2(global); - errdefer name.deref(); + extern fn Bun__Dirent__toJS(*JSC.JSGlobalObject, i32, *bun.String, *bun.String, cached_previous_path_jsvalue: ?*?*JSC.JSString) JSC.JSValue; + pub fn toJS(this: *Dirent, globalObject: *JSC.JSGlobalObject, cached_previous_path_jsvalue: ?*?*JSC.JSString) JSC.JSValue { + return Bun__Dirent__toJS( + globalObject, + switch (this.kind) { + .file => bun.windows.libuv.UV_DIRENT_FILE, + .block_device => bun.windows.libuv.UV_DIRENT_BLOCK, + .character_device => bun.windows.libuv.UV_DIRENT_CHAR, + .directory => bun.windows.libuv.UV_DIRENT_DIR, + // event_port is deliberate there. + .event_port, .named_pipe => bun.windows.libuv.UV_DIRENT_FIFO, - const path = try path_js.toBunString2(global); - errdefer path.deref(); + .unix_domain_socket => bun.windows.libuv.UV_DIRENT_SOCKET, + .sym_link => bun.windows.libuv.UV_DIRENT_LINK, - const kind = type_js.toInt32(); - const kind_enum: Kind = switch (kind) { - // these correspond to the libuv constants - else => .unknown, - 1 => .file, - 2 => .directory, - 3 => .sym_link, - 4 => .named_pipe, - 5 => .unix_domain_socket, - 6 => .character_device, - 7 => .block_device, - }; - - return Dirent.new(.{ - .name = name, - .path = path, - .kind = kind_enum, - }); + .whiteout, .door, .unknown => bun.windows.libuv.UV_DIRENT_UNKNOWN, + }, + &this.name, + &this.path, + cached_previous_path_jsvalue, + ); } - pub fn toJS(this: *Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue { - return Dirent.toJSUnchecked(globalObject, this); - } - - pub fn toJSNewlyCreated(this: *const Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue { - return toJS(Dirent.new(this.*), globalObject); - } - - pub fn getName(this: *Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue { - return this.name.toJS(globalObject); - } - - pub fn getPath(this: *Dirent, globalThis: *JSC.JSGlobalObject) JSC.JSValue { - return this.path.toJS(globalThis); - } - - pub fn isBlockDevice( - this: *Dirent, - _: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) bun.JSError!JSC.JSValue { - return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.block_device); - } - pub fn isCharacterDevice( - this: *Dirent, - _: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) bun.JSError!JSC.JSValue { - return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.character_device); - } - pub fn isDirectory( - this: *Dirent, - _: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) bun.JSError!JSC.JSValue { - return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.directory); - } - pub fn isFIFO( - this: *Dirent, - _: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) bun.JSError!JSC.JSValue { - return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.named_pipe or this.kind == std.fs.File.Kind.event_port); - } - pub fn isFile( - this: *Dirent, - _: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) bun.JSError!JSC.JSValue { - return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.file); - } - pub fn isSocket( - this: *Dirent, - _: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) bun.JSError!JSC.JSValue { - return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.unix_domain_socket); - } - pub fn isSymbolicLink( - this: *Dirent, - _: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) bun.JSError!JSC.JSValue { - return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.sym_link); + pub fn toJSNewlyCreated(this: *Dirent, globalObject: *JSC.JSGlobalObject, previous_jsstring: ?*?*JSC.JSString) JSC.JSValue { + // Shouldn't techcnically be necessary. + defer this.deref(); + return this.toJS(globalObject, previous_jsstring); } pub fn deref(this: *const Dirent) void { this.name.deref(); this.path.deref(); } - - pub fn finalize(this: *Dirent) void { - this.deref(); - this.destroy(); - } }; pub const Process = struct {