From 305fad9f86d372422e7db5531e2ae87a3e570030 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 26 Sep 2024 00:24:40 -0700 Subject: [PATCH] node: implement more validators in native code --- src/bun.js/bindings/BunProcess.cpp | 12 +- src/bun.js/bindings/BunProcess.h | 4 +- src/bun.js/bindings/ErrorCode.cpp | 33 ++- src/bun.js/bindings/ErrorCode.h | 6 +- src/bun.js/bindings/ErrorCode.ts | 1 + src/bun.js/bindings/NodeValidator.cpp | 285 +++++++++++++++++++++- src/bun.js/bindings/NodeValidator.h | 16 +- src/js/builtins/ProcessObjectInternals.ts | 8 +- src/js/internal/validators.ts | 11 + src/js/node/child_process.ts | 68 +----- src/js/node/dgram.ts | 55 +---- src/js/node/diagnostics_channel.ts | 11 +- src/js/node/events.ts | 26 +- src/js/node/http.ts | 8 +- src/js/node/readline.ts | 135 +--------- src/js/node/stream.ts | 186 ++------------ src/js/node/timers.promises.ts | 15 +- src/js/node/tty.ts | 8 +- 18 files changed, 410 insertions(+), 478 deletions(-) diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 354036653d281..9f9af4acff0fd 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -382,7 +382,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, return JSValue::encode(jsUndefined()); } - JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject * globalObject, + JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue exports); #if OS(WINDOWS) #define dlsym GetProcAddress @@ -688,6 +688,16 @@ static const NeverDestroyed signalNames[] = { MAKE_STATIC_STRING_IMPL("SIGSYS"), }; +bool isSignalName(WTF::String input) +{ + for (auto i = 0; i < std::size(signalNames); i++) { + if (input == signalNames[i]) { + return true; + } + } + return false; +} + static void loadSignalNumberMap() { diff --git a/src/bun.js/bindings/BunProcess.h b/src/bun.js/bindings/BunProcess.h index 718369852521b..368d93ae8b32f 100644 --- a/src/bun.js/bindings/BunProcess.h +++ b/src/bun.js/bindings/BunProcess.h @@ -104,4 +104,6 @@ class Process : public WebCore::JSEventEmitter { inline JSObject* bindingNatives() { return m_bindingNatives.getInitializedOnMainThread(this); } }; -} // namespace Bun \ No newline at end of file +bool isSignalName(WTF::String input); + +} // namespace Bun diff --git a/src/bun.js/bindings/ErrorCode.cpp b/src/bun.js/bindings/ErrorCode.cpp index 08d06859980f1..85f0829825e7c 100644 --- a/src/bun.js/bindings/ErrorCode.cpp +++ b/src/bun.js/bindings/ErrorCode.cpp @@ -416,7 +416,7 @@ JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObjec return {}; } -JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral name, JSC::JSValue value, ASCIILiteral reason) +JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral name, JSC::JSValue value, WTF::String reason) { ASCIILiteral type; { @@ -433,6 +433,18 @@ JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobal throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_VALUE, message)); return {}; } +JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue name, JSC::JSValue value, WTF::String reason) +{ + auto name_string = JSValueToStringSafe(globalObject, name); + RETURN_IF_EXCEPTION(throwScope, {}); + + auto value_string = JSValueToStringSafe(globalObject, value); + RETURN_IF_EXCEPTION(throwScope, {}); + + auto message = makeString("The argument '"_s, name_string, "' "_s, reason, ". Received "_s, value_string); + throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_INVALID_ARG_VALUE, message)); + return {}; +} JSC::EncodedJSValue UNKNOWN_ENCODING(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue encoding) { @@ -464,16 +476,31 @@ JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlo return {}; } -JSC::EncodedJSValue UNKNOWN_SIGNAL(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue signal) +JSC::EncodedJSValue UNKNOWN_SIGNAL(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue signal, bool triedUppercase) { auto signal_string = JSValueToStringSafe(globalObject, signal); RETURN_IF_EXCEPTION(throwScope, {}); - auto message = makeString("Unknown signal: "_s, signal_string); + auto message_extra = triedUppercase ? " (signals must use all capital letters)"_s : ""_s; + auto message = makeString("Unknown signal: "_s, signal_string, message_extra); throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_UNKNOWN_SIGNAL, message)); return {}; } +JSC::EncodedJSValue SOCKET_BAD_PORT(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue name, JSC::JSValue port, bool allowZero) +{ + ASCIILiteral op = allowZero ? ">="_s : ">"_s; + + auto name_string = JSValueToStringSafe(globalObject, name); + RETURN_IF_EXCEPTION(throwScope, {}); + auto port_string = JSValueToStringSafe(globalObject, port); + RETURN_IF_EXCEPTION(throwScope, {}); + + auto message = makeString(name_string, " should be "_s, op, " 0 and < 65536. Received "_s, port_string); + throwScope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_SOCKET_BAD_PORT, message)); + return {}; +} + } static JSC::JSValue ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue arg0, JSValue arg1, JSValue arg2) diff --git a/src/bun.js/bindings/ErrorCode.h b/src/bun.js/bindings/ErrorCode.h index 609050b36c0c0..d55119ec4b0e4 100644 --- a/src/bun.js/bindings/ErrorCode.h +++ b/src/bun.js/bindings/ErrorCode.h @@ -81,12 +81,14 @@ JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObjec JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name_val, size_t bound_num, Bound bound, JSC::JSValue actual); JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue arg_name_val, ASCIILiteral msg, JSC::JSValue actual); JSC::EncodedJSValue OUT_OF_RANGE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral arg_name_val, ASCIILiteral msg, JSC::JSValue actual); -JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral name, JSC::JSValue value, ASCIILiteral reason = "is invalid"_s); +JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral name, JSC::JSValue value, WTF::String reason = "is invalid"_s); +JSC::EncodedJSValue INVALID_ARG_VALUE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue name, JSC::JSValue value, WTF::String reason = "is invalid"_s); JSC::EncodedJSValue UNKNOWN_ENCODING(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue encoding); JSC::EncodedJSValue INVALID_STATE(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, ASCIILiteral statemsg); JSC::EncodedJSValue STRING_TOO_LONG(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject); JSC::EncodedJSValue BUFFER_OUT_OF_BOUNDS(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject); -JSC::EncodedJSValue UNKNOWN_SIGNAL(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue signal); +JSC::EncodedJSValue UNKNOWN_SIGNAL(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue signal, bool triedUppercase = false); +JSC::EncodedJSValue SOCKET_BAD_PORT(JSC::ThrowScope& throwScope, JSC::JSGlobalObject* globalObject, JSC::JSValue name, JSC::JSValue port, bool allowZero); } diff --git a/src/bun.js/bindings/ErrorCode.ts b/src/bun.js/bindings/ErrorCode.ts index 549225769e8c5..53b1796144a92 100644 --- a/src/bun.js/bindings/ErrorCode.ts +++ b/src/bun.js/bindings/ErrorCode.ts @@ -47,6 +47,7 @@ export default [ ["ERR_INVALID_STATE", Error, "Error"], ["ERR_BUFFER_OUT_OF_BOUNDS", RangeError, "RangeError"], ["ERR_UNKNOWN_SIGNAL", TypeError, "TypeError"], + ["ERR_SOCKET_BAD_PORT", RangeError, "RangeError"], // Bun-specific ["ERR_FORMDATA_PARSE_ERROR", TypeError, "TypeError"], diff --git a/src/bun.js/bindings/NodeValidator.cpp b/src/bun.js/bindings/NodeValidator.cpp index a3e4af5f05d60..60d786b716db6 100644 --- a/src/bun.js/bindings/NodeValidator.cpp +++ b/src/bun.js/bindings/NodeValidator.cpp @@ -6,9 +6,14 @@ #include "JavaScriptCore/ExceptionScope.h" #include "JavaScriptCore/CallData.h" #include "JavaScriptCore/JSObjectInlines.h" +#include "JavaScriptCore/JSType.h" +#include "JavaScriptCore/TypedArrayType.h" +#include "JavaScriptCore/ArrayConstructor.h" #include #include +#include "JSBufferEncodingType.h" +#include "BunProcess.h" #include "ErrorCode.h" #include "NodeValidator.h" @@ -52,9 +57,9 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateNumber, (JSC::JSGlobalObject * globa auto name = callFrame->argument(1); auto min = callFrame->argument(2); auto max = callFrame->argument(3); - return Bun::validateNumber(scope, globalObject, value, name, min, max); + return Bun::V::validateNumber(scope, globalObject, value, name, min, max); } -JSC::EncodedJSValue validateNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name, JSValue min, JSValue max) +JSC::EncodedJSValue V::validateNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name, JSValue min, JSValue max) { if (!value.isNumber()) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "number"_s, value); @@ -84,7 +89,10 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateString, (JSC::JSGlobalObject * globa auto value = callFrame->argument(0); auto name = callFrame->argument(1); - + return V::validateString(scope, globalObject, value, name); +} +JSC::EncodedJSValue V::validateString(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name) +{ if (!value.isString()) { return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "string"_s, value); } @@ -98,9 +106,9 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateFiniteNumber, (JSC::JSGlobalObject * auto number = callFrame->argument(0); auto name = callFrame->argument(1); - return Bun::validateFiniteNumber(scope, globalObject, number, name); + return Bun::V::validateFiniteNumber(scope, globalObject, number, name); } -JSC::EncodedJSValue validateFiniteNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue number, JSValue name) +JSC::EncodedJSValue V::validateFiniteNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue number, JSValue name) { if (number.isUndefined()) { return JSValue::encode(jsBoolean(false)); @@ -112,7 +120,7 @@ JSC::EncodedJSValue validateFiniteNumber(JSC::ThrowScope& scope, JSC::JSGlobalOb return JSValue::encode(jsBoolean(false)); } - Bun::validateNumber(scope, globalObject, number, name, jsUndefined(), jsUndefined()); + Bun::V::validateNumber(scope, globalObject, number, name, jsUndefined(), jsUndefined()); RETURN_IF_EXCEPTION(scope, {}); return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, "a finite number"_s, number); @@ -129,7 +137,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_checkRangesOrGetDefault, (JSC::JSGlobalObjec auto upper = callFrame->argument(3); auto def = callFrame->argument(4); - auto finite = Bun::validateFiniteNumber(scope, globalObject, number, name); + auto finite = Bun::V::validateFiniteNumber(scope, globalObject, number, name); RETURN_IF_EXCEPTION(scope, {}); auto finite_real = JSValue::decode(finite).asBoolean(); if (!finite_real) { @@ -163,4 +171,267 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateFunction, (JSC::JSGlobalObject * glo return JSValue::encode(jsUndefined()); } +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateBoolean, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto value = callFrame->argument(0); + auto name = callFrame->argument(1); + + if (!value.isBoolean()) { + return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "boolean"_s, value); + } + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validatePort, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto port = callFrame->argument(0); + auto name = callFrame->argument(1); + auto allowZero = callFrame->argument(2); + + if (name.isUndefined()) name = jsString(vm, String("Port"_s)); + if (allowZero.isUndefined()) allowZero = jsBoolean(true); + + auto allowZero_b = allowZero.toBoolean(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (!port.isNumber() && !port.isString()) return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); + + if (port.isString()) { + auto port_str = port.getString(globalObject); + auto trimmed = port_str.trim([](auto c) { + // https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.trim + // The definition of white space is the union of *WhiteSpace* and *LineTerminator*. + + // WhiteSpace :: + if (c == 0x0009) return true; // + if (c == 0x000B) return true; // + if (c == 0x000C) return true; // + if (c == 0xFEFF) return true; // + // + // any code point in general category “Space_Separator” + // ranges accurate as of unicode 16.0.0 + if (c >= 0x0009 && c <= 0x000D) return true; + if (c >= 0x0020 && c <= 0x0020) return true; + if (c >= 0x0085 && c <= 0x0085) return true; + if (c >= 0x00A0 && c <= 0x00A0) return true; + if (c >= 0x1680 && c <= 0x1680) return true; + if (c >= 0x2000 && c <= 0x200A) return true; + if (c >= 0x2028 && c <= 0x2028) return true; + if (c >= 0x2029 && c <= 0x2029) return true; + if (c >= 0x202F && c <= 0x202F) return true; + if (c >= 0x205F && c <= 0x205F) return true; + + // LineTerminator :: + if (c == 0x000A) return true; // + if (c == 0x000D) return true; // + if (c == 0x2028) return true; // + if (c == 0x2029) return true; // + + return false; + }); + if (trimmed.length() == 0) { + return Bun::ERR::SOCKET_BAD_PORT(scope, globalObject, name, port, allowZero_b); + } + } + + 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); + + return JSValue::encode(port); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateAbortSignal, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto signal = callFrame->argument(0); + auto name = callFrame->argument(1); + + if (!signal.isUndefined()) { + if (signal.isNull()) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "AbortSignal"_s, signal); + if (!signal.isObject()) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "AbortSignal"_s, signal); + + auto propin = signal.getObject()->hasOwnProperty(globalObject, Identifier::fromString(vm, "aborted"_s)); + RETURN_IF_EXCEPTION(scope, {}); + if (!propin) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "AbortSignal"_s, signal); + } + + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateArray, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto value = callFrame->argument(0); + auto name = callFrame->argument(1); + auto minLength = callFrame->argument(2); + + if (minLength.isUndefined()) minLength = jsNumber(0); + + if (!JSC::isArray(globalObject, value)) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "Array"_s, value); + + auto length = value.get(globalObject, Identifier::fromString(vm, "length"_s)); + RETURN_IF_EXCEPTION(scope, {}); + auto length_num = length.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + auto minLength_num = minLength.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + if (length_num < minLength_num) { + return Bun::ERR::INVALID_ARG_VALUE(scope, globalObject, name, value, makeString("must be longer than "_s, minLength_num)); + } + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateInt32, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto value = callFrame->argument(0); + auto name = callFrame->argument(1); + auto min = callFrame->argument(2); + auto max = callFrame->argument(3); + + if (!value.isNumber()) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "number"_s, value); + if (min.isUndefined()) min = jsNumber(std::numeric_limits().min()); + if (max.isUndefined()) max = jsNumber(std::numeric_limits().max()); + + auto value_num = value.asNumber(); + auto min_num = min.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + auto max_num = max.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + + if (std::fmod(value_num, 1.0) != 0) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, "an integer"_s, value); + if (value_num < min_num || value_num > max_num) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, min_num, max_num, value); + + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateUint32, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto value = callFrame->argument(0); + auto name = callFrame->argument(1); + auto positive = callFrame->argument(2); + + if (!value.isNumber()) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "number"_s, value); + if (positive.isUndefined()) positive = jsBoolean(false); + + auto value_num = value.asNumber(); + if (std::fmod(value_num, 1.0) != 0) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, "an integer"_s, value); + + auto positive_b = positive.toBoolean(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + auto min = positive_b ? 1 : 0; + auto max = std::numeric_limits().max(); + if (value_num < min || value_num > max) return Bun::ERR::OUT_OF_RANGE(scope, globalObject, name, min, max, value); + + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateSignalName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto signal = callFrame->argument(0); + auto name = callFrame->argument(1); + + if (name.isUndefined()) name = jsString(vm, String("signal"_s)); + + V::validateString(scope, globalObject, signal, name); + RETURN_IF_EXCEPTION(scope, {}); + + auto signal_str = signal.getString(globalObject); + if (isSignalName(signal_str)) return JSValue::encode(jsUndefined()); + + auto signal_upper = signal_str.convertToUppercaseWithoutLocale(); + RETURN_IF_EXCEPTION(scope, {}); + if (isSignalName(signal_str)) return Bun::ERR::UNKNOWN_SIGNAL(scope, globalObject, signal, true); + return Bun::ERR::UNKNOWN_SIGNAL(scope, globalObject, signal); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateEncoding, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto data = callFrame->argument(0); + auto encoding = callFrame->argument(1); + + auto normalized = WebCore::parseEnumeration(*globalObject, encoding); + auto length = data.get(globalObject, Identifier::fromString(vm, "length"_s)); + RETURN_IF_EXCEPTION(scope, {}); + auto length_num = length.toNumber(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + if (normalized == BufferEncodingType::hex && std::fmod(length_num, 2.0) != 0) { + return Bun::ERR::INVALID_ARG_VALUE(scope, globalObject, "encoding"_s, encoding, makeString("is invalid for data of length "_s, length_num)); + } + // no check for ERR_UNKNOWN_ENCODING ? it's not in node but feels apt here + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validatePlainFunction, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto value = callFrame->argument(0); + auto name = callFrame->argument(0); + + if (!value.isCallable()) { + return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "function"_s, value); + } + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateUndefined, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto value = callFrame->argument(0); + auto name = callFrame->argument(1); + + if (!value.isUndefined()) return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "undefined"_s, value); + + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateBuffer, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto buffer = callFrame->argument(0); + auto name = callFrame->argument(0); + + if (!buffer.isCell()) return JSValue::encode(jsUndefined()); + auto ty = buffer.asCell()->type(); + + if (JSC::typedArrayType(ty) == NotTypedArray) { + return Bun::ERR::INVALID_ARG_TYPE(scope, globalObject, name, "Buffer, TypedArray, or DataView"_s, buffer, true); + } + return JSValue::encode(jsUndefined()); +} + } diff --git a/src/bun.js/bindings/NodeValidator.h b/src/bun.js/bindings/NodeValidator.h index 2c8578da1bf4b..837ecf763f180 100644 --- a/src/bun.js/bindings/NodeValidator.h +++ b/src/bun.js/bindings/NodeValidator.h @@ -12,10 +12,24 @@ JSC_DEFINE_HOST_FUNCTION(jsFunction_validateString, (JSC::JSGlobalObject * globa JSC_DEFINE_HOST_FUNCTION(jsFunction_validateFiniteNumber, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); JSC_DEFINE_HOST_FUNCTION(jsFunction_checkRangesOrGetDefault, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); JSC_DEFINE_HOST_FUNCTION(jsFunction_validateFunction, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateBoolean, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validatePort, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateAbortSignal, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateArray, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateInt32, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateUint32, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateSignalName, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateEncoding, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validatePlainFunction, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateUndefined, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); +JSC_DEFINE_HOST_FUNCTION(jsFunction_validateBuffer, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)); + +namespace V { JSC::EncodedJSValue validateNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue value, JSC::JSValue name, JSC::JSValue min, JSC::JSValue max); JSC::EncodedJSValue validateFiniteNumber(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSC::JSValue number, JSC::JSValue name); +JSC::EncodedJSValue validateString(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue value, JSValue name); } -// validateFunction +} diff --git a/src/js/builtins/ProcessObjectInternals.ts b/src/js/builtins/ProcessObjectInternals.ts index ec6cae506f8b2..0cd8917744dd3 100644 --- a/src/js/builtins/ProcessObjectInternals.ts +++ b/src/js/builtins/ProcessObjectInternals.ts @@ -244,13 +244,7 @@ export function initializeNextTickQueue(process, nextTickQueue, drainMicrotasksF var drainMicrotasks = drainMicrotasksFn; var reportUncaughtException = reportUncaughtExceptionFn; - function validateFunction(cb) { - if (typeof cb !== "function") { - const err = new TypeError(`The "callback" argument must be of type "function". Received type ${typeof cb}`); - err.code = "ERR_INVALID_ARG_TYPE"; - throw err; - } - } + const { validateFunction } = require("internal/validators"); var setup; setup = () => { diff --git a/src/js/internal/validators.ts b/src/js/internal/validators.ts index a626c360d15b7..a19d5c315522e 100644 --- a/src/js/internal/validators.ts +++ b/src/js/internal/validators.ts @@ -5,4 +5,15 @@ export default { validateFiniteNumber: $newCppFunction("NodeValidator.cpp", "jsFunction_validateFiniteNumber", 0), checkRangesOrGetDefault: $newCppFunction("NodeValidator.cpp", "jsFunction_checkRangesOrGetDefault", 0), validateFunction: $newCppFunction("NodeValidator.cpp", "jsFunction_validateFunction", 0), + validateBoolean: $newCppFunction("NodeValidator.cpp", "jsFunction_validateBoolean", 0), + validatePort: $newCppFunction("NodeValidator.cpp", "jsFunction_validatePort", 0), + validateAbortSignal: $newCppFunction("NodeValidator.cpp", "jsFunction_validateAbortSignal", 0), + validateArray: $newCppFunction("NodeValidator.cpp", "jsFunction_validateArray", 0), + validateInt32: $newCppFunction("NodeValidator.cpp", "jsFunction_validateInt32", 0), + validateUint32: $newCppFunction("NodeValidator.cpp", "jsFunction_validateUint32", 0), + validateSignalName: $newCppFunction("NodeValidator.cpp", "jsFunction_validateSignalName", 0), + validateEncoding: $newCppFunction("NodeValidator.cpp", "jsFunction_validateEncoding", 0), + validatePlainFunction: $newCppFunction("NodeValidator.cpp", "jsFunction_validatePlainFunction", 0), + validateUndefined: $newCppFunction("NodeValidator.cpp", "jsFunction_validateUndefined", 0), + validateBuffer: $newCppFunction("NodeValidator.cpp", "jsFunction_validateBuffer", 0), }; diff --git a/src/js/node/child_process.ts b/src/js/node/child_process.ts index 655272b80e146..c92d5f8d02cc6 100644 --- a/src/js/node/child_process.ts +++ b/src/js/node/child_process.ts @@ -4,6 +4,13 @@ const StreamModule = require("node:stream"); const OsModule = require("node:os"); const { ERR_INVALID_ARG_TYPE, ERR_IPC_DISCONNECTED } = require("internal/errors"); const { kHandle } = require("internal/shared"); +const { + validateBoolean, + validateFunction, + validateString, + validateAbortSignal, + validateArray, +} = require("internal/validators"); var NetModule; @@ -1585,35 +1592,6 @@ function validateTimeout(timeout) { } } -function validateBoolean(value, name) { - if (typeof value !== "boolean") throw ERR_INVALID_ARG_TYPE(name, "boolean", value); -} - -/** - * @callback validateFunction - * @param {*} value - * @param {string} name - * @returns {asserts value is Function} - */ - -/** @type {validateFunction} */ -function validateFunction(value, name) { - if (typeof value !== "function") throw ERR_INVALID_ARG_TYPE(name, "Function", value); -} - -/** - * @callback validateAbortSignal - * @param {*} signal - * @param {string} name - */ - -/** @type {validateAbortSignal} */ -const validateAbortSignal = (signal, name) => { - if (signal !== undefined && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); - } -}; - /** * @callback validateOneOf * @template T @@ -1661,38 +1639,6 @@ const validateObject = (value, name, options = null) => { } }; -/** - * @callback validateArray - * @param {*} value - * @param {string} name - * @param {number} [minLength] - * @returns {asserts value is any[]} - */ - -/** @type {validateArray} */ -const validateArray = (value, name, minLength = 0) => { - // const validateArray = hideStackFrames((value, name, minLength = 0) => { - if (!$isJSArray(value)) { - throw ERR_INVALID_ARG_TYPE(name, "Array", value); - } - if (value.length < minLength) { - const reason = `must be longer than ${minLength}`; - throw ERR_INVALID_ARG_VALUE(name, value, reason); - } -}; - -/** - * @callback validateString - * @param {*} value - * @param {string} name - * @returns {asserts value is string} - */ - -/** @type {validateString} */ -function validateString(value, name) { - if (typeof value !== "string") throw ERR_INVALID_ARG_TYPE(name, "string", value); -} - function isInt32(value) { return value === (value | 0); } diff --git a/src/js/node/dgram.ts b/src/js/node/dgram.ts index 4093197f11794..b83c64bafdc43 100644 --- a/src/js/node/dgram.ts +++ b/src/js/node/dgram.ts @@ -35,6 +35,13 @@ const async_id_symbol = Symbol("async_id_symbol"); const { hideFromStack, throwNotImplemented } = require("internal/shared"); const { ERR_SOCKET_BAD_TYPE } = require("internal/errors"); +const { + validateString, + validateNumber, + validateFunction, + validatePort, + validateAbortSignal, +} = require("internal/validators"); const { FunctionPrototypeBind, @@ -128,54 +135,6 @@ function isInt32(value) { return value === (value | 0); } -function validateAbortSignal(signal, name) { - if (signal !== undefined && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); - } -} -hideFromStack(validateAbortSignal); - -function validateString(value, name) { - if (typeof value !== "string") throw new ERR_INVALID_ARG_TYPE(name, "string", value); -} -hideFromStack(validateString); - -function validateNumber(value, name, min?, max?) { - if (typeof value !== "number") throw new ERR_INVALID_ARG_TYPE(name, "number", value); - - if ( - (min != null && value < min) || - (max != null && value > max) || - ((min != null || max != null) && NumberIsNaN(value)) - ) { - throw new ERR_OUT_OF_RANGE( - name, - `${min != null ? `>= ${min}` : ""}${min != null && max != null ? " && " : ""}${max != null ? `<= ${max}` : ""}`, - value, - ); - } -} -hideFromStack(validateNumber); - -function validatePort(port, name = "Port", allowZero = true) { - if ( - (typeof port !== "number" && typeof port !== "string") || - (typeof port === "string" && StringPrototypeTrim(port).length === 0) || - +port !== +port >>> 0 || - port > 0xffff || - (port === 0 && !allowZero) - ) { - throw new ERR_SOCKET_BAD_PORT(name, port, allowZero); - } - return port | 0; -} -hideFromStack(validatePort); - -function validateFunction(value, name) { - if (typeof value !== "function") throw new ERR_INVALID_ARG_TYPE(name, "Function", value); -} -hideFromStack(validateFunction); - // placeholder function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { return block.$apply(null, args); diff --git a/src/js/node/diagnostics_channel.ts b/src/js/node/diagnostics_channel.ts index 0b6a0e39d747b..3a533b97e78eb 100644 --- a/src/js/node/diagnostics_channel.ts +++ b/src/js/node/diagnostics_channel.ts @@ -1,5 +1,8 @@ // Hardcoded module "node:diagnostics_channel" // Reference: https://github.com/nodejs/node/blob/fb47afc335ef78a8cef7eac52b8ee7f045300696/lib/diagnostics_channel.js + +const { validateFunction } = require("internal/validators"); + const SafeMap = Map; const SafeFinalizationRegistry = FinalizationRegistry; @@ -396,14 +399,6 @@ class ERR_INVALID_ARG_TYPE extends TypeError { } } -function validateFunction(callable, field) { - if (typeof callable !== "function") { - throw new ERR_INVALID_ARG_TYPE(field, "Function", callable); - } - - return callable; -} - export default { channel, hasSubscribers, diff --git a/src/js/node/events.ts b/src/js/node/events.ts index ca9cce2d3bc5e..c3a5fc14268d0 100644 --- a/src/js/node/events.ts +++ b/src/js/node/events.ts @@ -2,6 +2,7 @@ // Reference: https://github.com/nodejs/node/blob/main/lib/events.js const { throwNotImplemented } = require("internal/shared"); +const { validateAbortSignal, validateNumber, validateBoolean } = require("internal/validators"); const SymbolFor = Symbol.for; @@ -502,37 +503,12 @@ function ERR_OUT_OF_RANGE(name, range, value) { return err; } -function validateAbortSignal(signal, name) { - if (signal !== undefined && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); - } -} - -function validateNumber(value, name, min?: number, max?: number) { - if (typeof value !== "number") throw ERR_INVALID_ARG_TYPE(name, "number", value); - if ( - (min != null && value < min) || - (max != null && value > max) || - ((min != null || max != null) && Number.isNaN(value)) - ) { - throw ERR_OUT_OF_RANGE( - name, - `${min != null ? `>= ${min}` : ""}${min != null && max != null ? " && " : ""}${max != null ? `<= ${max}` : ""}`, - value, - ); - } -} - function checkListener(listener) { if (typeof listener !== "function") { throw new TypeError("The listener must be a function"); } } -function validateBoolean(value, name) { - if (typeof value !== "boolean") throw ERR_INVALID_ARG_TYPE(name, "boolean", value); -} - let AsyncResource = null; const eventTargetMaxListenersSymbol = Symbol("EventTarget.maxListeners"); diff --git a/src/js/node/http.ts b/src/js/node/http.ts index 01fc3702796bb..a0be75f7343b2 100644 --- a/src/js/node/http.ts +++ b/src/js/node/http.ts @@ -6,6 +6,7 @@ const { ERR_INVALID_ARG_TYPE, ERR_INVALID_PROTOCOL } = require("internal/errors" const { isPrimary } = require("internal/cluster/isPrimary"); const { kAutoDestroyed } = require("internal/shared"); const { urlToHttpOptions } = require("internal/url"); +const { validateFunction } = require("internal/validators"); const { getHeader, @@ -131,13 +132,6 @@ function validateMsecs(numberlike: any, field: string) { return numberlike; } -function validateFunction(callable: any, field: string) { - if (typeof callable !== "function") { - throw ERR_INVALID_ARG_TYPE(field, "Function", callable); - } - - return callable; -} type FakeSocket = InstanceType; var FakeSocket = class Socket extends Duplex { diff --git a/src/js/node/readline.ts b/src/js/node/readline.ts index 0c9ae8b2d0c0b..9db2708f43ead 100644 --- a/src/js/node/readline.ts +++ b/src/js/node/readline.ts @@ -27,7 +27,19 @@ // ---------------------------------------------------------------------------- const EventEmitter = require("node:events"); const { StringDecoder } = require("node:string_decoder"); + +const { + validateFunction, + validateAbortSignal, + validateArray, + validateString, + validateBoolean, + validateInteger, + validateUint32, +} = require("internal/validators"); + const internalGetStringWidth = $newZigFunction("string.zig", "String.jsGetStringWidth", 1); + const ObjectGetPrototypeOf = Object.getPrototypeOf; const ObjectGetOwnPropertyDescriptors = Object.getOwnPropertyDescriptors; const ObjectValues = Object.values; @@ -298,129 +310,6 @@ class AbortError extends Error { } } -// Validators - -/** - * @callback validateFunction - * @param {*} value - * @param {string} name - * @returns {asserts value is Function} - */ -function validateFunction(value, name) { - if (typeof value !== "function") throw $ERR_INVALID_ARG_TYPE(name, "Function", value); -} - -/** - * @callback validateAbortSignal - * @param {*} signal - * @param {string} name - */ -function validateAbortSignal(signal, name) { - if (signal !== undefined && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw $ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); - } -} - -/** - * @callback validateArray - * @param {*} value - * @param {string} name - * @param {number} [minLength] - * @returns {asserts value is any[]} - */ -function validateArray(value, name, minLength = 0) { - // var validateArray = hideStackFrames((value, name, minLength = 0) => { - if (!$isJSArray(value)) { - throw $ERR_INVALID_ARG_TYPE(name, "Array", value); - } - if (value.length < minLength) { - var reason = `must be longer than ${minLength}`; - throw new ERR_INVALID_ARG_VALUE(name, value, reason); - } -} - -/** - * @callback validateString - * @param {*} value - * @param {string} name - * @returns {asserts value is string} - */ -function validateString(value, name) { - if (typeof value !== "string") throw $ERR_INVALID_ARG_TYPE(name, "string", value); -} - -/** - * @callback validateBoolean - * @param {*} value - * @param {string} name - * @returns {asserts value is boolean} - */ -function validateBoolean(value, name) { - if (typeof value !== "boolean") throw $ERR_INVALID_ARG_TYPE(name, "boolean", value); -} - -/** - * @callback validateObject - * @param {*} value - * @param {string} name - * @param {{ - * allowArray?: boolean, - * allowFunction?: boolean, - * nullable?: boolean - * }} [options] - */ -function validateObject(value, name, options = null) { - // var validateObject = hideStackFrames((value, name, options = null) => { - var allowArray = options?.allowArray ?? false; - var allowFunction = options?.allowFunction ?? false; - var nullable = options?.nullable ?? false; - if ( - (!nullable && value === null) || - (!allowArray && $isJSArray.$call(null, value)) || - (typeof value !== "object" && (!allowFunction || typeof value !== "function")) - ) { - throw $ERR_INVALID_ARG_TYPE(name, "object", value); - } -} - -/** - * @callback validateInteger - * @param {*} value - * @param {string} name - * @param {number} [min] - * @param {number} [max] - * @returns {asserts value is number} - */ -function validateInteger(value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) { - if (typeof value !== "number") throw $ERR_INVALID_ARG_TYPE(name, "number", value); - if (!NumberIsInteger(value)) throw new ERR_OUT_OF_RANGE(name, "an integer", value); - if (value < min || value > max) throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); -} - -/** - * @callback validateUint32 - * @param {*} value - * @param {string} name - * @param {number|boolean} [positive=false] - * @returns {asserts value is number} - */ -function validateUint32(value, name, positive = false) { - if (typeof value !== "number") { - throw $ERR_INVALID_ARG_TYPE(name, "number", value); - } - - if (!NumberIsInteger(value)) { - throw new ERR_OUT_OF_RANGE(name, "an integer", value); - } - - var min = positive ? 1 : 0; // 2 ** 32 === 4294967296 - var max = 4_294_967_295; - - if (value < min || value > max) { - throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); - } -} - // ---------------------------------------------------------------------------- // Section: Utils // ---------------------------------------------------------------------------- diff --git a/src/js/node/stream.ts b/src/js/node/stream.ts index ec669f047c51e..613183cd4e1b0 100644 --- a/src/js/node/stream.ts +++ b/src/js/node/stream.ts @@ -29,6 +29,23 @@ const kPaused = Symbol("kPaused"); const StringDecoder = require("node:string_decoder").StringDecoder; const transferToNativeReadable = $newCppFunction("ReadableStream.cpp", "jsFunctionTransferToNativeReadableStream", 1); const { kAutoDestroyed } = require("internal/shared"); +const { + validateBoolean, + validateString, + validateNumber, + validateSignalName, + validateEncoding, + validatePort, + validateInteger, + validateInt32, + validateUint32, + validateArray, + validateBuffer, + validateAbortSignal, + validateFunction, + validatePlainFunction, + validateUndefined, +} = require("internal/validators"); const ObjectSetPrototypeOf = Object.setPrototypeOf; @@ -47,49 +64,8 @@ function isReadableStream(value) { return typeof value === "object" && value !== null && value instanceof ReadableStream; } -function validateBoolean(value, name) { - if (typeof value !== "boolean") throw ERR_INVALID_ARG_TYPE(name, "boolean", value); -} - $debug("node:stream loaded"); -/** - * @callback validateObject - * @param {*} value - * @param {string} name - * @param {{ - * allowArray?: boolean, - * allowFunction?: boolean, - * nullable?: boolean - * }} [options] - */ - -/** @type {validateObject} */ -const validateObject = (value, name, options = null) => { - const allowArray = options?.allowArray ?? false; - const allowFunction = options?.allowFunction ?? false; - const nullable = options?.nullable ?? false; - if ( - (!nullable && value === null) || - (!allowArray && $isJSArray(value)) || - (typeof value !== "object" && (!allowFunction || typeof value !== "function")) - ) { - throw ERR_INVALID_ARG_TYPE(name, "Object", value); - } -}; - -/** - * @callback validateString - * @param {*} value - * @param {string} name - * @returns {asserts value is string} - */ - -/** @type {validateString} */ -function validateString(value, name) { - if (typeof value !== "string") throw ERR_INVALID_ARG_TYPE(name, "string", value); -} - //------------------------------------------------------------------------------ // Node error polyfills //------------------------------------------------------------------------------ @@ -678,46 +654,6 @@ var require_validators = __commonJS({ validateInt32(value, name, 0, 2 ** 32 - 1); return value; } - var validateInteger = hideStackFrames((value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => { - if (typeof value !== "number") throw new ERR_INVALID_ARG_TYPE(name, "number", value); - if (!NumberIsInteger(value)) throw new ERR_OUT_OF_RANGE(name, "an integer", value); - if (value < min || value > max) throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); - }); - var validateInt32 = hideStackFrames((value, name, min = -2147483648, max = 2147483647) => { - if (typeof value !== "number") { - throw new ERR_INVALID_ARG_TYPE(name, "number", value); - } - if (!isInt32(value)) { - if (!NumberIsInteger(value)) { - throw new ERR_OUT_OF_RANGE(name, "an integer", value); - } - throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); - } - if (value < min || value > max) { - throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); - } - }); - var validateUint32 = hideStackFrames((value, name, positive) => { - if (typeof value !== "number") { - throw new ERR_INVALID_ARG_TYPE(name, "number", value); - } - if (!isUint32(value)) { - if (!NumberIsInteger(value)) { - throw new ERR_OUT_OF_RANGE(name, "an integer", value); - } - const min = positive ? 1 : 0; - throw new ERR_OUT_OF_RANGE(name, `>= ${min} && < 4294967296`, value); - } - if (positive && value === 0) { - throw new ERR_OUT_OF_RANGE(name, ">= 1 && < 4294967296", value); - } - }); - function validateString(value, name) { - if (typeof value !== "string") throw new ERR_INVALID_ARG_TYPE(name, "string", value); - } - function validateNumber(value, name) { - if (typeof value !== "number") throw new ERR_INVALID_ARG_TYPE(name, "number", value); - } var validateOneOf = hideStackFrames((value, name, oneOf) => { if (!ArrayPrototypeIncludes(oneOf, value)) { const allowed = ArrayPrototypeJoin( @@ -728,9 +664,6 @@ var require_validators = __commonJS({ throw new ERR_INVALID_ARG_VALUE(name, value, reason); } }); - function validateBoolean(value, name) { - if (typeof value !== "boolean") throw new ERR_INVALID_ARG_TYPE(name, "boolean", value); - } var validateObject = hideStackFrames((value, name, options) => { const useDefaultOptions = options == null; const allowArray = useDefaultOptions ? false : options.allowArray; @@ -744,84 +677,12 @@ var require_validators = __commonJS({ throw new ERR_INVALID_ARG_TYPE(name, "Object", value); } }); - var validateArray = hideStackFrames((value, name, minLength = 0) => { - if (!$isJSArray(value)) { - throw new ERR_INVALID_ARG_TYPE(name, "Array", value); - } - if (value.length < minLength) { - const reason = `must be longer than ${minLength}`; - throw new ERR_INVALID_ARG_VALUE(name, value, reason); - } - }); - function validateSignalName(signal, name = "signal") { - validateString(signal, name); - if (signals[signal] === void 0) { - if (signals[StringPrototypeToUpperCase(signal)] !== void 0) { - throw new ERR_UNKNOWN_SIGNAL(signal + " (signals must use all capital letters)"); - } - throw new ERR_UNKNOWN_SIGNAL(signal); - } - } - var validateBuffer = hideStackFrames((buffer, name = "buffer") => { - if (!isArrayBufferView(buffer)) { - throw new ERR_INVALID_ARG_TYPE(name, ["Buffer", "TypedArray", "DataView"], buffer); - } - }); - function validateEncoding(data, encoding) { - const normalizedEncoding = normalizeEncoding(encoding); - const length = data.length; - if (normalizedEncoding === "hex" && length % 2 !== 0) { - throw new ERR_INVALID_ARG_VALUE("encoding", encoding, `is invalid for data of length ${length}`); - } - } - function validatePort(port, name = "Port", allowZero = true) { - if ( - (typeof port !== "number" && typeof port !== "string") || - (typeof port === "string" && StringPrototypeTrim(port).length === 0) || - +port !== +port >>> 0 || - port > 65535 || - (port === 0 && !allowZero) - ) { - throw new ERR_SOCKET_BAD_PORT(name, port, allowZero); - } - return port | 0; - } - var validateAbortSignal = hideStackFrames((signal, name) => { - if (signal !== void 0 && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); - } - }); - var validateFunction = hideStackFrames((value, name) => { - if (typeof value !== "function") throw new ERR_INVALID_ARG_TYPE(name, "Function", value); - }); - var validatePlainFunction = hideStackFrames((value, name) => { - if (typeof value !== "function" || isAsyncFunction(value)) - throw new ERR_INVALID_ARG_TYPE(name, "Function", value); - }); - var validateUndefined = hideStackFrames((value, name) => { - if (value !== void 0) throw new ERR_INVALID_ARG_TYPE(name, "undefined", value); - }); module.exports = { isInt32, isUint32, parseFileMode, - validateArray, - validateBoolean, - validateBuffer, - validateEncoding, - validateFunction, - validateInt32, - validateInteger, - validateNumber, validateObject, validateOneOf, - validatePlainFunction, - validatePort, - validateSignalName, - validateString, - validateUint32, - validateUndefined, - validateAbortSignal, }; }, }); @@ -1119,7 +980,7 @@ var require_end_of_stream = __commonJS({ var { AbortError, codes } = require_errors(); var { ERR_INVALID_ARG_TYPE, ERR_STREAM_PREMATURE_CLOSE } = codes; var { once } = require_util(); - var { validateAbortSignal, validateFunction, validateObject } = require_validators(); + var { validateObject } = require_validators(); var { Promise: Promise2 } = require_primordials(); var { isClosed, @@ -1329,7 +1190,7 @@ var require_operators = __commonJS({ codes: { ERR_INVALID_ARG_TYPE, ERR_MISSING_ARGS, ERR_OUT_OF_RANGE }, AbortError, } = require_errors(); - var { validateAbortSignal, validateInteger, validateObject } = require_validators(); + var { validateObject } = require_validators(); var kWeakHandler = require_primordials().Symbol("kWeak"); var { finished } = require_end_of_stream(); var { @@ -2104,11 +1965,6 @@ var require_add_abort_signal = __commonJS({ var { AbortError, codes } = require_errors(); var eos = require_end_of_stream(); var { ERR_INVALID_ARG_TYPE } = codes; - var validateAbortSignal = (signal, name) => { - if (typeof signal !== "object" || !("aborted" in signal)) { - throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); - } - }; function isNodeStream(obj) { return !!(obj && typeof obj.pipe === "function"); } @@ -2270,7 +2126,6 @@ var require_state = __commonJS({ "use strict"; const { MathFloor, NumberIsInteger } = require_primordials(); - const { validateInteger } = require_validators(); const { ERR_INVALID_ARG_VALUE } = require_errors().codes; let defaultHighWaterMarkBytes = 16 * 1024; let defaultHighWaterMarkObjectMode = 16; @@ -4152,7 +4007,7 @@ var require_writable = __commonJS({ } return this; }; - function needFinish(state, tag) { + function needFinish(state, tag?) { var needFinish = state.ending && !state.destroyed && @@ -4955,7 +4810,6 @@ var require_pipeline = __commonJS({ codes: { ERR_INVALID_ARG_TYPE, ERR_INVALID_RETURN_VALUE, ERR_MISSING_ARGS, ERR_STREAM_DESTROYED }, AbortError, } = require_errors(); - var { validateFunction, validateAbortSignal } = require_validators(); var { isIterable, isReadable, isReadableNodeStream, isNodeStream } = require_utils(); var PassThrough; function destroyer(stream, reading, writing) { diff --git a/src/js/node/timers.promises.ts b/src/js/node/timers.promises.ts index e9b8a290af84a..68ac1fa3f6dae 100644 --- a/src/js/node/timers.promises.ts +++ b/src/js/node/timers.promises.ts @@ -1,5 +1,8 @@ // Hardcoded module "node:timers/promises" // https://github.com/niksy/isomorphic-timers-promises/blob/master/index.js + +const { validateBoolean, validateAbortSignal } = require("internal/validators"); + const symbolAsyncIterator = Symbol.asyncIterator; class ERR_INVALID_ARG_TYPE extends Error { @@ -22,18 +25,6 @@ function validateObject(object, name) { } } -function validateBoolean(value, name) { - if (typeof value !== "boolean") { - throw new ERR_INVALID_ARG_TYPE(name, "boolean", value); - } -} - -function validateAbortSignal(signal, name) { - if (typeof signal !== "undefined" && (signal === null || typeof signal !== "object" || !("aborted" in signal))) { - throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); - } -} - function asyncIterator({ next: nextFunction, return: returnFunction }) { const result = {}; if (typeof nextFunction === "function") { diff --git a/src/js/node/tty.ts b/src/js/node/tty.ts index 62a3756493730..6bd0611a32fcf 100644 --- a/src/js/node/tty.ts +++ b/src/js/node/tty.ts @@ -4,6 +4,8 @@ const { getWindowSize: _getWindowSize, } = $cpp("ProcessBindingTTYWrap.cpp", "createBunTTYFunctions"); +const { validateInteger } = require("internal/validators"); + // primordials const NumberIsInteger = Number.isInteger; @@ -321,12 +323,6 @@ Object.defineProperty(WriteStream, "prototype", { configurable: true, }); -var validateInteger = (value, name, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) => { - if (typeof value !== "number") throw ERR_INVALID_ARG_TYPE(name, "number", value); - if (!NumberIsInteger(value)) throw ERR_OUT_OF_RANGE(name, "an integer", value); - if (value < min || value > max) throw ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); -}; - export default { ReadStream, WriteStream, isatty }; function ERR_INVALID_ARG_TYPE(name, type, value) {