From 4c5a4215f81f697f35ab330b47190cbb31e782c7 Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Mon, 29 Mar 2021 12:07:23 -0700 Subject: [PATCH] fix maximum call stack size reached issue (#14878) * fix maximum call stack size reached issue * Revert "fix maximum call stack size reached issue" This reverts commit 178675633032a508ddb5585d1adc4f83bb243f55. * add a few array operations * use new push --- .../contrib/query/browser/messagePanel.ts | 3 +- src/vs/base/browser/ui/tree/indexTreeModel.ts | 4 +- src/vs/base/common/arrays.ts | 75 +++++++++++++++++++ src/vs/base/test/common/arrays.test.ts | 42 +++++++++++ 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/src/sql/workbench/contrib/query/browser/messagePanel.ts b/src/sql/workbench/contrib/query/browser/messagePanel.ts index cae9750ed6..a436a02c65 100644 --- a/src/sql/workbench/contrib/query/browser/messagePanel.ts +++ b/src/sql/workbench/contrib/query/browser/messagePanel.ts @@ -34,6 +34,7 @@ import { IDataTreeViewState } from 'vs/base/browser/ui/tree/dataTree'; import { IRange } from 'vs/editor/common/core/range'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQueryEditorConfiguration } from 'sql/platform/query/common/query'; +import { push } from 'vs/base/common/arrays'; export interface IResultMessageIntern { id?: string; @@ -201,7 +202,7 @@ export class MessagePanel extends Disposable { private onMessage(message: IQueryMessage | IQueryMessage[], setInput: boolean = false) { if (isArray(message)) { - this.model.messages.push(...message); + push(this.model.messages, message); } else { this.model.messages.push(message); } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 3fc133487f..ba9b016289 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree'; -import { tail2 } from 'vs/base/common/arrays'; +import { splice, tail2 } from 'vs/base/common/arrays'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { ISpliceable } from 'vs/base/common/sequence'; @@ -149,7 +149,7 @@ export class IndexTreeModel, TFilterData = voi } } - const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); + const deletedNodes = splice(parentNode.children, lastIndex, deleteCount, nodesToInsert); // figure out what is the count of deleted visible children let deletedVisibleChildrenCount = 0; diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index a319a85728..8add598553 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -612,3 +612,78 @@ export function mapFind(array: Iterable, mapFn: (value: T) => R | undef return undefined; } + +/** + * An alternative for Array.push(...items) method, Use this if you need to push large number of items to the array. + * Array.push(...item) can only support limited number of items due to the maximum call stack size limit. + * @param array The array to add items to. + * @param items The new items to be added. + */ +export function push(array: T[], items: T[]): void { + // set array length and then assign value at index is faster than doing array.push(item) individually + const newLength = array.length + items.length; + const startIdx = array.length; + array.length = newLength; + items.forEach((item, index) => { + array[startIdx + index] = item; + }); +} + +/** + * Insert the new items to array, the insertion be performed on the original array directly, the alternative insertArray() method will return a new array. + * @param array The original array. + * @param start The zero-based location in the array from which to start inserting elements. + * @param newItems The items to be inserted + */ +export function insertArray2(array: T[], start: number, newItems: T[]): void { + const startIdx = getActualStartIndex(array, start); + const originalLength = array.length; + const newItemsLength = newItems.length; + array.length = originalLength + newItemsLength; + // Move the items after the start index, start from the end so that we don't overwrite any value. + for (let i = originalLength - 1; i >= startIdx; i--) { + array[i + newItemsLength] = array[i]; + } + + for (let i = 0; i < newItemsLength; i++) { + array[i + startIdx] = newItems[i]; + } +} + +/** + * Removes elements from an array and inserts new elements in their place, returning the deleted elements. Alternative to the native Array.splice method, it + * can only support limited number of items due to the maximum call stack size limit. + * @param array The original array. + * @param start The zero-based location in the array from which to start removing elements. + * @param deleteCount The number of elements to remove. + * @returns An array containing the elements that were deleted. + */ +export function splice(array: T[], start: number, deleteCount: number, newItems: T[]): T[] { + const startIdx = getActualStartIndex(array, start); + const deletedItems = array.splice(startIdx, deleteCount); + insertArray2(array, startIdx, newItems); + return deletedItems; +} + +/** + * Determine the actual start index (same logic as the native splice() or slice()) + * If greater than the length of the array, start will be set to the length of the array. In this case, no element will be deleted but the method will behave as an adding function, adding as many element as item[n*] provided. + * If negative, it will begin that many elements from the end of the array. (In this case, the origin -1, meaning -n is the index of the nth last element, and is therefore equivalent to the index of array.length - n.) If array.length + start is less than 0, it will begin from index 0. + * @param array The target array. + * @param start The operation index. + */ +function getActualStartIndex(array: T[], start: number): number { + let startIndex: number; + if (start > array.length) { + startIndex = array.length; + } else if (start < 0) { + if ((start + array.length) < 0) { + startIndex = 0; + } else { + startIndex = start + array.length; + } + } else { + startIndex = start; + } + return startIndex; +} diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index fa1b1aea1d..02d78eb21a 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -369,4 +369,46 @@ suite('Arrays', () => { remove(); assert.equal(array.length, 0); }); + + test('splice', function () { + // negative start index, absolute value greater than the length + let array = [1, 2, 3, 4, 5]; + arrays.splice(array, -6, 3, [6, 7]); + assert.strictEqual(array.length, 4); + assert.strictEqual(array[0], 6); + assert.strictEqual(array[1], 7); + assert.strictEqual(array[2], 4); + assert.strictEqual(array[3], 5); + + // negative start index, absolute value less than the length + array = [1, 2, 3, 4, 5]; + arrays.splice(array, -3, 3, [6, 7]); + assert.strictEqual(array.length, 4); + assert.strictEqual(array[0], 1); + assert.strictEqual(array[1], 2); + assert.strictEqual(array[2], 6); + assert.strictEqual(array[3], 7); + + // Start index less than the length + array = [1, 2, 3, 4, 5]; + arrays.splice(array, 3, 3, [6, 7]); + assert.strictEqual(array.length, 5); + assert.strictEqual(array[0], 1); + assert.strictEqual(array[1], 2); + assert.strictEqual(array[2], 3); + assert.strictEqual(array[3], 6); + assert.strictEqual(array[4], 7); + + // Start index greater than the length + array = [1, 2, 3, 4, 5]; + arrays.splice(array, 6, 3, [6, 7]); + assert.strictEqual(array.length, 7); + assert.strictEqual(array[0], 1); + assert.strictEqual(array[1], 2); + assert.strictEqual(array[2], 3); + assert.strictEqual(array[3], 4); + assert.strictEqual(array[4], 5); + assert.strictEqual(array[5], 6); + assert.strictEqual(array[6], 7); + }); });