diff --git a/benchmark/url/whatwgurl-to-and-from-path.js b/benchmark/url/whatwgurl-to-and-from-path.js new file mode 100644 index 00000000000000..3b87c0670a8fee --- /dev/null +++ b/benchmark/url/whatwgurl-to-and-from-path.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common.js'); +const { fileURLToPath, pathToFileURL } = require('node:url'); +const isWindows = process.platform === 'win32'; + +const bench = common.createBenchmark(main, { + input: isWindows ? [ + 'file:///c/', + ] : [ + 'file:///dev/null', + 'file:///dev/null?key=param&bool', + 'file:///dev/null?key=param&bool#hash', + ], + method: isWindows ? [ + 'fileURLToPath', + ] : [ + 'fileURLToPath', + 'pathToFileURL', + ], + n: [5e6], +}); + +function main({ n, input, method }) { + method = method === 'fileURLOrPath' ? fileURLToPath : pathToFileURL; + bench.start(); + for (let i = 0; i < n; i++) { + method(input); + } + bench.end(n); +} diff --git a/lib/internal/freeze_intrinsics.js b/lib/internal/freeze_intrinsics.js index 508b071c7c0f88..1e2a732eb1dbe1 100644 --- a/lib/internal/freeze_intrinsics.js +++ b/lib/internal/freeze_intrinsics.js @@ -61,6 +61,7 @@ const { Int32ArrayPrototype, Int8Array, Int8ArrayPrototype, + IteratorPrototype, Map, MapPrototype, Number, @@ -211,7 +212,7 @@ module.exports = function() { // 27 Control Abstraction Objects // 27.1 Iteration - ObjectGetPrototypeOf(ArrayIteratorPrototype), // 27.1.2 IteratorPrototype + IteratorPrototype, // 27.1.2 IteratorPrototype // 27.1.3 AsyncIteratorPrototype ObjectGetPrototypeOf(ObjectGetPrototypeOf(ObjectGetPrototypeOf( (async function*() {})() diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 370f5953dc78e5..d7ca58d0469c07 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -260,6 +260,8 @@ function copyPrototype(src, dest, prefix) { copyPrototype(original.prototype, primordials, `${name}Prototype`); }); +primordials.IteratorPrototype = Reflect.getPrototypeOf(primordials.ArrayIteratorPrototype); + /* eslint-enable node-core/prefer-primordials */ const { diff --git a/lib/internal/url.js b/lib/internal/url.js index 40b25f6890b5db..ae133b02e20fa1 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -9,6 +9,7 @@ const { ArrayPrototypeSlice, FunctionPrototypeBind, Int8Array, + IteratorPrototype, Number, ObjectCreate, ObjectDefineProperties, @@ -19,11 +20,13 @@ const { ReflectApply, ReflectGetOwnPropertyDescriptor, ReflectOwnKeys, + RegExpPrototypeSymbolReplace, String, + StringPrototypeCharAt, StringPrototypeCharCodeAt, + StringPrototypeCodePointAt, StringPrototypeIncludes, - StringPrototypeReplace, - StringPrototypeReplaceAll, + StringPrototypeIndexOf, StringPrototypeSlice, StringPrototypeSplit, StringPrototypeStartsWith, @@ -45,6 +48,7 @@ const { removeColors, toUSVString, kEnumerableProperty, + SideEffectFreeRegExpPrototypeSymbolReplace, } = require('internal/util'); const { @@ -113,6 +117,8 @@ const { revokeDataObject, } = internalBinding('blob'); +const FORWARD_SLASH = /\//g; + const context = Symbol('context'); const cannotBeBase = Symbol('cannot-be-base'); const cannotHaveUsernamePasswordPort = @@ -139,11 +145,6 @@ function lazyCryptoRandom() { return cryptoRandom; } -// https://tc39.github.io/ecma262/#sec-%iteratorprototype%-object -const IteratorPrototype = ObjectGetPrototypeOf( - ObjectGetPrototypeOf([][SymbolIterator]()) -); - // Refs: https://html.spec.whatwg.org/multipage/browsers.html#concept-origin-opaque const kOpaqueOrigin = 'null'; @@ -1377,7 +1378,7 @@ defineIDLClass(URLSearchParamsIteratorPrototype, 'URLSearchParams Iterator', { }, [] ); - const breakLn = inspect(output, innerOpts).includes('\n'); + const breakLn = StringPrototypeIncludes(inspect(output, innerOpts), '\n'); const outputStrs = ArrayPrototypeMap(output, (p) => inspect(p, innerOpts)); let outputStr; if (breakLn) { @@ -1435,7 +1436,7 @@ function getPathFromURLWin32(url) { let pathname = url.pathname; for (let n = 0; n < pathname.length; n++) { if (pathname[n] === '%') { - const third = pathname.codePointAt(n + 2) | 0x20; + const third = StringPrototypeCodePointAt(pathname, n + 2) | 0x20; if ((pathname[n + 1] === '2' && third === 102) || // 2f 2F / (pathname[n + 1] === '5' && third === 99)) { // 5c 5C \ throw new ERR_INVALID_FILE_URL_PATH( @@ -1444,7 +1445,7 @@ function getPathFromURLWin32(url) { } } } - pathname = StringPrototypeReplaceAll(pathname, '/', '\\'); + pathname = SideEffectFreeRegExpPrototypeSymbolReplace(FORWARD_SLASH, pathname, '\\'); pathname = decodeURIComponent(pathname); if (hostname !== '') { // If hostname is set, then we have a UNC path @@ -1456,13 +1457,13 @@ function getPathFromURLWin32(url) { return `\\\\${domainToUnicode(hostname)}${pathname}`; } // Otherwise, it's a local path that requires a drive letter - const letter = pathname.codePointAt(1) | 0x20; - const sep = pathname[2]; + const letter = StringPrototypeCodePointAt(pathname, 1) | 0x20; + const sep = StringPrototypeCharAt(pathname, 2); if (letter < CHAR_LOWERCASE_A || letter > CHAR_LOWERCASE_Z || // a..z A..Z (sep !== ':')) { throw new ERR_INVALID_FILE_URL_PATH('must be absolute'); } - return pathname.slice(1); + return StringPrototypeSlice(pathname, 1); } function getPathFromURLPosix(url) { @@ -1472,7 +1473,7 @@ function getPathFromURLPosix(url) { const pathname = url.pathname; for (let n = 0; n < pathname.length; n++) { if (pathname[n] === '%') { - const third = pathname.codePointAt(n + 2) | 0x20; + const third = StringPrototypeCodePointAt(pathname, n + 2) | 0x20; if (pathname[n + 1] === '2' && third === 102) { throw new ERR_INVALID_FILE_URL_PATH( 'must not include encoded / characters' @@ -1512,16 +1513,16 @@ const tabRegEx = /\t/g; function encodePathChars(filepath) { if (StringPrototypeIncludes(filepath, '%')) - filepath = StringPrototypeReplace(filepath, percentRegEx, '%25'); + filepath = RegExpPrototypeSymbolReplace(percentRegEx, filepath, '%25'); // In posix, backslash is a valid character in paths: if (!isWindows && StringPrototypeIncludes(filepath, '\\')) - filepath = StringPrototypeReplace(filepath, backslashRegEx, '%5C'); + filepath = RegExpPrototypeSymbolReplace(backslashRegEx, filepath, '%5C'); if (StringPrototypeIncludes(filepath, '\n')) - filepath = StringPrototypeReplace(filepath, newlineRegEx, '%0A'); + filepath = RegExpPrototypeSymbolReplace(newlineRegEx, filepath, '%0A'); if (StringPrototypeIncludes(filepath, '\r')) - filepath = StringPrototypeReplace(filepath, carriageReturnRegEx, '%0D'); + filepath = RegExpPrototypeSymbolReplace(carriageReturnRegEx, filepath, '%0D'); if (StringPrototypeIncludes(filepath, '\t')) - filepath = StringPrototypeReplace(filepath, tabRegEx, '%09'); + filepath = RegExpPrototypeSymbolReplace(tabRegEx, filepath, '%09'); return filepath; } @@ -1529,25 +1530,25 @@ function pathToFileURL(filepath) { const outURL = new URL('file://'); if (isWindows && StringPrototypeStartsWith(filepath, '\\\\')) { // UNC path format: \\server\share\resource - const paths = StringPrototypeSplit(filepath, '\\'); - if (paths.length <= 3) { + const hostnameEndIndex = StringPrototypeIndexOf(filepath, '\\', 2); + if (hostnameEndIndex === -1) { throw new ERR_INVALID_ARG_VALUE( 'filepath', filepath, 'Missing UNC resource path' ); } - const hostname = paths[2]; - if (hostname.length === 0) { + if (hostnameEndIndex === 2) { throw new ERR_INVALID_ARG_VALUE( 'filepath', filepath, 'Empty UNC servername' ); } + const hostname = StringPrototypeSlice(filepath, 2, hostnameEndIndex); outURL.hostname = domainToASCII(hostname); outURL.pathname = encodePathChars( - ArrayPrototypeJoin(ArrayPrototypeSlice(paths, 3), '/')); + RegExpPrototypeSymbolReplace(backslashRegEx, StringPrototypeSlice(filepath, hostnameEndIndex), '/')); } else { let resolved = path.resolve(filepath); // path.resolve strips trailing slashes so we must add them back