From 087c86a7844bba7edf6115b66bdaddb1b52f7ff6 Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:50:35 +0000 Subject: [PATCH 1/2] Optimize ExpectCallTransformer.transform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optimized code achieves a **6,675% speedup** (from 789ms to 11.6ms) by replacing an expensive O(n²) pattern with an O(n log n) approach for detecting whether regex matches occur inside string literals. **Original Performance Bottleneck:** The original code called `is_inside_string(code, pos)` for every regex match found. This function linearly scanned from position 0 to `pos` each time, checking character-by-character whether the position was inside a string literal. Line profiler shows `is_inside_string` consumed **~10 seconds** (99.6% of total time in `transform`), with ~14.6 million character checks across all calls. **Key Optimization:** The optimized version introduces two new methods: 1. **`_compute_string_spans(code)`** - Performs a single O(n) pass over the entire source code at the start of `transform()`, identifying all string literal regions and storing them as sorted `(start, end)` span lists 2. **`_pos_inside_spans(pos, starts, ends)`** - Uses `bisect.bisect_right()` to perform O(log n) binary search lookups against the precomputed spans **Why This Works:** - **Eliminates redundant scanning**: Instead of scanning the code prefix 1,641 times (once per match), we scan once and cache the results - **Logarithmic lookups**: Each string-check becomes O(log n) binary search instead of O(n) linear scan - **Optimal for multiple matches**: The more regex matches in the code, the greater the benefit. Test cases with 100-1000 expect calls show 1,000%+ speedups **Test Results Show Clear Pattern:** - Small files (1-3 matches): 7-45% slower due to upfront span computation overhead - Medium files (50-100 matches): 565-1,151% faster - Large files (200-1000 matches): 1,836-11,489% faster The optimization is particularly effective for the tool's primary use case: instrumenting JavaScript test files with many `expect()` calls, where the original quadratic behavior became prohibitively expensive. --- codeflash/languages/javascript/instrument.py | 72 +++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/codeflash/languages/javascript/instrument.py b/codeflash/languages/javascript/instrument.py index dee534044..f7022480d 100644 --- a/codeflash/languages/javascript/instrument.py +++ b/codeflash/languages/javascript/instrument.py @@ -6,6 +6,7 @@ from __future__ import annotations +import bisect import re from dataclasses import dataclass from pathlib import Path @@ -487,14 +488,18 @@ def transform(self, code: str) -> str: result: list[str] = [] pos = 0 - while pos < len(code): + # Precompute string spans once per transform invocation for fast lookups + starts, ends = self._compute_string_spans(code) + + n = len(code) + while pos < n: match = self._expect_pattern.search(code, pos) if not match: result.append(code[pos:]) break # Skip if inside a string literal (e.g., test description) - if is_inside_string(code, match.start()): + if self._pos_inside_spans(match.start(), starts, ends): result.append(code[pos : match.end()]) pos = match.end() continue @@ -730,6 +735,69 @@ def _generate_transformed_call(self, match: ExpectCallMatch) -> str: ) + def _compute_string_spans(self, code: str) -> tuple[list[int], list[int]]: + """Compute inclusive/exclusive spans for string contents. + + Spans represent the region of code that would be considered 'inside' a string + by the original is_inside_string behavior: start is the first position after + the opening quote, end is the position after the closing quote (or len(code) + if no closing quote is found). A position pos is considered inside a string + if start <= pos < end. + + This function handles escapes (\\) similarly to the original function and + treats backticks (`) like other quotes (no special ${} handling to match original). + """ + starts: list[int] = [] + ends: list[int] = [] + i = 0 + n = len(code) + + while i < n: + ch = code[i] + if ch in "\"'`": + # Determine if this quote is escaped + # A quote is escaped if the preceding char is an odd number of backslashes. + # Simpler: check immediate preceding char only (original code only checked immediate preceding char). + # To preserve behavior, we follow original: only treat as escaped if code[i-1] == '\\' + if i > 0 and code[i - 1] == "\\": + i += 1 + continue + + start = i + 1 # start of content (pos equal to quote itself is considered outside) + quote_char = ch + i += 1 + # Scan until closing quote or end + while i < n: + c = code[i] + if c == "\\" and i + 1 < n: + i += 2 + continue + if c == quote_char: + i += 1 + break + i += 1 + end = i # position after closing quote or n if not closed + starts.append(start) + ends.append(end) + else: + i += 1 + + return starts, ends + + def _pos_inside_spans(self, pos: int, starts: list[int], ends: list[int]) -> bool: + """Return True if pos is inside any (start, end) span using binary search. + + Spans are expected to be non-overlapping and sorted by start. + """ + if not starts: + return False + idx = bisect.bisect_right(starts, pos) + if idx == 0: + return False + si = idx - 1 + return pos < ends[si] + + def transform_expect_calls( code: str, function_to_optimize: FunctionToOptimize, capture_func: str, remove_assertions: bool = False ) -> tuple[str, int]: From 6b27ccf9479044bead5a9a3035dc06c07bd9c2f5 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 12 Feb 2026 16:53:43 +0000 Subject: [PATCH 2/2] style: fix D301 linting error in docstring --- codeflash/languages/javascript/instrument.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/codeflash/languages/javascript/instrument.py b/codeflash/languages/javascript/instrument.py index f7022480d..1af809411 100644 --- a/codeflash/languages/javascript/instrument.py +++ b/codeflash/languages/javascript/instrument.py @@ -734,9 +734,8 @@ def _generate_transformed_call(self, match: ExpectCallMatch) -> str: f"'{line_id}', {func_ref})){match.assertion_chain}{semicolon}" ) - def _compute_string_spans(self, code: str) -> tuple[list[int], list[int]]: - """Compute inclusive/exclusive spans for string contents. + r"""Compute inclusive/exclusive spans for string contents. Spans represent the region of code that would be considered 'inside' a string by the original is_inside_string behavior: start is the first position after