From 859ae5522d330acede339e74f36f5c78ce87bf23 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Tue, 2 Jun 2026 06:11:31 -0400 Subject: [PATCH 01/12] test: update test426-fixtures to 2965987bf4c96afa400c9356c8e620cb340aaee PR-URL: https://github.com/nodejs/node/pull/63668 Reviewed-By: Antoine du Hamel Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- .../range-mappings-proposal-tests.json | 110 +++++++++++++++++- .../range-mappings/extra-lines-in-mappings.js | 0 .../extra-lines-in-range-mappings.js | 3 + .../extra-lines-in-range-mappings.js.map | 8 ++ .../invalid-mapping-for-range.js | 3 + .../invalid-mapping-for-range.js.map | 8 ++ .../range-mappings/invalid-vlq-zero.js.map | 4 +- .../range-mappings/multiple-mappings.js | 2 +- .../range-mappings/multiple-mappings.js.map | 6 +- .../range-mappings/newline-semantics.js.map | 2 +- .../non-full-line-coverage.js.map | 4 +- .../range-mappings/out-of-range-2.js.map | 6 +- .../range-mappings/out-of-range-3.js | 2 + .../range-mappings/out-of-range-3.js.map | 8 ++ .../range-mappings/out-of-range.js.map | 4 +- .../proposals/range-mappings/simple.js.map | 2 +- .../range-mappings/vlq-continuation-bit.js | 2 + .../vlq-continuation-bit.js.map | 8 ++ test/test426/README.md | 2 +- 19 files changed, 164 insertions(+), 20 deletions(-) create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-mappings.js create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js.map create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js.map create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js.map create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js create mode 100644 test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js.map diff --git a/test/fixtures/test426/range-mappings-proposal-tests.json b/test/fixtures/test426/range-mappings-proposal-tests.json index 7b108e7e5e15bd..ff4a1791dbfee4 100644 --- a/test/fixtures/test426/range-mappings-proposal-tests.json +++ b/test/fixtures/test426/range-mappings-proposal-tests.json @@ -38,11 +38,18 @@ }, { "name": "rangeMappingsInvalidVLQZero", - "description": "VLQ of zero is invalid because offsets are 1-based", + "description": "VLQ with relative offset of 0 after the initial mapping is invalid", "baseFile": "invalid-vlq-zero.js", "sourceMapFile": "invalid-vlq-zero.js.map", "sourceMapIsValid": false }, + { + "name": "rangeMappingsInvalidMappingForRange", + "description": "Range mappings cannot point to a mapping withou an original position", + "baseFile": "invalid-mapping-for-range.js", + "sourceMapFile": "invalid-mapping-for-range.js.map", + "sourceMapIsValid": false + }, { "name": "rangeMappingsOutOfRange", "description": "Test an invalid range mapping which is outside the mappings length", @@ -57,6 +64,13 @@ "sourceMapFile": "out-of-range-2.js.map", "sourceMapIsValid": false }, + { + "name": "rangeMappingsOutOfRange3", + "description": "Test an invalid range mapping encoded with a continuation bit that is too large", + "baseFile": "out-of-range-3.js", + "sourceMapFile": "out-of-range-3.js.map", + "sourceMapIsValid": false + }, { "name": "rangeMappingsEmpty", "description": "Test a trivial range mapping that is the empty string", @@ -64,6 +78,13 @@ "sourceMapFile": "empty-string.js.map", "sourceMapIsValid": true }, + { + "name": "rangeMappingsExtraLinesInRangeMappings", + "description": "Test that it's ok for range mappings to have trailing empty lines not in mappings", + "baseFile": "extra-lines-in-range-mappings.js", + "sourceMapFile": "extra-lines-in-range-mappings.js.map", + "sourceMapIsValid": true + }, { "name": "rangeMappingsNonFullLineCoverage", "description": "Test a non-empty range mapping where not all lines in the generated source are covered", @@ -71,6 +92,60 @@ "sourceMapFile": "non-full-line-coverage.js.map", "sourceMapIsValid": true }, + { + "name": "rangeMappingsVLQContinuationBit", + "description": "Test a range mapping that uses a VLQ with continuation bit", + "baseFile": "vlq-continuation-bit.js", + "sourceMapFile": "vlq-continuation-bit.js.map", + "sourceMapIsValid": true, + "testActions": [ + { + "actionType": "checkMapping", + "generatedLine": 0, + "generatedColumn": 0, + "originalSource": "vlq-continuation-bit-original.js", + "originalLine": 0, + "originalColumn": 0, + "mappedName": null + }, + { + "actionType": "checkMapping", + "generatedLine": 0, + "generatedColumn": 1, + "originalSource": "vlq-continuation-bit-original.js", + "originalLine": 0, + "originalColumn": 1, + "mappedName": null + }, + { + "actionType": "checkMapping", + "generatedLine": 0, + "generatedColumn": 3, + "originalSource": "vlq-continuation-bit-original.js", + "originalLine": 0, + "originalColumn": 2, + "mappedName": null + }, + { + "actionType": "checkMapping", + "generatedLine": 0, + "generatedColumn": 196, + "originalSource": "vlq-continuation-bit-original.js", + "originalLine": 0, + "originalColumn": 195, + "mappedName": null + }, + { + "actionType": "checkMapping", + "generatedLine": 0, + "generatedColumn": 199, + "originalSource": "vlq-continuation-bit-original.js", + "originalLine": 0, + "originalColumn": 199, + "mappedName": null + } + ] + }, { "name": "rangeMappingsSimple", "description": "Test a simple, single-entry rangeMappings", @@ -138,7 +213,7 @@ "generatedColumn": 16, "originalSource": "multiple-mappings-original.js", "originalLine": 0, - "originalColumn": 16, + "originalColumn": 17, "mappedName": null }, { @@ -147,7 +222,25 @@ "generatedColumn": 18, "originalSource": "multiple-mappings-original.js", "originalLine": 0, - "originalColumn": 18, + "originalColumn": 19, + "mappedName": null + }, + { + "actionType": "checkMapping", + "generatedLine": 1, + "generatedColumn": 30, + "originalSource": "multiple-mappings-original.js", + "originalLine": 0, + "originalColumn": 31, + "mappedName": null + }, + { + "actionType": "checkMapping", + "generatedLine": 1, + "generatedColumn": 34, + "originalSource": "multiple-mappings-original.js", + "originalLine": 0, + "originalColumn": 35, "mappedName": null }, { @@ -156,7 +249,16 @@ "generatedColumn": 35, "originalSource": "multiple-mappings-original.js", "originalLine": 0, - "originalColumn": 36, + "originalColumn": 35, + "mappedName": null + }, + { + "actionType": "checkMapping", + "generatedLine": 1, + "generatedColumn": 36, + "originalSource": "multiple-mappings-original.js", + "originalLine": 0, + "originalColumn": 38, "mappedName": null } ] diff --git a/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-mappings.js b/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-mappings.js new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js b/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js new file mode 100644 index 00000000000000..13a2975e15b0de --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js @@ -0,0 +1,3 @@ + +42; +//# sourceMappingURL=extra-lines-in-range-mappings.js.map diff --git a/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js.map b/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js.map new file mode 100644 index 00000000000000..f08fe09d0fc6ac --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/extra-lines-in-range-mappings.js.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "names": [], + "sources": ["extra-lines-in-range-mappings-original.js"], + "sourcesContent": ["42;"], + "mappings": ";AAAA,EAAE;", + "rangeMappings": ";A;;;" +} diff --git a/test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js b/test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js new file mode 100644 index 00000000000000..29924a5d23d095 --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js @@ -0,0 +1,3 @@ + +42; +//# sourceMappingURL=invalid-mapping-for-range.js.map diff --git a/test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js.map b/test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js.map new file mode 100644 index 00000000000000..c6da9691361da1 --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/invalid-mapping-for-range.js.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "names": [], + "sources": ["invalid-mapping-for-range-original.js"], + "sourcesContent": ["42;"], + "mappings": ";A,EAAE", + "rangeMappings": ";A" +} diff --git a/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js.map b/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js.map index fe13e8f5225acd..e9ac1349ee1aad 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js.map +++ b/test/fixtures/test426/resources/proposals/range-mappings/invalid-vlq-zero.js.map @@ -4,6 +4,6 @@ "file": "invalid-vlq-zero.js", "sources": ["empty-original.js"], "sourcesContent": [""], - "mappings": "A", - "rangeMappings": "A" + "mappings": "A,A", + "rangeMappings": "AA" } diff --git a/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js b/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js index b98843245bf481..4e97684d030f75 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js +++ b/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js @@ -1,3 +1,3 @@ - "Hello world"; function f() { } + "Hello world"; function f() { }; 6 //# sourceMappingURL=multiple-mappings.js.map diff --git a/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map b/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map index c7ffc83e012283..534f744149e2bc 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map +++ b/test/fixtures/test426/resources/proposals/range-mappings/multiple-mappings.js.map @@ -2,7 +2,7 @@ "version": 3, "names": [], "sources": ["multiple-mappings-original.js"], - "sourcesContent": ["\"Hello World\"; function f() { } "], - "mappings": ";CAAA,aAAa,EAAG,iBAAoB;A", - "rangeMappings": ";BC;" + "sourcesContent": ["\"Hello World\"; function f() { }; 6"], + "mappings": ";CAAA,aAAa,EAAI,kBAAkB,EAAG;", + "rangeMappings": ";AC;" } diff --git a/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map b/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map index b6f7b44282c3ff..9f9e581b9edd17 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map +++ b/test/fixtures/test426/resources/proposals/range-mappings/newline-semantics.js.map @@ -5,5 +5,5 @@ "sources": ["newline-semantics-original.js"], "sourcesContent": ["1234\n5678"], "mappings": "CAAA;GACG", - "rangeMappings": "B;" + "rangeMappings": "A;" } diff --git a/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map b/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map index 952a3c30b1d084..35d437de38228a 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map +++ b/test/fixtures/test426/resources/proposals/range-mappings/non-full-line-coverage.js.map @@ -4,6 +4,6 @@ "file": "empty-string.js", "sources": ["simple-original.js"], "sourcesContent": ["\"Hello World\""], - "mappings": ";CAAA;A", - "rangeMappings": ";B" + "mappings": ";CAAA;A;;;", + "rangeMappings": ";A" } diff --git a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map index f573e035dca549..9e4d32d34b51c5 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map +++ b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-2.js.map @@ -1,9 +1,9 @@ { "version": 3, "names": [], - "file": "out-of-range.js", + "file": "out-of-range-2.js", "sources": ["foo.js"], "sourcesContent": ["\"foo\""], - "mappings": "AAA", - "rangeMappings": "C;B;B" + "mappings": "AAAA", + "rangeMappings": "B;A;A" } diff --git a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js new file mode 100644 index 00000000000000..7a461fc61277ca --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js @@ -0,0 +1,2 @@ +1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;21;22;23;24;25;26;27;28;29;30;31;31;32;33;34;35;36;37;38;39;40;41;41;42;43;44;45;46;47;48;49;50;51;51;52;53;54;55;56;57;58;59;60;61;61;62;63;64;65;66;67;68;69;70 +//# sourceMappingURL=out-of-range-3.js.map diff --git a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js.map b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js.map new file mode 100644 index 00000000000000..314dfc03781170 --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range-3.js.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "names": [], + "sources": ["out-of-range-3.js"], + "sourcesContent": ["1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;21;22;23;24;25;26;27;28;29;30;31;31;32;33;34;35;36;37;38;39;40;41;41;42;43;44;45;46;47;48;49;50;51;51;52;53;54;55;56;57;58;59;60;61;61;62;63;64;65;66;67;68;69;70"], + "mappings": "AAAA,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG", + "rangeMappings": "mC" +} diff --git a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map index 5da575a7a6355d..f5b61daf071b2f 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map +++ b/test/fixtures/test426/resources/proposals/range-mappings/out-of-range.js.map @@ -4,6 +4,6 @@ "file": "out-of-range.js", "sources": ["foo.js"], "sourcesContent": ["\"foo\""], - "mappings": "AAA", - "rangeMappings": "C" + "mappings": "AAAA", + "rangeMappings": "B" } diff --git a/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map b/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map index 856d31a9c84e14..0cd782bcee0638 100644 --- a/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map +++ b/test/fixtures/test426/resources/proposals/range-mappings/simple.js.map @@ -5,5 +5,5 @@ "sources": ["simple-original.js"], "sourcesContent": ["\"Hello World\""], "mappings": ";CAAA;A", - "rangeMappings": ";B;" + "rangeMappings": ";A;" } diff --git a/test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js b/test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js new file mode 100644 index 00000000000000..137479bc04ca72 --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js @@ -0,0 +1,2 @@ +1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;21;22;23;24;25;26;27;28;29;30;31;31;32;33;34;35;36;37;38;39;40;41;41;42;43;44;45;46;47;48;49;50;51;51;52;53;54;55;56;57;58;59;60;61;61;62;63;64;65;66;67;68;69;70 +//# sourceMappingURL=vlq-continuation-bit.js.map diff --git a/test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js.map b/test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js.map new file mode 100644 index 00000000000000..5b8c1eb071fdd8 --- /dev/null +++ b/test/fixtures/test426/resources/proposals/range-mappings/vlq-continuation-bit.js.map @@ -0,0 +1,8 @@ +{ + "version": 3, + "names": [], + "sources": ["vlq-continuation-bit-original.js"], + "sourcesContent": ["1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;21;22;23;24;25;26;27;28;29;30;31;31;32;33;34;35;36;37;38;39;40;41;41;42;43;44;45;46;47;48;49;50;51;51;52;53;54;55;56;57;58;59;60;61;61;62;63;64;65;66;67;68;69;70"], + "mappings": "AAAA,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG", + "rangeMappings": "AgCF" +} diff --git a/test/test426/README.md b/test/test426/README.md index 00f249c8d36a29..2bc52a566ba924 100644 --- a/test/test426/README.md +++ b/test/test426/README.md @@ -7,7 +7,7 @@ suite ensures that the Node.js source map implementation conforms to the The [`test/fixtures/test426`](../fixtures/test426/) contains a copy of the set of [Source Map Tests][] suite. The last updated hash is: -* +* See the json files in [the `status` folder](./status) for prerequisites, expected failures, and support status for specific tests. From faa413ea58626556c51b6b70e3333f6ea133fa80 Mon Sep 17 00:00:00 2001 From: "Node.js GitHub Bot" Date: Tue, 2 Jun 2026 06:11:45 -0400 Subject: [PATCH 02/12] deps: update googletest to 8736d2cd5c1dcba41170ed2fddca14021d4916c3 PR-URL: https://github.com/nodejs/node/pull/63669 Reviewed-By: Antoine du Hamel Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca --- .../include/gtest/internal/gtest-death-test-internal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/googletest/include/gtest/internal/gtest-death-test-internal.h b/deps/googletest/include/gtest/internal/gtest-death-test-internal.h index 6013a57bfe9f69..f88e2049c249c7 100644 --- a/deps/googletest/include/gtest/internal/gtest-death-test-internal.h +++ b/deps/googletest/include/gtest/internal/gtest-death-test-internal.h @@ -229,7 +229,8 @@ GTEST_API_ bool ExitedUnsuccessfully(int exit_status); goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ } \ if (gtest_dt != nullptr) { \ - std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \ + const std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr( \ + gtest_dt); \ switch (gtest_dt->AssumeRole()) { \ case ::testing::internal::DeathTest::OVERSEE_TEST: \ if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ From ba7e6b3c479fd5f917e9a34ef5694255bcb99071 Mon Sep 17 00:00:00 2001 From: Mohamed Sayed <73386311+3zrv@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:25:47 +0300 Subject: [PATCH 03/12] lib: make `Navigator#language` getter throw on invalid `this` Signed-off-by: Mohamed Sayed PR-URL: https://github.com/nodejs/node/pull/63601 Fixes: https://github.com/nodejs/node/issues/63513 Reviewed-By: LiviaMedeiros Reviewed-By: Matthew Aitken Reviewed-By: Yagiz Nizipli Reviewed-By: Antoine du Hamel --- lib/internal/navigator.js | 7 ++++++- test/parallel/test-navigator.js | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/internal/navigator.js b/lib/internal/navigator.js index 2904779a47e64b..d8681c89990494 100644 --- a/lib/internal/navigator.js +++ b/lib/internal/navigator.js @@ -79,7 +79,8 @@ function getNavigatorPlatform(arch, platform) { } class Navigator { - // Private properties are used to avoid brand validations. + // Private properties are used to avoid brand validations: reading a private + // field from a non-Navigator receiver throws a TypeError on its own. #availableParallelism; #locks; #userAgent; @@ -113,6 +114,10 @@ class Navigator { * @returns {string} */ get language() { + // `language` does not read a private field, so brand-check explicitly + // to keep parity with the other getters when called on a non-Navigator + // receiver (e.g. `Navigator.prototype.language`). + this.#languages; // eslint-disable-line no-unused-expressions // The default locale might be changed dynamically, so always invoke the // binding. return getDefaultLocale() || 'en-US'; diff --git a/test/parallel/test-navigator.js b/test/parallel/test-navigator.js index 01c5595b93cd12..82e3fe903d455f 100644 --- a/test/parallel/test-navigator.js +++ b/test/parallel/test-navigator.js @@ -147,3 +147,19 @@ Object.defineProperty(navigator, 'language', { value: 'for-testing' }); assert.strictEqual(navigator.language, 'for-testing'); assert.strictEqual(navigator.languages.length, 1); assert.strictEqual(navigator.languages[0] !== 'for-testing', true); + +{ + const { Navigator } = globalThis; + for (const name of Object.keys(Navigator.prototype)) { + assert.throws( + () => Navigator.prototype[name], + { name: 'TypeError' }, + `expected TypeError when reading ${name} on Navigator.prototype`, + ); + assert.throws( + () => Reflect.get(Navigator.prototype, name, {}), + { name: 'TypeError' }, + `expected TypeError when reading ${name} on a plain object`, + ); + } +} From 29aad20aa674329b6bf5d0a306427f0e6fe51d65 Mon Sep 17 00:00:00 2001 From: sangwook Date: Sun, 3 May 2026 21:57:51 +0900 Subject: [PATCH 04/12] test_runner: cache `shouldSkipFileCoverage` result per URL `shouldSkipFileCoverage(url)` is invoked twice for every covered script (once during source-map mapping and once during merge), and the same script URL is typically reported by every worker. The result depends only on `options.cwd`, `coverageExcludeGlobs`, and `coverageIncludeGlobs`, all of which are fixed for the lifetime of a TestCoverage instance, so the URL -> boolean mapping is deterministic and safe to cache. Add a private `#skipCache` SafeMap and split the method into a thin caching wrapper plus a private `#computeShouldSkipFileCoverage` that holds the original logic. Callers are unchanged. This eliminates redundant glob and URL parsing work proportional to the number of workers x scripts in the coverage report. Refs: https://github.com/nodejs/node/issues/55103 Signed-off-by: sangwook PR-URL: https://github.com/nodejs/node/pull/63675 Reviewed-By: Matteo Collina Reviewed-By: Moshe Atlow Reviewed-By: Chemi Atlow --- lib/internal/test_runner/coverage.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 37ff2473b68011..755f54b30df70a 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -85,6 +85,7 @@ class TestCoverage { #sourceLines = new SafeMap(); #typeScriptLines = new SafeSet(); + #skipCache = new SafeMap(); getLines(fileUrl, source) { // Split the file source into lines. Make sure the lines maintain their @@ -534,6 +535,14 @@ class TestCoverage { } shouldSkipFileCoverage(url) { + const cached = this.#skipCache.get(url); + if (cached !== undefined) return cached; + const result = this.#computeShouldSkipFileCoverage(url); + this.#skipCache.set(url, result); + return result; + } + + #computeShouldSkipFileCoverage(url) { // This check filters out core modules, which start with 'node:' in // coverage reports, as well as any invalid coverages which have been // observed on Windows. From 75b16f1f4ed2984b304c015ca39f5533d8473336 Mon Sep 17 00:00:00 2001 From: sangwook Date: Sun, 31 May 2026 16:30:27 +0900 Subject: [PATCH 05/12] test_runner: avoid recompiling coverage globs for every file `shouldSkipFileCoverage()` called `matchGlobPattern()` for each covered file, and `matchGlobPattern()` builds a fresh Minimatch (a full glob parse and regexp compile) on every call. The coverage exclude/include globs never change during a run, so the same patterns were recompiled once per file, which dominated the coverage report time. Compile `coverageExcludeGlobs`/`coverageIncludeGlobs` to matchers once per `TestCoverage` instance and reuse them for every file. Expose `createMatcher()` from `internal/fs/glob` for this. On a synthetic 200-test-file project this drops shouldSkipFileCoverage from ~117ms to ~11ms (cpu-prof, isolation=none); the saving scales with files * globs. Refs: https://github.com/nodejs/node/issues/55103 Signed-off-by: sangwook PR-URL: https://github.com/nodejs/node/pull/63675 Reviewed-By: Matteo Collina Reviewed-By: Moshe Atlow Reviewed-By: Chemi Atlow --- lib/internal/fs/glob.js | 1 + lib/internal/test_runner/coverage.js | 37 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/lib/internal/fs/glob.js b/lib/internal/fs/glob.js index c5bbdb9813c0d1..6cda2c9e6da073 100644 --- a/lib/internal/fs/glob.js +++ b/lib/internal/fs/glob.js @@ -947,5 +947,6 @@ function matchGlobPattern(path, pattern, windows = isWindows) { module.exports = { __proto__: null, Glob, + createMatcher, matchGlobPattern, }; diff --git a/lib/internal/test_runner/coverage.js b/lib/internal/test_runner/coverage.js index 755f54b30df70a..798e41f0ac4be6 100644 --- a/lib/internal/test_runner/coverage.js +++ b/lib/internal/test_runner/coverage.js @@ -37,7 +37,7 @@ const { ERR_SOURCE_MAP_MISSING_SOURCE, }, } = require('internal/errors'); -const { matchGlobPattern } = require('internal/fs/glob'); +const { createMatcher } = require('internal/fs/glob'); const { constants: { kMockSearchParam } } = require('internal/test_runner/mock/loader'); const kCoverageFileRegex = /^coverage-(\d+)-(\d{13})-(\d+)\.json$/; @@ -86,6 +86,8 @@ class TestCoverage { #sourceLines = new SafeMap(); #typeScriptLines = new SafeSet(); #skipCache = new SafeMap(); + #excludeMatchers = null; + #includeMatchers = null; getLines(fileUrl, source) { // Split the file source into lines. Make sure the lines maintain their @@ -550,28 +552,27 @@ class TestCoverage { const absolutePath = fileURLToPath(url); const relativePath = relative(this.options.cwd, absolutePath); - const { - coverageExcludeGlobs: excludeGlobs, - coverageIncludeGlobs: includeGlobs, - } = this.options; + // The exclude/include globs are fixed for the lifetime of this + // TestCoverage instance, so compile each glob to a matcher once and reuse + // it for every file. Building a fresh Minimatch per call (the previous + // behavior) dominated the coverage report time, scaling with + // files * globs. + this.#excludeMatchers ??= ArrayPrototypeMap( + this.options.coverageExcludeGlobs ?? [], (pattern) => createMatcher(pattern)); + this.#includeMatchers ??= ArrayPrototypeMap( + this.options.coverageIncludeGlobs ?? [], (pattern) => createMatcher(pattern)); // This check filters out files that match the exclude globs. - if (excludeGlobs?.length > 0) { - for (let i = 0; i < excludeGlobs.length; ++i) { - if ( - matchGlobPattern(relativePath, excludeGlobs[i]) || - matchGlobPattern(absolutePath, excludeGlobs[i]) - ) return true; - } + for (let i = 0; i < this.#excludeMatchers.length; ++i) { + const matcher = this.#excludeMatchers[i]; + if (matcher.match(relativePath) || matcher.match(absolutePath)) return true; } // This check filters out files that do not match the include globs. - if (includeGlobs?.length > 0) { - for (let i = 0; i < includeGlobs.length; ++i) { - if ( - matchGlobPattern(relativePath, includeGlobs[i]) || - matchGlobPattern(absolutePath, includeGlobs[i]) - ) return false; + if (this.#includeMatchers.length > 0) { + for (let i = 0; i < this.#includeMatchers.length; ++i) { + const matcher = this.#includeMatchers[i]; + if (matcher.match(relativePath) || matcher.match(absolutePath)) return false; } return true; } From fd632cf96e75c4d2a88c905adcf41befc65b801a Mon Sep 17 00:00:00 2001 From: Rafael Gonzaga Date: Tue, 2 Jun 2026 09:59:55 -0300 Subject: [PATCH 06/12] test: add more test cases for pathToFileURL Signed-off-by: RafaelGSS PR-URL: https://github.com/nodejs/node/pull/63293 Reviewed-By: Antoine du Hamel --- test/parallel/test-url-pathtofileurl.js | 62 +++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/test/parallel/test-url-pathtofileurl.js b/test/parallel/test-url-pathtofileurl.js index 01d27f2b736d75..290cac41037ca9 100644 --- a/test/parallel/test-url-pathtofileurl.js +++ b/test/parallel/test-url-pathtofileurl.js @@ -22,11 +22,63 @@ const url = require('url'); } { - assert.throws(() => { - url.pathToFileURL('\\\\exa mple\\share\\file.txt', { windows: true }); - }, { - code: 'ERR_INVALID_URL', - }); + const forbiddenHostnameChars = [ + '\\\\exa mple\\share\\file.txt', + '\\\\host name\\share\\file.txt', // space + '\\\\host@name\\share\\file.txt', // at sign + '\\\\host:name\\share\\file.txt', // colon + '\\\\host[name\\share\\file.txt', // left bracket + '\\\\host]name\\share\\file.txt', // right bracket + ]; + for (const path of forbiddenHostnameChars) { + assert.throws(() => { + url.pathToFileURL(path, { windows: true }); + }, { + code: 'ERR_INVALID_URL', + }, `pathToFileURL('${path}') should throw ERR_INVALID_URL`); + } +} + +{ + const hostnameStateTerminatorTestCases = [ + { + path: '\\\\host#name\\share\\file.txt', + expected: 'file://host/share/file.txt', + }, + { + path: '\\\\host?name\\share\\file.txt', + expected: 'file://host/share/file.txt', + }, + { + path: '\\\\host/name\\share\\file.txt', + expected: 'file://host/share/file.txt', + }, + ]; + for (const { path, expected } of hostnameStateTerminatorTestCases) { + const actual = url.pathToFileURL(path, { windows: true }).href; + assert.strictEqual(actual, expected); + } +} + +{ + const ignoredHostnameCodePointTestCases = [ + { + path: '\\\\host\nname\\share\\file.txt', + expected: 'file://hostname/share/file.txt', + }, + { + path: '\\\\host\rname\\share\\file.txt', + expected: 'file://hostname/share/file.txt', + }, + { + path: '\\\\host\tname\\share\\file.txt', + expected: 'file://hostname/share/file.txt', + }, + ]; + for (const { path, expected } of ignoredHostnameCodePointTestCases) { + const actual = url.pathToFileURL(path, { windows: true }).href; + assert.strictEqual(actual, expected); + } } { From 458c37b019c8c9a88124b591ca398267e7d784b0 Mon Sep 17 00:00:00 2001 From: Nora Dossche <7771979+ndossche@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:00:09 +0200 Subject: [PATCH 07/12] src: fix edge case when deflateInit2() fails with Z_VERSION_ERROR This function call can fail with `Z_VERSION_ERROR` if the compiled library vs loaded library mismatched in version number or in stream structure size. In those cases, zlib doesn't initialize the `strm_.msg` field to null. Therefore, when a `CompressionError` object is created via `ErrorForMessage()`, it can read a stale or uninitialized `strm_.msg` pointer that will cause a crash. Example ASAN report: ``` AddressSanitizer: SEGV on unknown address #0 __strlen_avx2 string/../sysdeps/x86_64/multiarch/strlen-avx2.S:76 #1 strlen (/work/node/out/Debug/node+0x1a42ab7) #2 v8::(anonymous namespace)::StringLength(char const*) /work/node/out/../deps/v8/src/api/api.cc:7581:16 #3 v8::(anonymous namespace)::StringLength(unsigned char const*) /work/node/out/../deps/v8/src/api/api.cc:7587:10 #4 v8::String::NewFromOneByte(v8::Isolate*, unsigned char const*, v8::NewStringType, int) /work/node/out/../deps/v8/src/api/api.cc:7677:3 #5 node::OneByteString(v8::Isolate*, char const*, int, v8::NewStringType) /work/node/out/../src/util-inl.h:166:10 #6 node::(anonymous namespace)::CompressionStream< node::(anonymous namespace)::ZlibContext> ::EmitError(node::(anonymous namespace) ::CompressionError const&) /work/node/out/../src/node_zlib.cc:565:7 #7 node::(anonymous namespace)::CompressionStream< node::(anonymous namespace)::ZlibContext> ::CheckError() /work/node/out/../src/node_zlib.cc:519:5 #8 node::(anonymous namespace)::CompressionStream< node::(anonymous namespace)::ZlibContext> ::AfterThreadPoolWork(int) /work/node/out/../src/node_zlib.cc:543:10 #9 node::ThreadPoolWork::ScheduleWork() ::'lambda'(uv_work_s*, int) ::operator()(uv_work_s*, int) const /work/node/out/../src/threadpoolwork-inl.h:57:15 #10 node::ThreadPoolWork::ScheduleWork() ::'lambda'(uv_work_s*, int) ::__invoke(uv_work_s*, int) /work/node/out/../src/threadpoolwork-inl.h:48:7 #11 uv__work_done /work/libuv-1.51.0/src/threadpool.c:330:5 #12 uv__async_io.part.0 /work/libuv-1.51.0/src/unix/async.c:208:5 ``` Signed-off-by: ndossche PR-URL: https://github.com/nodejs/node/pull/63476 Reviewed-By: Anna Henningsen --- src/node_zlib.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 792d800847e105..9774c847ee50da 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -1285,6 +1285,11 @@ bool ZlibContext::InitZlib() { return false; } + // If deflateInit2() fails with Z_VERSION_ERROR, msg remains uninitialized. + // Initialize it here to avoid reading the uninitialized pointer during error + // emission. + strm_.msg = nullptr; + switch (mode_) { case DEFLATE: case GZIP: From 60dab8d2e3263e54165308a8dae2487e550d7d55 Mon Sep 17 00:00:00 2001 From: alphaleadership <47387699+alphaleadership@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:24:30 +0200 Subject: [PATCH 08/12] doc: update `blockList` stability status to release candidate Signed-off-by: arbinger PR-URL: https://github.com/nodejs/node/pull/63050 Reviewed-By: James M Snell Reviewed-By: Ethan Arrowood --- doc/api/net.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/net.md b/doc/api/net.md index 9b330a948019ab..e1c77130a26ac6 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -183,7 +183,7 @@ added: ### `blockList.fromJSON(value)` -> Stability: 1 - Experimental +> Stability: 1.2 - Release candidate + + > Stability: 2 - Stable