From f57d3cee2c7d10040c0f82495c8b96cad5ec0122 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 15 Jul 2024 16:09:54 -0700 Subject: [PATCH] lib: make navigator not runtime-lookup process.version/arch/platform Preserves #53649. PR-URL: https://github.com/nodejs/node/pull/53765 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Yagiz Nizipli Reviewed-By: Joyee Cheung Reviewed-By: Matteo Collina Reviewed-By: Antoine du Hamel Reviewed-By: Daeyeon Jeong Reviewed-By: James M Snell --- lib/internal/navigator.js | 67 ++++++++++++++------------- lib/internal/process/per_thread.js | 10 +++- lib/internal/process/pre_execution.js | 5 ++ lib/internal/url.js | 6 ++- test/parallel/test-navigator.js | 65 ++++++++++++++++++-------- 5 files changed, 99 insertions(+), 54 deletions(-) diff --git a/lib/internal/navigator.js b/lib/internal/navigator.js index e56b2da5e339e3..63b3fa27905bf0 100644 --- a/lib/internal/navigator.js +++ b/lib/internal/navigator.js @@ -7,13 +7,8 @@ const { StringPrototypeSlice, StringPrototypeToUpperCase, Symbol, - globalThis, } = primordials; -const { - Intl, -} = globalThis; - const { ERR_ILLEGAL_CONSTRUCTOR, } = require('internal/errors').codes; @@ -27,52 +22,60 @@ const { } = internalBinding('os'); const kInitialize = Symbol('kInitialize'); -const nodeVersion = process.version; + +const { + platform, + arch, + version: nodeVersion, +} = require('internal/process/per_thread'); + +const { + language, +} = internalBinding('config'); /** - * @param {object} process - * @param {string} process.platform - * @param {string} process.arch + * @param {string} arch + * @param {string} platform * @returns {string} */ -function getNavigatorPlatform(process) { - if (process.platform === 'darwin') { +function getNavigatorPlatform(arch, platform) { + if (platform === 'darwin') { // On macOS, modern browsers return 'MacIntel' even if running on Apple Silicon. return 'MacIntel'; - } else if (process.platform === 'win32') { + } else if (platform === 'win32') { // On Windows, modern browsers return 'Win32' even if running on a 64-bit version of Windows. // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform#usage_notes return 'Win32'; - } else if (process.platform === 'linux') { - if (process.arch === 'ia32') { + } else if (platform === 'linux') { + if (arch === 'ia32') { return 'Linux i686'; - } else if (process.arch === 'x64') { + } else if (arch === 'x64') { return 'Linux x86_64'; } - return `Linux ${process.arch}`; - } else if (process.platform === 'freebsd') { - if (process.arch === 'ia32') { + return `Linux ${arch}`; + } else if (platform === 'freebsd') { + if (arch === 'ia32') { return 'FreeBSD i386'; - } else if (process.arch === 'x64') { + } else if (arch === 'x64') { return 'FreeBSD amd64'; } - return `FreeBSD ${process.arch}`; - } else if (process.platform === 'openbsd') { - if (process.arch === 'ia32') { + return `FreeBSD ${arch}`; + } else if (platform === 'openbsd') { + if (arch === 'ia32') { return 'OpenBSD i386'; - } else if (process.arch === 'x64') { + } else if (arch === 'x64') { return 'OpenBSD amd64'; } - return `OpenBSD ${process.arch}`; - } else if (process.platform === 'sunos') { - if (process.arch === 'ia32') { + return `OpenBSD ${arch}`; + } else if (platform === 'sunos') { + if (arch === 'ia32') { return 'SunOS i86pc'; } - return `SunOS ${process.arch}`; - } else if (process.platform === 'aix') { + return `SunOS ${arch}`; + } else if (platform === 'aix') { return 'AIX'; } - return `${StringPrototypeToUpperCase(process.platform[0])}${StringPrototypeSlice(process.platform, 1)} ${process.arch}`; + return `${StringPrototypeToUpperCase(platform[0])}${StringPrototypeSlice(platform, 1)} ${arch}`; } class Navigator { @@ -80,7 +83,6 @@ class Navigator { #availableParallelism; #userAgent; #platform; - #language; #languages; constructor() { @@ -102,8 +104,7 @@ class Navigator { * @return {string} */ get language() { - this.#language ??= Intl?.Collator().resolvedOptions().locale || 'en-US'; - return this.#language; + return language; } /** @@ -126,7 +127,7 @@ class Navigator { * @return {string} */ get platform() { - this.#platform ??= getNavigatorPlatform(process); + this.#platform ??= getNavigatorPlatform(arch, platform); return this.#platform; } } diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 6fdf5dafcedafc..70dcca3e8f0d96 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -46,9 +46,11 @@ const { validateNumber, validateObject, } = require('internal/validators'); -const { getValidatedPath } = require('internal/fs/utils'); + const constants = internalBinding('constants').os.signals; +let getValidatedPath; // We need to lazy load it because of the circular dependency. + const kInternal = Symbol('internal properties'); function assert(x, msg) { @@ -253,6 +255,7 @@ function wrapProcessMethods(binding) { */ function loadEnvFile(path = undefined) { // Provide optional value so that `loadEnvFile.length` returns 0 if (path != null) { + getValidatedPath ??= require('internal/fs/utils').getValidatedPath; path = getValidatedPath(path); _loadEnvFile(path); } else { @@ -421,6 +424,8 @@ function toggleTraceCategoryState(asyncHooksEnabled) { } } +const { arch, platform, version } = process; + module.exports = { toggleTraceCategoryState, assert, @@ -428,4 +433,7 @@ module.exports = { wrapProcessMethods, hrtime, hrtimeBigInt, + arch, + platform, + version, }; diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 6ad46428727f07..383dee4e6988e4 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -21,6 +21,10 @@ const { globalThis, } = primordials; +const { + Intl, +} = globalThis; + const { getOptionValue, refreshOptions, @@ -341,6 +345,7 @@ function setupNavigator() { // https://html.spec.whatwg.org/multipage/system-state.html#the-navigator-object exposeLazyInterfaces(globalThis, 'internal/navigator', ['Navigator']); + internalBinding('config').language = Intl?.Collator().resolvedOptions().locale || 'en-US'; defineReplaceableLazyAttribute(globalThis, 'internal/navigator', ['navigator'], false); } diff --git a/lib/internal/url.js b/lib/internal/url.js index b73e92a32d5a0e..fead9b2dbdbba6 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -49,6 +49,10 @@ const { isWindows, } = require('internal/util'); +const { + platform, +} = require('internal/process/per_thread'); + const { markTransferMode, } = require('internal/worker/js_transferable'); @@ -1469,7 +1473,7 @@ function getPathFromURLWin32(url) { function getPathFromURLPosix(url) { if (url.hostname !== '') { - throw new ERR_INVALID_FILE_URL_HOST(process.platform); + throw new ERR_INVALID_FILE_URL_HOST(platform); } const pathname = url.pathname; for (let n = 0; n < pathname.length; n++) { diff --git a/test/parallel/test-navigator.js b/test/parallel/test-navigator.js index b280a047d1c410..ebd1f67760bd44 100644 --- a/test/parallel/test-navigator.js +++ b/test/parallel/test-navigator.js @@ -2,8 +2,36 @@ 'use strict'; -const common = require('../common'); +/* eslint node-core/require-common-first: 0 */ + const assert = require('assert'); + +{ + + // Ensures `navigator` has not been evaluated yet + assert.strictEqual(require.resolve('../common') in require.cache, false); + + const { version, platform, arch } = process; + try { + let called = false; + Object.defineProperty(process, 'arch', { get() { called += 'arch|'; return arch; } }); + Object.defineProperty(process, 'platform', { get() { called = 'platform|'; return platform; } }); + Object.defineProperty(process, 'version', { get() { called = 'version|'; return version; } }); + + navigator; // eslint-disable-line no-unused-expressions + + assert.strictEqual( + called, + false + ); + } finally { + Object.defineProperty(process, 'arch', { value: arch }); + Object.defineProperty(process, 'platform', { value: platform }); + Object.defineProperty(process, 'version', { value: version }); + } +} + +const common = require('../common'); const { getNavigatorPlatform } = require('internal/navigator'); const { execFile } = require('child_process'); @@ -57,24 +85,23 @@ if (process.platform === 'darwin') { assert.strictEqual(navigator.platform, `${process.platform[0].toUpperCase()}${process.platform.slice(1)} ${process.arch}`); } -assert.strictEqual(getNavigatorPlatform({ arch: 'x64', platform: 'darwin' }), 'MacIntel'); -assert.strictEqual(getNavigatorPlatform({ arch: 'arm64', platform: 'darwin' }), 'MacIntel'); -assert.strictEqual(getNavigatorPlatform({ arch: 'ia32', platform: 'linux' }), 'Linux i686'); -assert.strictEqual(getNavigatorPlatform({ arch: 'x64', platform: 'linux' }), 'Linux x86_64'); -assert.strictEqual(getNavigatorPlatform({ arch: 'arm64', platform: 'linux' }), 'Linux arm64'); -assert.strictEqual(getNavigatorPlatform({ arch: 'ia32', platform: 'win32' }), 'Win32'); -assert.strictEqual(getNavigatorPlatform({ arch: 'x64', platform: 'win32' }), 'Win32'); -assert.strictEqual(getNavigatorPlatform({ arch: 'arm64', platform: 'win32' }), 'Win32'); -assert.strictEqual(getNavigatorPlatform({ arch: 'ia32', platform: 'freebsd' }), 'FreeBSD i386'); -assert.strictEqual(getNavigatorPlatform({ arch: 'x64', platform: 'freebsd' }), 'FreeBSD amd64'); -assert.strictEqual(getNavigatorPlatform({ arch: 'arm64', platform: 'freebsd' }), 'FreeBSD arm64'); -assert.strictEqual(getNavigatorPlatform({ arch: 'ia32', platform: 'openbsd' }), 'OpenBSD i386'); -assert.strictEqual(getNavigatorPlatform({ arch: 'x64', platform: 'openbsd' }), 'OpenBSD amd64'); -assert.strictEqual(getNavigatorPlatform({ arch: 'arm64', platform: 'openbsd' }), 'OpenBSD arm64'); -assert.strictEqual(getNavigatorPlatform({ arch: 'ia32', platform: 'sunos' }), 'SunOS i86pc'); -assert.strictEqual(getNavigatorPlatform({ arch: 'x64', platform: 'sunos' }), 'SunOS x64'); -assert.strictEqual(getNavigatorPlatform({ arch: 'ppc', platform: 'aix' }), 'AIX'); -assert.strictEqual(getNavigatorPlatform({ arch: 'x64', platform: 'reactos' }), 'Reactos x64'); +assert.strictEqual(getNavigatorPlatform('x64', 'darwin'), 'MacIntel'); +assert.strictEqual(getNavigatorPlatform('arm64', 'darwin'), 'MacIntel'); +assert.strictEqual(getNavigatorPlatform('ia32', 'linux'), 'Linux i686'); +assert.strictEqual(getNavigatorPlatform('x64', 'linux'), 'Linux x86_64'); +assert.strictEqual(getNavigatorPlatform('arm64', 'linux'), 'Linux arm64'); +assert.strictEqual(getNavigatorPlatform('x64', 'win32'), 'Win32'); +assert.strictEqual(getNavigatorPlatform('arm64', 'win32'), 'Win32'); +assert.strictEqual(getNavigatorPlatform('ia32', 'freebsd'), 'FreeBSD i386'); +assert.strictEqual(getNavigatorPlatform('x64', 'freebsd'), 'FreeBSD amd64'); +assert.strictEqual(getNavigatorPlatform('arm64', 'freebsd'), 'FreeBSD arm64'); +assert.strictEqual(getNavigatorPlatform('ia32', 'openbsd'), 'OpenBSD i386'); +assert.strictEqual(getNavigatorPlatform('x64', 'openbsd'), 'OpenBSD amd64'); +assert.strictEqual(getNavigatorPlatform('arm64', 'openbsd'), 'OpenBSD arm64'); +assert.strictEqual(getNavigatorPlatform('ia32', 'sunos'), 'SunOS i86pc'); +assert.strictEqual(getNavigatorPlatform('x64', 'sunos'), 'SunOS x64'); +assert.strictEqual(getNavigatorPlatform('ppc', 'aix'), 'AIX'); +assert.strictEqual(getNavigatorPlatform('x64', 'reactos'), 'Reactos x64'); assert.strictEqual(typeof navigator.language, 'string'); assert.strictEqual(navigator.language.length !== 0, true);