diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index aee7de99086bd1..e2b5f763df7dd9 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -270,6 +270,8 @@ const { Array: ArrayConstructor, ArrayPrototypeForEach, ArrayPrototypeMap, + ArrayPrototypePushApply, + ArrayPrototypeSlice, FinalizationRegistry, FunctionPrototypeCall, Map, @@ -720,5 +722,26 @@ primordials.SafeStringPrototypeSearch = (str, regexp) => { return match ? match.index : -1; }; +/** + * Variadic functions with lots of arguments will cause stack overflow errors. + * Use this function when `items` can be arbitrarily large, this function splits + * it into chunks of size 2**16 making stack overflow less likely. + * @param {Array} arr + * @param {Parameters} items + * @returns {ReturnType} + */ +primordials.SafeArrayPrototypePushApply = (arr, items) => { + let end = 0x10000; + if (end < items.length) { + let start = 0; + do { + ArrayPrototypePushApply(arr, ArrayPrototypeSlice(items, start, start = end)); + end += 0x10000; + } while (end < items.length); + items = ArrayPrototypeSlice(items, start); + } + return ArrayPrototypePushApply(arr, items); +}; + ObjectSetPrototypeOf(primordials, null); ObjectFreeze(primordials); diff --git a/lib/internal/perf/observe.js b/lib/internal/perf/observe.js index 3e992b8f3f9576..58eca95d9de710 100644 --- a/lib/internal/perf/observe.js +++ b/lib/internal/perf/observe.js @@ -6,7 +6,6 @@ const { ArrayPrototypeFilter, ArrayPrototypeIncludes, ArrayPrototypePush, - ArrayPrototypePushApply, ArrayPrototypeSlice, ArrayPrototypeSort, Error, @@ -14,6 +13,7 @@ const { MathMin, ObjectDefineProperties, ObjectFreeze, + SafeArrayPrototypePushApply, SafeMap, SafeSet, Symbol, @@ -300,7 +300,7 @@ class PerformanceObserver { maybeIncrementObserverCount(type); if (buffered) { const entries = filterBufferMapByNameAndType(undefined, type); - ArrayPrototypePushApply(this.#buffer, entries); + SafeArrayPrototypePushApply(this.#buffer, entries); kPending.add(this); if (kPending.size) queuePending(); @@ -507,9 +507,9 @@ function filterBufferMapByNameAndType(name, type) { return []; } else { bufferList = []; - ArrayPrototypePushApply(bufferList, markEntryBuffer); - ArrayPrototypePushApply(bufferList, measureEntryBuffer); - ArrayPrototypePushApply(bufferList, resourceTimingBuffer); + SafeArrayPrototypePushApply(bufferList, markEntryBuffer); + SafeArrayPrototypePushApply(bufferList, measureEntryBuffer); + SafeArrayPrototypePushApply(bufferList, resourceTimingBuffer); } if (name !== undefined) { bufferList = ArrayPrototypeFilter(bufferList, (buffer) => buffer.name === name); diff --git a/test/parallel/test-performance-many-marks.js b/test/parallel/test-performance-many-marks.js new file mode 100644 index 00000000000000..0156d31780cf09 --- /dev/null +++ b/test/parallel/test-performance-many-marks.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); + +for (let i = 0; i < 1e6; i++) { + performance.mark(`mark-${i}`); +} + +performance.getEntriesByName('mark-0'); +performance.clearMarks(); diff --git a/test/parallel/test-primordials-apply.js b/test/parallel/test-primordials-apply.js index 0901a87ba18777..fae672f6f60c45 100644 --- a/test/parallel/test-primordials-apply.js +++ b/test/parallel/test-primordials-apply.js @@ -10,6 +10,7 @@ const { ArrayPrototypeUnshiftApply, MathMaxApply, MathMinApply, + SafeArrayPrototypePushApply, StringPrototypeConcatApply, TypedArrayOfApply, } = require('internal/test/binding').primordials; @@ -43,6 +44,26 @@ const { assert.deepStrictEqual(arr1, expected); } +{ + const arr1 = [1, 2, 3]; + const arr2 = [4, 5, 6]; + + const expected = [...arr1, ...arr2]; + + assert.strictEqual(SafeArrayPrototypePushApply(arr1, arr2), expected.length); + assert.deepStrictEqual(arr1, expected); +} + +{ + const arr1 = [1, 2, 3]; + const arr2 = Array.from({ length: 1e6 }, (_, i) => i); + + const expected = [...arr1, ...arr2]; + + assert.strictEqual(SafeArrayPrototypePushApply(arr1, arr2), expected.length); + assert.deepStrictEqual(arr1, expected); +} + { const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; diff --git a/typings/primordials.d.ts b/typings/primordials.d.ts index ed77d4112e2984..204c12b0087f91 100644 --- a/typings/primordials.d.ts +++ b/typings/primordials.d.ts @@ -374,6 +374,7 @@ declare namespace primordials { export const RegExpPrototypeGetUnicode: UncurryGetter; export const RegExpPrototypeSymbolReplace: UncurryMethod export const RegExpPrototypeSymbolSplit: UncurryMethod + export const SafeArrayPrototypePushApply: typeof ArrayPrototypePushApply; export import Set = globalThis.Set; export const SetLength: typeof Set.length export const SetName: typeof Set.name