diff options
-rw-r--r-- | src/bun.js/bindings/ModuleLoader.cpp | 8 | ||||
-rw-r--r-- | src/bun.js/bindings/exports.zig | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-handwritten.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/node_util_types.cpp | 352 | ||||
-rw-r--r-- | src/bun.js/bindings/node_util_types.h | 11 | ||||
-rw-r--r-- | src/bun.js/module_loader.zig | 5 | ||||
-rw-r--r-- | test/bun.js/test-util-types.test.js | 281 |
7 files changed, 659 insertions, 0 deletions
diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index c35627f25..40e41b083 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -34,6 +34,7 @@ #include "../modules/ObjectModule.h" #include "../modules/NodeModuleModule.h" #include "../modules/TTYModule.h" +#include "node_util_types.h" namespace Bun { using namespace Zig; @@ -426,6 +427,13 @@ static JSValue fetchSourceCode( return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source))); } + case SyntheticModuleType::NodeUtilTypes: { + auto source = JSC::SourceCode( + JSC::SyntheticSourceProvider::create(Bun::generateNodeUtilTypesSourceCode, + JSC::SourceOrigin(), WTFMove(moduleKey))); + + return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source))); + } case SyntheticModuleType::Process: { auto source = JSC::SourceCode( JSC::SyntheticSourceProvider::create(generateProcessSourceCode, diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index 953c2e807..2e187e302 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -235,6 +235,7 @@ pub const ResolvedSource = extern struct { @"node:string_decoder" = 1027, @"node:module" = 1028, @"node:tty" = 1029, + @"node:util/types" = 1030, }; }; diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 5ce164a69..0a2619187 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -207,6 +207,7 @@ enum SyntheticModuleType : uint64_t { StringDecoder = 1027, Module = 1028, TTY = 1029, + NodeUtilTypes = 1030, }; extern "C" const char* Bun__userAgent; diff --git a/src/bun.js/bindings/node_util_types.cpp b/src/bun.js/bindings/node_util_types.cpp new file mode 100644 index 000000000..54fcca78b --- /dev/null +++ b/src/bun.js/bindings/node_util_types.cpp @@ -0,0 +1,352 @@ +#include "root.h" +#include "node_util_types.h" + +#include "webcrypto/JSCryptoKey.h" +#include "napi_external.h" +#include "JavaScriptCore/CallFrame.h" +#include "JavaScriptCore/CallFrameInlines.h" +#include "JavaScriptCore/AggregateError.h" +#include "JavaScriptCore/JSArrayBuffer.h" +#include "webcrypto/JSJsonWebKey.h" +#include "JavaScriptCore/ObjectConstructor.h" +#include "JavaScriptCore/GeneratorFunctionPrototype.h" +#include "JavaScriptCore/AsyncFunctionPrototype.h" + +using namespace JSC; + +#define GET_FIRST_VALUE \ + if (callframe->argumentCount() < 1) \ + return JSValue::encode(jsBoolean(false)); \ + JSValue value = callframe->uncheckedArgument(0); + +#define GET_FIRST_CELL \ + if (callframe->argumentCount() < 1) \ + return JSValue::encode(jsBoolean(false)); \ + JSValue value = callframe->uncheckedArgument(0); \ + if (!value.isCell()) \ + return JSValue::encode(jsBoolean(false)); \ + JSCell* cell = value.asCell(); + +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsExternal, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + return JSValue::encode(jsBoolean(value.isCell() && jsDynamicCast<Bun::NapiExternal*>(value) != nullptr)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsDate, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSDateType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsArgumentsObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + if (!value.isCell()) + return JSValue::encode(jsBoolean(false)); + + auto type = value.asCell()->type(); + switch (type) { + case DirectArgumentsType: + case ScopedArgumentsType: + case ClonedArgumentsType: + return JSValue::encode(jsBoolean(true)); + default: + return JSValue::encode(jsBoolean(false)); + } + + __builtin_unreachable(); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBigIntObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(globalObject->bigIntObjectStructure() == cell->structure())); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBooleanObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + return JSValue::encode(jsBoolean(value.isCell() && value.asCell()->type() == BooleanObjectType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsNumberObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + return JSValue::encode(jsBoolean(value.isCell() && value.asCell()->type() == NumberObjectType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsStringObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + return JSValue::encode(jsBoolean(value.isCell() && (value.asCell()->type() == StringObjectType || value.asCell()->type() == DerivedStringObjectType))); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsSymbolObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + + return JSValue::encode(jsBoolean(globalObject->symbolObjectStructure() == cell->structure())); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsNativeError, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + return JSValue::encode(jsBoolean(value.isCell() && jsDynamicCast<ErrorInstance*>(value) != nullptr)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsRegExp, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + return JSValue::encode(jsBoolean(value.isCell() && value.asCell()->type() == RegExpObjectType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsAsyncFunction, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(JSValue::strictEqual(globalObject, JSValue(globalObject->asyncFunctionPrototype()), cell->getObject()->getPrototype(cell->getObject(), globalObject)))); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsGeneratorFunction, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_VALUE + auto* function = jsDynamicCast<JSFunction*>(value); + if (!function) + return JSValue::encode(jsBoolean(false)); + + auto* executable = function->jsExecutable(); + if (!executable) + return JSValue::encode(jsBoolean(false)); + + return JSValue::encode(jsBoolean(executable->isGenerator())); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsGeneratorObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + + return JSValue::encode(jsBoolean(cell->type() == JSGeneratorType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsPromise, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSPromiseType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsMap, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSMapType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsSet, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSSetType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsMapIterator, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSMapIteratorType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsSetIterator, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSSetIteratorType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsWeakMap, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSWeakMapType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsWeakSet, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == JSWeakSetType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsArrayBuffer, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(jsDynamicCast<JSArrayBuffer*>(cell) != nullptr)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsDataView, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == DataViewType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsSharedArrayBuffer, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + auto* arrayBuffer = jsDynamicCast<JSArrayBuffer*>(cell); + if (!arrayBuffer) + return JSValue::encode(jsBoolean(false)); + return JSValue::encode(jsBoolean(arrayBuffer->isShared())); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsProxy, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == PureForwardingProxyType || cell->type() == ProxyObjectType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsModuleNamespaceObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == ModuleNamespaceObjectType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsAnyArrayBuffer, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + auto* arrayBuffer = jsDynamicCast<JSArrayBuffer*>(cell); + return JSValue::encode(jsBoolean(arrayBuffer != nullptr)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBoxedPrimitive, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + switch (cell->type()) { + case JSC::BooleanObjectType: + case JSC::NumberObjectType: + case JSC::StringObjectType: + case JSC::DerivedStringObjectType: + return JSValue::encode(jsBoolean(true)); + + default: { + if (cell->structure() == globalObject->symbolObjectStructure()) + return JSValue::encode(jsBoolean(true)); + + if (cell->structure() == globalObject->bigIntObjectStructure()) + return JSValue::encode(jsBoolean(true)); + } + } + + return JSValue::encode(jsBoolean(false)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsArrayBufferView, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() >= Int8ArrayType && cell->type() <= DataViewType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsTypedArray, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() >= Int8ArrayType && cell->type() <= BigUint64ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsUint8Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Uint8ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsUint8ClampedArray, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Uint8ClampedArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsUint16Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Uint16ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsUint32Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Uint32ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsInt8Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Int8ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsInt16Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Int16ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsInt32Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Int32ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsFloat32Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Float32ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsFloat64Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == Float64ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBigInt64Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == BigInt64ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsBigUint64Array, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->type() == BigUint64ArrayType)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsKeyObject, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + // Not implemented + return JSValue::encode(jsBoolean(false)); +} +JSC_DEFINE_HOST_FUNCTION(jsFunctionIsCryptoKey, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callframe)) +{ + GET_FIRST_CELL + return JSValue::encode(jsBoolean(cell->inherits<WebCore::JSCryptoKey>())); +} + +namespace Bun { +void generateNodeUtilTypesSourceCode(JSC::JSGlobalObject* lexicalGlobalObject, + JSC::Identifier moduleKey, + Vector<JSC::Identifier, 4>& exportNames, + JSC::MarkedArgumentBuffer& exportValues) +{ + Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + + JSC::VM& vm = globalObject->vm(); + + JSC::JSObject* defaultObject = constructEmptyObject(globalObject, globalObject->objectPrototype(), 43); + exportNames.reserveCapacity(43); + exportValues.ensureCapacity(43); + + auto putBoth = [&](JSC::Identifier identifier, NativeFunction functionPtr) { + JSC::JSFunction* function = JSC::JSFunction::create(vm, globalObject, 1, identifier.string(), functionPtr, ImplementationVisibility::Public, NoIntrinsic, functionPtr); + defaultObject->putDirect(vm, identifier, function, 0); + exportNames.append(identifier); + exportValues.append(function); + }; + + putBoth(Identifier::fromString(vm, "isExternal"_s), jsFunctionIsExternal); + putBoth(Identifier::fromString(vm, "isDate"_s), jsFunctionIsDate); + putBoth(Identifier::fromString(vm, "isArgumentsObject"_s), jsFunctionIsArgumentsObject); + putBoth(Identifier::fromString(vm, "isBigIntObject"_s), jsFunctionIsBigIntObject); + putBoth(Identifier::fromString(vm, "isBooleanObject"_s), jsFunctionIsBooleanObject); + putBoth(Identifier::fromString(vm, "isNumberObject"_s), jsFunctionIsNumberObject); + putBoth(Identifier::fromString(vm, "isStringObject"_s), jsFunctionIsStringObject); + putBoth(Identifier::fromString(vm, "isSymbolObject"_s), jsFunctionIsSymbolObject); + putBoth(Identifier::fromString(vm, "isNativeError"_s), jsFunctionIsNativeError); + putBoth(Identifier::fromString(vm, "isRegExp"_s), jsFunctionIsRegExp); + putBoth(Identifier::fromString(vm, "isAsyncFunction"_s), jsFunctionIsAsyncFunction); + putBoth(Identifier::fromString(vm, "isGeneratorFunction"_s), jsFunctionIsGeneratorFunction); + putBoth(Identifier::fromString(vm, "isGeneratorObject"_s), jsFunctionIsGeneratorObject); + putBoth(Identifier::fromString(vm, "isPromise"_s), jsFunctionIsPromise); + putBoth(Identifier::fromString(vm, "isMap"_s), jsFunctionIsMap); + putBoth(Identifier::fromString(vm, "isSet"_s), jsFunctionIsSet); + putBoth(Identifier::fromString(vm, "isMapIterator"_s), jsFunctionIsMapIterator); + putBoth(Identifier::fromString(vm, "isSetIterator"_s), jsFunctionIsSetIterator); + putBoth(Identifier::fromString(vm, "isWeakMap"_s), jsFunctionIsWeakMap); + putBoth(Identifier::fromString(vm, "isWeakSet"_s), jsFunctionIsWeakSet); + putBoth(Identifier::fromString(vm, "isArrayBuffer"_s), jsFunctionIsArrayBuffer); + putBoth(Identifier::fromString(vm, "isDataView"_s), jsFunctionIsDataView); + putBoth(Identifier::fromString(vm, "isSharedArrayBuffer"_s), jsFunctionIsSharedArrayBuffer); + putBoth(Identifier::fromString(vm, "isProxy"_s), jsFunctionIsProxy); + putBoth(Identifier::fromString(vm, "isModuleNamespaceObject"_s), jsFunctionIsModuleNamespaceObject); + putBoth(Identifier::fromString(vm, "isAnyArrayBuffer"_s), jsFunctionIsAnyArrayBuffer); + putBoth(Identifier::fromString(vm, "isBoxedPrimitive"_s), jsFunctionIsBoxedPrimitive); + putBoth(Identifier::fromString(vm, "isArrayBufferView"_s), jsFunctionIsArrayBufferView); + putBoth(Identifier::fromString(vm, "isTypedArray"_s), jsFunctionIsTypedArray); + putBoth(Identifier::fromString(vm, "isUint8Array"_s), jsFunctionIsUint8Array); + putBoth(Identifier::fromString(vm, "isUint8ClampedArray"_s), jsFunctionIsUint8ClampedArray); + putBoth(Identifier::fromString(vm, "isUint16Array"_s), jsFunctionIsUint16Array); + putBoth(Identifier::fromString(vm, "isUint32Array"_s), jsFunctionIsUint32Array); + putBoth(Identifier::fromString(vm, "isInt8Array"_s), jsFunctionIsInt8Array); + putBoth(Identifier::fromString(vm, "isInt16Array"_s), jsFunctionIsInt16Array); + putBoth(Identifier::fromString(vm, "isInt32Array"_s), jsFunctionIsInt32Array); + putBoth(Identifier::fromString(vm, "isFloat32Array"_s), jsFunctionIsFloat32Array); + putBoth(Identifier::fromString(vm, "isFloat64Array"_s), jsFunctionIsFloat64Array); + putBoth(Identifier::fromString(vm, "isBigInt64Array"_s), jsFunctionIsBigInt64Array); + putBoth(Identifier::fromString(vm, "isBigUint64Array"_s), jsFunctionIsBigUint64Array); + putBoth(Identifier::fromString(vm, "isKeyObject"_s), jsFunctionIsKeyObject); + putBoth(Identifier::fromString(vm, "isCryptoKey"_s), jsFunctionIsCryptoKey); + defaultObject->putDirect(vm, JSC::PropertyName(Identifier::fromUid(vm.symbolRegistry().symbolForKey("CommonJS"_s))), jsNumber(0), 0); + + exportNames.append(JSC::Identifier::fromString(vm, "default"_s)); + exportValues.append(defaultObject); +} +}
\ No newline at end of file diff --git a/src/bun.js/bindings/node_util_types.h b/src/bun.js/bindings/node_util_types.h new file mode 100644 index 000000000..adf0cd0ea --- /dev/null +++ b/src/bun.js/bindings/node_util_types.h @@ -0,0 +1,11 @@ +#include "JavaScriptCore/JSGlobalObject.h" +#include "ZigGlobalObject.h" + +namespace Bun { +using namespace WebCore; + +void generateNodeUtilTypesSourceCode(JSC::JSGlobalObject* lexicalGlobalObject, + JSC::Identifier moduleKey, + Vector<JSC::Identifier, 4>& exportNames, + JSC::MarkedArgumentBuffer& exportValues); +} diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index a9e2dca5e..f36884014 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -1627,6 +1627,7 @@ pub const ModuleLoader = struct { .@"node:events" => return jsSyntheticModule(.@"node:events"), .@"node:process" => return jsSyntheticModule(.@"node:process"), .@"node:tty" => return jsSyntheticModule(.@"node:tty"), + .@"node:util/types" => return jsSyntheticModule(.@"node:util/types"), .@"node:stream" => { return ResolvedSource{ .allocator = null, @@ -1984,6 +1985,7 @@ pub const HardcodedModule = enum { @"node:timers/promises", @"node:tty", @"node:url", + @"node:util/types", @"undici", @"ws", /// Already resolved modules go in here. @@ -2024,6 +2026,7 @@ pub const HardcodedModule = enum { .{ "node:timers/promises", HardcodedModule.@"node:timers/promises" }, .{ "node:tty", HardcodedModule.@"node:tty" }, .{ "node:url", HardcodedModule.@"node:url" }, + .{ "node:util/types", HardcodedModule.@"node:util/types" }, .{ "undici", HardcodedModule.@"undici" }, .{ "ws", HardcodedModule.@"ws" }, }, @@ -2074,6 +2077,7 @@ pub const HardcodedModule = enum { .{ "node:timers/promises", "node:timers/promises" }, .{ "node:tty", "node:tty" }, .{ "node:url", "node:url" }, + .{ "node:util", "node:util" }, .{ "os", "node:os" }, .{ "path", "node:path" }, .{ "path/posix", "node:path/posix" }, @@ -2092,6 +2096,7 @@ pub const HardcodedModule = enum { .{ "tty", "node:tty" }, .{ "undici", "undici" }, .{ "url", "node:url" }, + .{ "util", "node:util" }, .{ "ws", "ws" }, .{ "ws/lib/websocket", "ws" }, }, diff --git a/test/bun.js/test-util-types.test.js b/test/bun.js/test-util-types.test.js new file mode 100644 index 000000000..ce10713e4 --- /dev/null +++ b/test/bun.js/test-util-types.test.js @@ -0,0 +1,281 @@ +const assert = require("assert"); +import { test, expect } from "bun:test"; +const types = require("util/types"); + +function inspect(val) { + return Bun.inspect(val); +} + +for (const [value, _method] of [ + [new Date()], + [ + (function () { + return arguments; + })(), + "isArgumentsObject", + ], + [new Boolean(), "isBooleanObject"], + [new Number(), "isNumberObject"], + [new String(), "isStringObject"], + [Object(Symbol()), "isSymbolObject"], + [Object(BigInt(0)), "isBigIntObject"], + [new Error(), "isNativeError"], + [new RegExp()], + [async function () {}, "isAsyncFunction"], + [function* () {}, "isGeneratorFunction"], + [(function* () {})(), "isGeneratorObject"], + [Promise.resolve()], + [new Map()], + [new Set()], + [new Map()[Symbol.iterator](), "isMapIterator"], + [new Set()[Symbol.iterator](), "isSetIterator"], + [new WeakMap()], + [new WeakSet()], + [new ArrayBuffer()], + [new Uint8Array()], + [new Uint8ClampedArray()], + [new Uint16Array()], + [new Uint32Array()], + [new Int8Array()], + [new Int16Array()], + [new Int32Array()], + [new Float32Array()], + [new Float64Array()], + [new BigInt64Array()], + [new BigUint64Array()], + [new DataView(new ArrayBuffer())], + [new SharedArrayBuffer()], + [new Proxy({}, {}), "isProxy"], +]) { + const method = _method || `is${value.constructor.name}`; + test(method, () => { + assert(method in types, `Missing ${method} for ${inspect(value)}`); + assert(types[method](value), `Want ${inspect(value)} to match ${method}`); + + for (const key of Object.keys(types)) { + if ( + ((types.isArrayBufferView(value) || types.isAnyArrayBuffer(value)) && + key.includes("Array")) || + key === "isBoxedPrimitive" + ) { + continue; + } + + expect(types[key](value)).toBe(key === method); + } + }); +} + +// Check boxed primitives. +test("isBoxedPrimitive", () => { + [ + new Boolean(), + new Number(), + new String(), + Object(Symbol()), + Object(BigInt(0)), + ].forEach((entry) => assert(types.isBoxedPrimitive(entry))); +}); + +{ + const primitive = true; + const arrayBuffer = new ArrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + const dataView = new DataView(arrayBuffer); + const uint8Array = new Uint8Array(arrayBuffer); + const uint8ClampedArray = new Uint8ClampedArray(arrayBuffer); + const uint16Array = new Uint16Array(arrayBuffer); + const uint32Array = new Uint32Array(arrayBuffer); + const int8Array = new Int8Array(arrayBuffer); + const int16Array = new Int16Array(arrayBuffer); + const int32Array = new Int32Array(arrayBuffer); + const float32Array = new Float32Array(arrayBuffer); + const float64Array = new Float64Array(arrayBuffer); + const bigInt64Array = new BigInt64Array(arrayBuffer); + const bigUint64Array = new BigUint64Array(arrayBuffer); + + const fakeBuffer = Object.create(Buffer.prototype); + const fakeDataView = Object.create(DataView.prototype); + const fakeUint8Array = Object.create(Uint8Array.prototype); + const fakeUint8ClampedArray = Object.create(Uint8ClampedArray.prototype); + const fakeUint16Array = Object.create(Uint16Array.prototype); + const fakeUint32Array = Object.create(Uint32Array.prototype); + const fakeInt8Array = Object.create(Int8Array.prototype); + const fakeInt16Array = Object.create(Int16Array.prototype); + const fakeInt32Array = Object.create(Int32Array.prototype); + const fakeFloat32Array = Object.create(Float32Array.prototype); + const fakeFloat64Array = Object.create(Float64Array.prototype); + const fakeBigInt64Array = Object.create(BigInt64Array.prototype); + const fakeBigUint64Array = Object.create(BigUint64Array.prototype); + + const stealthyDataView = Object.setPrototypeOf( + new DataView(arrayBuffer), + Uint8Array.prototype, + ); + const stealthyUint8Array = Object.setPrototypeOf( + new Uint8Array(arrayBuffer), + ArrayBuffer.prototype, + ); + const stealthyUint8ClampedArray = Object.setPrototypeOf( + new Uint8ClampedArray(arrayBuffer), + ArrayBuffer.prototype, + ); + const stealthyUint16Array = Object.setPrototypeOf( + new Uint16Array(arrayBuffer), + Uint16Array.prototype, + ); + const stealthyUint32Array = Object.setPrototypeOf( + new Uint32Array(arrayBuffer), + Uint32Array.prototype, + ); + const stealthyInt8Array = Object.setPrototypeOf( + new Int8Array(arrayBuffer), + Int8Array.prototype, + ); + const stealthyInt16Array = Object.setPrototypeOf( + new Int16Array(arrayBuffer), + Int16Array.prototype, + ); + const stealthyInt32Array = Object.setPrototypeOf( + new Int32Array(arrayBuffer), + Int32Array.prototype, + ); + const stealthyFloat32Array = Object.setPrototypeOf( + new Float32Array(arrayBuffer), + Float32Array.prototype, + ); + const stealthyFloat64Array = Object.setPrototypeOf( + new Float64Array(arrayBuffer), + Float64Array.prototype, + ); + const stealthyBigInt64Array = Object.setPrototypeOf( + new BigInt64Array(arrayBuffer), + BigInt64Array.prototype, + ); + const stealthyBigUint64Array = Object.setPrototypeOf( + new BigUint64Array(arrayBuffer), + BigUint64Array.prototype, + ); + + const all = [ + primitive, + arrayBuffer, + buffer, + fakeBuffer, + dataView, + fakeDataView, + stealthyDataView, + uint8Array, + fakeUint8Array, + stealthyUint8Array, + uint8ClampedArray, + fakeUint8ClampedArray, + stealthyUint8ClampedArray, + uint16Array, + fakeUint16Array, + stealthyUint16Array, + uint32Array, + fakeUint32Array, + stealthyUint32Array, + int8Array, + fakeInt8Array, + stealthyInt8Array, + int16Array, + fakeInt16Array, + stealthyInt16Array, + int32Array, + fakeInt32Array, + stealthyInt32Array, + float32Array, + fakeFloat32Array, + stealthyFloat32Array, + float64Array, + fakeFloat64Array, + stealthyFloat64Array, + bigInt64Array, + fakeBigInt64Array, + stealthyBigInt64Array, + bigUint64Array, + fakeBigUint64Array, + stealthyBigUint64Array, + ]; + + const expected = { + isArrayBufferView: [ + buffer, + dataView, + stealthyDataView, + uint8Array, + stealthyUint8Array, + uint8ClampedArray, + stealthyUint8ClampedArray, + uint16Array, + stealthyUint16Array, + uint32Array, + stealthyUint32Array, + int8Array, + stealthyInt8Array, + int16Array, + stealthyInt16Array, + int32Array, + stealthyInt32Array, + float32Array, + stealthyFloat32Array, + float64Array, + stealthyFloat64Array, + bigInt64Array, + stealthyBigInt64Array, + bigUint64Array, + stealthyBigUint64Array, + ], + isTypedArray: [ + buffer, + uint8Array, + stealthyUint8Array, + uint8ClampedArray, + stealthyUint8ClampedArray, + uint16Array, + stealthyUint16Array, + uint32Array, + stealthyUint32Array, + int8Array, + stealthyInt8Array, + int16Array, + stealthyInt16Array, + int32Array, + stealthyInt32Array, + float32Array, + stealthyFloat32Array, + float64Array, + stealthyFloat64Array, + bigInt64Array, + stealthyBigInt64Array, + bigUint64Array, + stealthyBigUint64Array, + ], + isUint8Array: [buffer, uint8Array, stealthyUint8Array], + isUint8ClampedArray: [uint8ClampedArray, stealthyUint8ClampedArray], + isUint16Array: [uint16Array, stealthyUint16Array], + isUint32Array: [uint32Array, stealthyUint32Array], + isInt8Array: [int8Array, stealthyInt8Array], + isInt16Array: [int16Array, stealthyInt16Array], + isInt32Array: [int32Array, stealthyInt32Array], + isFloat32Array: [float32Array, stealthyFloat32Array], + isFloat64Array: [float64Array, stealthyFloat64Array], + isBigInt64Array: [bigInt64Array, stealthyBigInt64Array], + isBigUint64Array: [bigUint64Array, stealthyBigUint64Array], + }; + + for (const testedFunc of Object.keys(expected)) { + test(testedFunc, () => { + const func = types[testedFunc]; + const yup = []; + for (const value of all) { + if (func(value)) { + yup.push(value); + } + } + expect(yup).toEqual(expected[testedFunc]); + }); + } +} |