diff --git a/apps/oxlint/src-js/plugins/comments.ts b/apps/oxlint/src-js/plugins/comments.ts index d4e8eef8eb980..73cc3f1505911 100644 --- a/apps/oxlint/src-js/plugins/comments.ts +++ b/apps/oxlint/src-js/plugins/comments.ts @@ -53,7 +53,9 @@ export const cachedComments: Comment[] = []; let previousComments: Comment[] = []; // Comments whose `loc` property has been accessed, and therefore needs clearing on reset. +// Never shrunk - `activeCommentsWithLocCount` tracks the active count to avoid freeing the backing store. const commentsWithLoc: Comment[] = []; +let activeCommentsWithLocCount = 0; // Tracks indices of deserialized comments so their `value` can be cleared on reset, // preventing source text strings from being held alive by stale `value` slices. @@ -111,7 +113,17 @@ class Comment implements Span { const loc = this.#loc; if (loc !== null) return loc; - commentsWithLoc.push(this); + // Store comment in `commentsWithLoc` array. `resetComments` will clear the `#loc` property. + // Note: The comparison `activeCommentsWithLocCount < commentsWithLoc.length` must be this way around + // so that V8 can remove the bounds check on `commentsWithLoc[activeCommentsWithLocCount]`. + // `commentsWithLoc.length > activeCommentsWithLocCount` would *not* remove the bounds check in Maglev compiler. + if (activeCommentsWithLocCount < commentsWithLoc.length) { + commentsWithLoc[activeCommentsWithLocCount] = this; + } else { + commentsWithLoc.push(this); + } + activeCommentsWithLocCount++; + return (this.#loc = computeLoc(this.start, this.end)); } @@ -411,11 +423,11 @@ export function resetComments(): void { } // Reset `#loc` on comments where `loc` has been accessed - for (let i = 0, len = commentsWithLoc.length; i < len; i++) { + for (let i = 0; i < activeCommentsWithLocCount; i++) { resetCommentLoc(commentsWithLoc[i]); } - commentsWithLoc.length = 0; + activeCommentsWithLocCount = 0; // Reset other state comments = null; diff --git a/apps/oxlint/src-js/plugins/tokens.ts b/apps/oxlint/src-js/plugins/tokens.ts index 89e448094a117..8caad1d73577e 100644 --- a/apps/oxlint/src-js/plugins/tokens.ts +++ b/apps/oxlint/src-js/plugins/tokens.ts @@ -117,8 +117,10 @@ export const cachedTokens: Token[] = []; // Reused for next file if next file has less tokens than the previous file (by truncating it to correct length). let previousTokens: Token[] = []; -// Tokens whose `loc` property has been accessed, and therefore needs clearing on reset +// Tokens whose `loc` property has been accessed, and therefore needs clearing on reset. +// Never shrunk - `activeTokensWithLocCount` tracks the active count to avoid freeing the backing store. const tokensWithLoc: Token[] = []; +let activeTokensWithLocCount = 0; // Cached regex descriptor objects, reused across files const regexObjects: Regex[] = []; @@ -182,7 +184,17 @@ class Token { const loc = this.#loc; if (loc !== null) return loc; - tokensWithLoc.push(this); + // Store token in `tokensWithLoc` array. `resetTokens` will clear the `#loc` property. + // Note: The comparison `activeTokensWithLocCount < tokensWithLoc.length` must be this way around + // so that V8 can remove the bounds check on `tokensWithLoc[activeTokensWithLocCount]`. + // `tokensWithLoc.length > activeTokensWithLocCount` would *not* remove the bounds check in Maglev compiler. + if (activeTokensWithLocCount < tokensWithLoc.length) { + tokensWithLoc[activeTokensWithLocCount] = this; + } else { + tokensWithLoc.push(this); + } + activeTokensWithLocCount++; + return (this.#loc = computeLoc(this.start, this.end)); } @@ -541,11 +553,11 @@ export function resetTokens() { } // Reset `#loc` on tokens where `loc` has been accessed - for (let i = 0, len = tokensWithLoc.length; i < len; i++) { + for (let i = 0; i < activeTokensWithLocCount; i++) { resetLoc(tokensWithLoc[i]); } - tokensWithLoc.length = 0; + activeTokensWithLocCount = 0; // Reset `regex` property on regex tokens. // This avoids having to set it for every non-regex token in `deserializeTokenIfNeeded`.