From 127a9e1bd777ddcdda81e052d8f90330cc65eeec Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 13 Apr 2025 05:40:20 -0700 Subject: [PATCH] Make validators faster --- src/js/internal/validators.ts | 80 ++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/src/js/internal/validators.ts b/src/js/internal/validators.ts index 9ab57ecd13..00410c7014 100644 --- a/src/js/internal/validators.ts +++ b/src/js/internal/validators.ts @@ -1,19 +1,16 @@ const { hideFromStack } = require("internal/shared"); const RegExpPrototypeExec = RegExp.prototype.exec; -const ArrayPrototypeIncludes = Array.prototype.includes; -const ArrayPrototypeJoin = Array.prototype.join; -const ArrayPrototypeMap = Array.prototype.map; -const ArrayIsArray = Array.isArray; -const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/; +let tokenRegExp: RegExp | undefined; + /** * Verifies that the given val is a valid HTTP token * per the rules defined in RFC 7230 * See https://tools.ietf.org/html/rfc7230#section-3.2.6 */ function checkIsHttpToken(val) { - return RegExpPrototypeExec.$call(tokenRegExp, val) !== null; + return RegExpPrototypeExec.$call((tokenRegExp ??= /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/), val) !== null; } /* @@ -24,9 +21,12 @@ function checkIsHttpToken(val) { (not necessarily a valid URI reference) followed by zero or more link-params separated by semicolons. */ -const linkValueRegExp = /^(?:<[^>]*>)(?:\s*;\s*[^;"\s]+(?:=(")?[^;"\s]*\1)?)*$/; +let linkValueRegExp: RegExp | undefined; function validateLinkHeaderFormat(value, name) { - if (typeof value === "undefined" || !RegExpPrototypeExec.$call(linkValueRegExp, value)) { + if ( + typeof value === "undefined" || + !RegExpPrototypeExec.$call((linkValueRegExp ??= /^(?:<[^>]*>)(?:\s*;\s*[^;"\s]+(?:=(")?[^;"\s]*\1)?)*$/), value) + ) { throw $ERR_INVALID_ARG_VALUE( name, value, @@ -39,7 +39,7 @@ function validateLinkHeaderValue(hints) { if (typeof hints === "string") { validateLinkHeaderFormat(hints, "hints"); return hints; - } else if (ArrayIsArray(hints)) { + } else if ($isArray(hints)) { const hintsLength = hints.length; let result = ""; @@ -68,31 +68,61 @@ function validateLinkHeaderValue(hints) { } hideFromStack(validateLinkHeaderValue); +// We want to let the JIT remove the most trivial checks. +const validateObjectInternal = $newCppFunction("NodeValidator.cpp", "jsFunction_validateObject", 2); +const validateNumberInternal = $newCppFunction("NodeValidator.cpp", "jsFunction_validateNumber", 0); +const validateStringInternal = $newCppFunction("NodeValidator.cpp", "jsFunction_validateString", 0); +const validateFunctionInternal = $newCppFunction("NodeValidator.cpp", "jsFunction_validateFunction", 0); +const validateBooleanInternal = $newCppFunction("NodeValidator.cpp", "jsFunction_validateBoolean", 0); +const validateArrayInternal = $newCppFunction("NodeValidator.cpp", "jsFunction_validateArray", 0); + export default { /** (value, name) */ - validateObject: $newCppFunction("NodeValidator.cpp", "jsFunction_validateObject", 2), - validateLinkHeaderValue: validateLinkHeaderValue, - checkIsHttpToken: checkIsHttpToken, + validateObject: function validateObject(value, name) { + if (!$isObject(value)) { + return validateObjectInternal.$apply(this, arguments); + } + }, + /** `(value, name, min, max)` */ validateInteger: $newCppFunction("NodeValidator.cpp", "jsFunction_validateInteger", 0), /** `(value, name, min, max)` */ - validateNumber: $newCppFunction("NodeValidator.cpp", "jsFunction_validateNumber", 0), + validateNumber: function validateNumber(value, name, min, max) { + if (typeof value !== "number") { + return validateNumberInternal.$apply(this, arguments); + } + }, /** `(value, name)` */ - validateString: $newCppFunction("NodeValidator.cpp", "jsFunction_validateString", 0), + validateString: function validateString(value, name) { + if (typeof value !== "string") { + return validateStringInternal.$apply(this, arguments); + } + }, /** `(number, name)` */ validateFiniteNumber: $newCppFunction("NodeValidator.cpp", "jsFunction_validateFiniteNumber", 0), /** `(number, name, lower, upper, def)` */ checkRangesOrGetDefault: $newCppFunction("NodeValidator.cpp", "jsFunction_checkRangesOrGetDefault", 0), /** `(value, name)` */ - validateFunction: $newCppFunction("NodeValidator.cpp", "jsFunction_validateFunction", 0), + validateFunction: function validateFunction(value, name) { + if (typeof value !== "function") { + return validateFunctionInternal.$apply(this, arguments); + } + }, /** `(value, name)` */ - validateBoolean: $newCppFunction("NodeValidator.cpp", "jsFunction_validateBoolean", 0), - /** `(port, name = 'Port', allowZero = true)` */ + validateBoolean: function validateBoolean(value, name) { + if (typeof value !== "boolean") { + return validateBooleanInternal.$apply(this, arguments); + } + }, validatePort: $newCppFunction("NodeValidator.cpp", "jsFunction_validatePort", 0), /** `(signal, name)` */ validateAbortSignal: $newCppFunction("NodeValidator.cpp", "jsFunction_validateAbortSignal", 0), /** `(value, name, minLength = 0)` */ - validateArray: $newCppFunction("NodeValidator.cpp", "jsFunction_validateArray", 0), + validateArray: function validateArray(value, name, minLength) { + if (!$isArray(value) || (typeof minLength === "number" && value.length < minLength)) { + return validateArrayInternal.$apply(this, arguments); + } + }, /** `(value, name, min = -2147483648, max = 2147483647)` */ validateInt32: $newCppFunction("NodeValidator.cpp", "jsFunction_validateInt32", 0), /** `(value, name, positive = false)` */ @@ -101,12 +131,20 @@ export default { validateSignalName: $newCppFunction("NodeValidator.cpp", "jsFunction_validateSignalName", 0), /** `(data, encoding)` */ validateEncoding: $newCppFunction("NodeValidator.cpp", "jsFunction_validateEncoding", 0), + + // ** UNUSED ** /** `(value, name)` */ - validatePlainFunction: $newCppFunction("NodeValidator.cpp", "jsFunction_validatePlainFunction", 0), - /** `(value, name)` */ - validateUndefined: $newCppFunction("NodeValidator.cpp", "jsFunction_validateUndefined", 0), + // validatePlainFunction: $newCppFunction("NodeValidator.cpp", "jsFunction_validatePlainFunction", 0), + // /** `(value, name)` */ + // validateUndefined: function validateUndefined(value, name) { + // if (typeof value !== "undefined") { + // return validateUndefinedInternal.$apply(this, arguments); + // } + // }, /** `(buffer, name = 'buffer')` */ validateBuffer: $newCppFunction("NodeValidator.cpp", "jsFunction_validateBuffer", 0), /** `(value, name, oneOf)` */ validateOneOf: $newCppFunction("NodeValidator.cpp", "jsFunction_validateOneOf", 0), + validateLinkHeaderValue: validateLinkHeaderValue, + checkIsHttpToken: checkIsHttpToken, };