From 864300fdb3a49c2d9d43daef07c64b1c6f85282b Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 21:36:55 +0000 Subject: [PATCH] Optimize JsxRenderCallTransformer.transform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runtime improvement (primary): The optimized version reduces end-to-end transform time from 904 ms to 9.21 ms (~98× faster; reported 9714% speedup). Line-profiling confirms the hot loop that previously dominated (string-state checks) is now a one-time linear pass instead of being repeated for every regex match. What changed (concrete optimizations) - Precompute string-inside table: instead of calling is_inside_string(code, pos) for every regex match (which re-scanned the prefix repeatedly), the optimized transform builds an inside_flags list of length n+1 in a single forward O(n) pass. Subsequent checks become O(1) array lookups. - Avoid extra substring allocation: replaced code[match.start():].index("(") with code.find("(", match.start()) so we don't create a large slice just to find the parenthesis. - Single forward pointer in precompute: the incremental j pointer simulates the original is_inside_string scan exactly but only once, preserving behavior while avoiding repeated scans. Why it is faster (performance reasoning) - Complexity reduction: original behavior effectively did repeated scanning of the source to determine string state for each match (O(matches * scan)), which becomes quadratic-like on pathological inputs. The optimized version does a single linear scan over the input (O(n)) plus cheap per-match work, yielding overall O(n + matches) instead of repeated O(n) work per match. - Reduced allocation and work: avoiding the render_call_text substring removes memory allocations and copying when matching, lowering both CPU and GC/allocator pressure. - The heavy work that remains (parenthesis matching in _find_matching_paren) was already necessary and remains unchanged; the biggest waste (checking "inside string" by rescanning) is eliminated. Evidence in profiling and tests - Original line-profiler shows is_inside_string and repeated scanning accounted for the vast majority of time. Optimized profiling moves that cost to a single precompute loop, and total transform time drops from ~14s (profile aggregate) to ~0.088s. - Annotated unit tests show the biggest wins on large inputs: e.g., transforming 1000 render calls goes from ~615 ms to ~4.99 ms (huge improvement). Mixed-content large tests also show thousands-percent improvement. - Small inputs: there is a tiny precompute overhead for very small files — some micro-tests show a small increase in latency (single-digit microsecond differences). This is an expected and reasonable trade-off for the large wins on real/hot workloads. Behavioral and workload impact - Behavior-preserving: the precompute loop faithfully reproduces the original string-parsing rules (including escape handling and backticks), so match-skipping semantics are preserved. - Hot-path benefit: where this transformer runs on large files or on code with many render(...) calls (the typical hot path in the tests), the change dramatically reduces CPU time and allocation churn. - Trade-offs: memory usage rises slightly (O(n) boolean flags), and tiny single-match inputs may see small overhead; this trade-off is acceptable because it removes the dominant repeated work and yields orders-of-magnitude runtime reductions for typical large/hot inputs. Summary The optimized code eliminates repeated rescans for string membership by precomputing a single, linear-time "inside string" table and avoids an unnecessary substring allocation when searching for the opening parenthesis. These two targeted changes transform an expensive repeated-O(n) operation into a one-time O(n) cost plus cheap O(1) checks, producing the large runtime improvement observed in profiling and tests. --- codeflash/languages/javascript/instrument.py | 42 ++++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/codeflash/languages/javascript/instrument.py b/codeflash/languages/javascript/instrument.py index 96ed550c6..d02356076 100644 --- a/codeflash/languages/javascript/instrument.py +++ b/codeflash/languages/javascript/instrument.py @@ -135,6 +135,35 @@ def transform(self, code: str) -> str: result: list[str] = [] pos = 0 + + # Precompute a table of "is inside string" for every position 0..len(code) + # flags[p] == True means that a call to is_inside_string(code, p) would return True. + n = len(code) + inside_flags = [False] * (n + 1) + # We simulate the original is_inside_string scanning behavior incrementally: + j = 0 + in_string = False + string_char = None + for p in range(n + 1): + # advance j up to p using the exact same rules as is_inside_string + while j < p: + ch = code[j] + if in_string: + # Check for escape sequence + if ch == "\\" and j + 1 < n: + j += 2 + continue + # Check for end of string + if ch == string_char: + in_string = False + string_char = None + # Check for start of string + elif ch in "\"'`": + in_string = True + string_char = ch + j += 1 + inside_flags[p] = in_string + while pos < len(code): match = self._render_pattern.search(code, pos) if not match: @@ -142,7 +171,7 @@ def transform(self, code: str) -> str: break # Skip if inside a string literal - if is_inside_string(code, match.start()): + if inside_flags[match.start()]: result.append(code[pos : match.end()]) pos = match.end() continue @@ -161,9 +190,14 @@ def transform(self, code: str) -> str: prefix = match.group(2) or "" # "await " or "" # Find the render( opening paren - render_call_text = code[match.start() :] - render_paren_offset = render_call_text.index("(") - open_paren_pos = match.start() + render_paren_offset + open_paren_pos = code.find("(", match.start()) + if open_paren_pos == -1: + # Fallback: shouldn't happen due to regex, but keep same skip behavior + result.append(code[match.start() : match.end()]) + pos = match.end() + continue + + # Find the matching closing paren of render(...) # Find the matching closing paren of render(...) close_pos = self._find_matching_paren(code, open_paren_pos)