-
Notifications
You must be signed in to change notification settings - Fork 21
fix: handle assertThrows variable assignment in Java instrumentation #1443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: handle assertThrows variable assignment in Java instrumentation #1443
Conversation
When assertThrows was assigned to a variable to validate exception
properties, the transformation generated invalid Java syntax by
replacing the assertThrows call with try-catch while leaving the
variable assignment intact.
Example of invalid output:
IllegalArgumentException e = try { code(); } catch (Exception) {}
This fix detects variable assignments, extracts the exception type
from assertThrows arguments, and generates proper exception capture:
IllegalArgumentException e = null;
try { code(); } catch (IllegalArgumentException _cf_caught1) { e = _cf_caught1; } catch (Exception _cf_ignored1) {}
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The optimization achieves a **33% runtime speedup** (from 1.63ms to 1.23ms) by eliminating repeated regex compilation overhead through two key changes: **What Changed:** 1. **Precompiled regex pattern**: The regex pattern `r"(\w+(?:<[^>]+>)?)\s+(\w+)\s*=\s*$"` is now compiled once in `__init__` and stored as `self._assign_re`, rather than being recompiled on every call to `_detect_variable_assignment`. 2. **Direct substring search**: Instead of first extracting `line_before_assert = source[line_start:assertion_start]` and then searching it, the optimized version directly searches the source string using `self._assign_re.search(source, line_start, assertion_start)` with positional parameters. **Why This Is Faster:** - **Regex compilation overhead eliminated**: Line profiler shows the original code spent **53.4% of total time** (3.89ms out of 7.29ms) on `re.search(pattern, line_before_assert)`. This line was called 1,057 times, meaning the regex pattern was compiled 1,057 times. The optimized version reduces this to just **30.8%** (1.20ms out of 3.91ms) by using a precompiled pattern. - **Reduced string allocations**: By passing `line_start` and `assertion_start` as positional bounds to `search()`, we avoid creating the temporary `line_before_assert` substring (which took 5% of time in the original), reducing memory churn. **Performance Across Test Cases:** The optimization shows consistent improvements across all scenarios: - **Simple cases**: 35-45% faster (e.g., simple variable assignment: 39.1% faster) - **No-match cases**: 82-101% faster (e.g., no assignment: 101% faster) - regex compilation was pure overhead here - **Complex generics**: Still 6-14% faster despite more complex matching - **Large-scale test** (1000 iterations): 36.7% faster, proving the benefit scales with repeated calls **Impact on Workloads:** Since `_detect_variable_assignment` is called for every assertion in Java test code being analyzed, and the `JavaAssertTransformer` is likely instantiated once per file/session, this optimization provides cumulative benefits. The precompilation happens once at instantiation, then every subsequent call benefits from the compiled pattern - making it especially valuable when processing files with many assertions (as demonstrated by the 1000-iteration test showing consistent 36.7% improvement).
⚡️ Codeflash found optimizations for this PR📄 33% (0.33x) speedup for
|
| current = [] | ||
| parts = [] | ||
|
|
||
| for char in args_content: | ||
| if char in "(<": | ||
| depth += 1 | ||
| current.append(char) | ||
| elif char in ")>": | ||
| depth -= 1 | ||
| current.append(char) | ||
| elif char == "," and depth == 0: | ||
| parts.append("".join(current).strip()) | ||
| current = [] | ||
| else: | ||
| current.append(char) | ||
|
|
||
| if current: | ||
| parts.append("".join(current).strip()) | ||
|
|
||
| if parts: | ||
| exception_arg = parts[0].strip() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⚡️Codeflash found 104% (1.04x) speedup for JavaAssertTransformer._extract_exception_class in codeflash/languages/java/remove_asserts.py
⏱️ Runtime : 9.86 milliseconds → 4.84 milliseconds (best of 162 runs)
📝 Explanation and details
The optimized code achieves a 103% speedup (2x faster) by eliminating unnecessary string building operations and using early termination.
Key Optimization:
The original code builds intermediate data structures (current list and parts list) by iterating through the entire input string, joining characters, and then extracting the first element. The optimized version directly slices the input string up to the first comma at depth 0, avoiding:
- List append operations for every character (~186K operations in profiling)
- String join operations to reconstruct parts
- Multiple intermediate list allocations
Why This Is Faster:
- Early termination: When the first comma at depth 0 is found, the optimized code immediately returns instead of processing the entire string
- Direct string slicing: Uses
args_content[:i]instead of building character lists and joining them - Reduced operations: Line profiler shows ~101K iterations vs ~192K in the original (47% fewer character iterations)
Impact on Test Cases:
- Simple cases: 29-97% faster (e.g.,
test_basic_simple_exception_extraction: 6.57μs → 3.69μs) - Complex nested structures: 60-119% faster (e.g.,
test_edge_nested_parentheses_in_second_argument_do_not_affect_split: 7.49μs → 2.85μs) - Large-scale workloads: Dramatic gains - up to 60,162% faster for the 1000-argument test case (2.22ms → 3.68μs), where early termination provides massive benefits
- Edge cases with no comma: Still faster (22-29%) due to simpler string operations
The optimization is particularly effective for assertThrows parsing in Java test code where arguments after the exception class can be arbitrarily complex (nested lambdas, method calls with many parameters). The early-exit strategy means performance scales with the position of the first comma rather than total input length.
✅ Correctness verification report:
| Test | Status |
|---|---|
| ⚙️ Existing Unit Tests | 🔘 None Found |
| 🌀 Generated Regression Tests | ✅ 4085 Passed |
| ⏪ Replay Tests | 🔘 None Found |
| 🔎 Concolic Coverage Tests | 🔘 None Found |
| 📊 Tests Coverage | 100.0% |
🌀 Click to see Generated Regression Tests
import pytest # used for our unit tests
from codeflash.languages.java.remove_asserts import JavaAssertTransformer
# Create a transformer instance for use in tests (uses real JavaAnalyzer via get_java_analyzer)
# We pass a dummy function name because the tested method is independent of it.
_transformer = JavaAssertTransformer("assertThrows")
def test_basic_simple_exception_extraction():
# Simple, typical usage: exception class followed by other arguments.
s = "IllegalArgumentException.class, () -> { /* lambda */ }"
# Expect the class name without the `.class` suffix.
codeflash_output = _transformer._extract_exception_class(s) # 6.57μs -> 3.69μs (78.0% faster)
def test_basic_fully_qualified_class():
# Fully-qualified class names should be preserved (packages included).
s = "com.example.errors.MyException.class, () -> {}"
# Expect the fully-qualified name minus `.class`.
codeflash_output = _transformer._extract_exception_class(s) # 6.08μs -> 4.05μs (50.3% faster)
def test_basic_only_class_no_comma():
# If only the exception class is provided (no comma), it should still be extracted.
s = "MyCustomException.class"
# Expect the bare class name.
codeflash_output = _transformer._extract_exception_class(s) # 3.84μs -> 2.98μs (29.0% faster)
def test_edge_empty_string_returns_none():
# Empty content inside assertThrows should return None.
s = ""
# No first argument => function must return None.
codeflash_output = _transformer._extract_exception_class(s) # 808ns -> 848ns (4.72% slower)
def test_edge_first_argument_missing_class_suffix_returns_none():
# If the first argument doesn't end with `.class`, it's not recognized as an exception class.
s = "new IllegalArgumentException(), () -> {}"
# Should return None because the first part is not a `.class` reference.
codeflash_output = _transformer._extract_exception_class(s) # 5.41μs -> 3.78μs (43.2% faster)
def test_edge_comma_inside_generics_of_first_argument_is_handled():
# First argument contains generics with a comma; splitting must not break on that comma.
s = "Map.Entry<String, Integer>.class, someOtherCall(arg1, arg2)"
# The function should return the entire generic-qualified first argument (minus `.class`).
codeflash_output = _transformer._extract_exception_class(s) # 7.07μs -> 4.09μs (72.8% faster)
def test_edge_nested_parentheses_in_second_argument_do_not_affect_split():
# Second argument contains nested parentheses and commas which must be ignored by the top-level split.
s = "MyException.class, () -> doSomething(foo(bar, baz), another)"
# Ensure the first argument was correctly identified.
codeflash_output = _transformer._extract_exception_class(s) # 7.49μs -> 2.85μs (163% faster)
def test_edge_generic_class_with_angle_brackets_is_returned_intact():
# Generic type references on the class should be returned as-is (with angle brackets).
s = "Outer.Inner<GenericType>.class, someOther"
# The returned value should keep the angle-bracketed generic.
codeflash_output = _transformer._extract_exception_class(s) # 5.58μs -> 3.77μs (48.3% faster)
def test_edge_leading_trailing_whitespace_is_stripped():
# Whitespace around arguments should be trimmed by the function.
s = " WeirdException .class , () -> {}"
# Note: the function strips only after removing .class; it will also strip whitespace from ends.
codeflash_output = _transformer._extract_exception_class(s) # 5.79μs -> 3.72μs (55.7% faster)
def test_edge_none_input_raises_type_error():
# Passing None is a misuse; iterating over None should raise a TypeError.
with pytest.raises(TypeError):
_transformer._extract_exception_class(None) # 2.61μs -> 2.71μs (3.70% slower)
def test_edge_non_string_bytes_input_raises_type_error():
# Passing bytes is not supported; the function expects a str and will fail during processing/join.
with pytest.raises(TypeError):
_transformer._extract_exception_class(b"MyException.class, arg") # 3.31μs -> 3.40μs (2.76% slower)
def test_large_scale_many_trailing_arguments_and_nested_structures():
# Construct a long argument list (1000 items) where only the first is the exception .class reference.
tail_parts = []
for i in range(1000):
# Each trailing argument contains nested parentheses and commas to stress the parser logic.
tail_parts.append(f"fn{i}(arg{i}, nested({i}, {i+1}))")
long_args = ", ".join(tail_parts)
s = "MassiveException.class, " + long_args
# The function should still correctly extract the first `.class` reference.
codeflash_output = _transformer._extract_exception_class(s) # 2.22ms -> 3.68μs (60162% faster)
def test_large_scale_repeated_calls_stability_and_correctness():
# Call the method repeatedly (1000 iterations) with a variety of valid inputs to ensure consistency.
inputs = [
"A.class, x",
"pkg.B.class, () -> {}",
"C<D, E>.class, something(1,2)",
"Single.class"
]
# Loop many times to detect any stateful behavior or performance regressions.
for _ in range(1000):
# Check all inputs in every iteration
codeflash_output = _transformer._extract_exception_class(inputs[0]) # 1.26ms -> 971μs (30.2% faster)
codeflash_output = _transformer._extract_exception_class(inputs[1])
codeflash_output = _transformer._extract_exception_class(inputs[2]) # 2.01ms -> 1.19ms (69.1% faster)
codeflash_output = _transformer._extract_exception_class(inputs[3])
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.import pytest
from codeflash.languages.java.parser import JavaAnalyzer
from codeflash.languages.java.remove_asserts import JavaAssertTransformer
# Basic tests - verify fundamental functionality under normal conditions
def test_extract_exception_class_simple_exception():
"""Test extracting a simple exception class name from assertThrows."""
transformer = JavaAssertTransformer("test_method")
# Input: "IllegalArgumentException.class, () -> method()"
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class, () -> method()"); result = codeflash_output # 6.10μs -> 3.79μs (61.0% faster)
def test_extract_exception_class_with_package():
"""Test extracting fully qualified exception class name."""
transformer = JavaAssertTransformer("test_method")
# Input with package prefix
codeflash_output = transformer._extract_exception_class("java.lang.IllegalArgumentException.class, () -> method()"); result = codeflash_output # 6.90μs -> 4.33μs (59.1% faster)
def test_extract_exception_class_runtime_exception():
"""Test extracting RuntimeException."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("RuntimeException.class, () -> {}"); result = codeflash_output # 5.05μs -> 3.28μs (54.0% faster)
def test_extract_exception_class_null_pointer_exception():
"""Test extracting NullPointerException."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("NullPointerException.class, () -> foo()"); result = codeflash_output # 5.65μs -> 3.47μs (63.1% faster)
def test_extract_exception_class_custom_exception():
"""Test extracting a custom exception class."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("com.example.CustomException.class, () -> doSomething()"); result = codeflash_output # 6.67μs -> 3.83μs (74.1% faster)
def test_extract_exception_class_with_lambda():
"""Test extracting exception class when lambda has no arguments."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IOException.class, () -> readFile()"); result = codeflash_output # 5.36μs -> 2.98μs (80.2% faster)
def test_extract_exception_class_with_method_call():
"""Test extracting exception class with method call in lambda."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalStateException.class, () -> obj.method()"); result = codeflash_output # 5.97μs -> 3.50μs (70.6% faster)
def test_extract_exception_class_with_lambda_body():
"""Test extracting exception class with complex lambda body."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class, () -> { throw new RuntimeException(); }"); result = codeflash_output # 7.59μs -> 3.65μs (108% faster)
# Edge tests - evaluate behavior under extreme or unusual conditions
def test_extract_exception_class_only_exception_no_lambda():
"""Test extracting exception class when only exception is provided (no lambda)."""
transformer = JavaAssertTransformer("test_method")
# Only the exception class, no second argument
codeflash_output = transformer._extract_exception_class("NullPointerException.class"); result = codeflash_output # 4.04μs -> 3.29μs (22.6% faster)
def test_extract_exception_class_empty_string():
"""Test with empty string input."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class(""); result = codeflash_output # 817ns -> 817ns (0.000% faster)
def test_extract_exception_class_no_dot_class_suffix():
"""Test with exception class name but no .class suffix."""
transformer = JavaAssertTransformer("test_method")
# Missing .class suffix
codeflash_output = transformer._extract_exception_class("IllegalArgumentException, () -> method()"); result = codeflash_output # 5.42μs -> 3.22μs (68.4% faster)
def test_extract_exception_class_only_dot_class():
"""Test with only '.class' string."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class(".class"); result = codeflash_output # 2.66μs -> 2.18μs (21.9% faster)
def test_extract_exception_class_whitespace_before_exception():
"""Test with leading whitespace before exception class."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class(" IllegalArgumentException.class, () -> method()"); result = codeflash_output # 6.24μs -> 3.91μs (59.5% faster)
def test_extract_exception_class_whitespace_around_comma():
"""Test with whitespace around comma separator."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class , () -> method()"); result = codeflash_output # 6.45μs -> 3.88μs (66.0% faster)
def test_extract_exception_class_nested_parentheses_in_lambda():
"""Test with nested parentheses in lambda expression."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class, () -> method(param(a, b))"); result = codeflash_output # 6.93μs -> 3.63μs (91.1% faster)
def test_extract_exception_class_nested_angle_brackets():
"""Test with nested angle brackets (generics) in arguments."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class, () -> List<String> list = new ArrayList<>()"); result = codeflash_output # 8.05μs -> 3.68μs (119% faster)
def test_extract_exception_class_mixed_depth_nesting():
"""Test with mixed nesting of parentheses and angle brackets."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class, () -> method(List<String> param)"); result = codeflash_output # 7.20μs -> 3.64μs (97.8% faster)
def test_extract_exception_class_multiple_commas_in_lambda():
"""Test with multiple commas inside lambda parentheses."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class, () -> method(a, b, c)"); result = codeflash_output # 7.00μs -> 3.54μs (97.7% faster)
def test_extract_exception_class_no_space_after_dot_class():
"""Test with no space after .class."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class,() -> method()"); result = codeflash_output # 5.88μs -> 3.63μs (62.0% faster)
def test_extract_exception_class_exception_with_numbers():
"""Test exception class name containing numbers."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException2.class, () -> method()"); result = codeflash_output # 5.85μs -> 3.63μs (60.9% faster)
def test_extract_exception_class_deeply_nested_generics():
"""Test with deeply nested generic types."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class, () -> Map<String, List<Integer>> map = new HashMap<>()"); result = codeflash_output # 9.30μs -> 3.54μs (163% faster)
def test_extract_exception_class_with_inner_class():
"""Test exception class that is an inner class."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("OuterException.InnerException.class, () -> method()"); result = codeflash_output # 6.29μs -> 3.91μs (60.7% faster)
def test_extract_exception_class_exception_only_with_trailing_comma():
"""Test when only exception is present but followed by comma."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("NullPointerException.class,"); result = codeflash_output # 4.20μs -> 3.47μs (21.0% faster)
def test_extract_exception_class_very_long_package_name():
"""Test with very long fully qualified package name."""
transformer = JavaAssertTransformer("test_method")
long_name = "com.example.test.util.exception.custom.VeryLongExceptionClassName.class, () -> method()"
codeflash_output = transformer._extract_exception_class(long_name); result = codeflash_output # 9.22μs -> 6.18μs (49.1% faster)
def test_extract_exception_class_exception_with_underscores():
"""Test exception class name with underscores."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("Illegal_Argument_Exception.class, () -> method()"); result = codeflash_output # 6.14μs -> 3.77μs (62.7% faster)
def test_extract_exception_class_single_letter_class():
"""Test single letter exception class name."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("E.class, () -> method()"); result = codeflash_output # 4.45μs -> 2.26μs (97.1% faster)
def test_extract_exception_class_multiple_spaces_between_parts():
"""Test with multiple spaces between exception and comma."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class , () -> method()"); result = codeflash_output # 6.46μs -> 4.01μs (61.2% faster)
def test_extract_exception_class_tabs_and_newlines():
"""Test with tabs and newlines in the input."""
transformer = JavaAssertTransformer("test_method")
codeflash_output = transformer._extract_exception_class("IllegalArgumentException.class\t,\n() -> method()"); result = codeflash_output # 5.95μs -> 3.75μs (58.5% faster)
# Large-scale tests - assess performance and scalability
def test_extract_exception_class_very_long_lambda_body():
"""Test with a very long lambda body containing many method calls."""
transformer = JavaAssertTransformer("test_method")
# Create a long lambda with 100 method calls separated by commas
method_calls = ", ".join([f"method{i}()" for i in range(100)])
args = f"IllegalArgumentException.class, () -> {{{method_calls}}}"
codeflash_output = transformer._extract_exception_class(args); result = codeflash_output # 80.3μs -> 3.69μs (2079% faster)
def test_extract_exception_class_deeply_nested_parentheses():
"""Test with deeply nested parentheses (100 levels deep)."""
transformer = JavaAssertTransformer("test_method")
# Create 100 levels of nested parentheses
nested = "method" + "(" * 100 + ")" * 100
args = f"IllegalArgumentException.class, () -> {nested}"
codeflash_output = transformer._extract_exception_class(args); result = codeflash_output # 17.7μs -> 3.71μs (377% faster)
def test_extract_exception_class_many_commas_in_method_args():
"""Test with method calls containing 50 parameters separated by commas."""
transformer = JavaAssertTransformer("test_method")
# Create a method with 50 parameters
params = ", ".join([f"arg{i}" for i in range(50)])
args = f"IllegalArgumentException.class, () -> method({params})"
codeflash_output = transformer._extract_exception_class(args); result = codeflash_output # 33.2μs -> 3.60μs (822% faster)
def test_extract_exception_class_large_number_of_generics():
"""Test with many nested generic type parameters."""
transformer = JavaAssertTransformer("test_method")
# Create deeply nested generics
generics = "Map<String, Map<String, Map<String, Map<String, Map<String, Integer>>>>>"
args = f"IllegalArgumentException.class, () -> {generics} map = new HashMap<>()"
codeflash_output = transformer._extract_exception_class(args); result = codeflash_output # 12.9μs -> 3.57μs (263% faster)
def test_extract_exception_class_multiple_consecutive_calls():
"""Test multiple consecutive calls maintain consistency."""
transformer = JavaAssertTransformer("test_method")
# Call multiple times with same input - should always return same result
test_input = "IllegalArgumentException.class, () -> method()"
results = [transformer._extract_exception_class(test_input) for _ in range(1000)]
def test_extract_exception_class_varied_exception_types_batch():
"""Test extraction of 100 different exception types."""
transformer = JavaAssertTransformer("test_method")
exceptions = [f"Exception{i}.class, () -> method()" for i in range(100)]
results = [transformer._extract_exception_class(exc) for exc in exceptions]
def test_extract_exception_class_with_very_long_package_path():
"""Test with package path containing 50 segments."""
transformer = JavaAssertTransformer("test_method")
package_parts = ".".join([f"segment{i}" for i in range(50)])
full_class = f"{package_parts}.MyException"
args = f"{full_class}.class, () -> method()"
codeflash_output = transformer._extract_exception_class(args); result = codeflash_output # 35.8μs -> 31.0μs (15.2% faster)
def test_extract_exception_class_alternating_parentheses_and_angles():
"""Test with alternating parentheses and angle brackets 50 times."""
transformer = JavaAssertTransformer("test_method")
# Create alternating pattern: <...>, (...), <...>, (...)
pattern = "".join([f"<{i}>" if i % 2 == 0 else f"({i})" for i in range(50)])
args = f"IllegalArgumentException.class, () -> method{pattern}"
codeflash_output = transformer._extract_exception_class(args); result = codeflash_output # 18.0μs -> 3.58μs (402% faster)
def test_extract_exception_class_stress_test_boundary():
"""Stress test with all edge cases combined in single input."""
transformer = JavaAssertTransformer("test_method")
# Complex nested structure with multiple edge cases
args = (
"com.example.test.VeryLongExceptionName_With_Underscores123.class, "
"() -> method(" + ", ".join([f"arg{i}" for i in range(100)]) + ")"
)
codeflash_output = transformer._extract_exception_class(args); result = codeflash_output # 62.9μs -> 5.59μs (1024% faster)
def test_extract_exception_class_performance_many_similar_inputs():
"""Test performance with 500 similar inputs containing various whitespace."""
transformer = JavaAssertTransformer("test_method")
test_cases = []
for i in range(500):
# Vary whitespace but keep exception name same
spaces_before = " " * (i % 10)
spaces_after = " " * ((i + 1) % 10)
args = f"{spaces_before}IllegalArgumentException.class{spaces_after}, () -> method()"
test_cases.append(args)
results = [transformer._extract_exception_class(args) for args in test_cases]
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.To test or edit this optimization locally git merge codeflash/optimize-pr1443-2026-02-10T21.40.29
Click to see suggested changes
| current = [] | |
| parts = [] | |
| for char in args_content: | |
| if char in "(<": | |
| depth += 1 | |
| current.append(char) | |
| elif char in ")>": | |
| depth -= 1 | |
| current.append(char) | |
| elif char == "," and depth == 0: | |
| parts.append("".join(current).strip()) | |
| current = [] | |
| else: | |
| current.append(char) | |
| if current: | |
| parts.append("".join(current).strip()) | |
| if parts: | |
| exception_arg = parts[0].strip() | |
| for i, char in enumerate(args_content): | |
| if char in "(<": | |
| depth += 1 | |
| elif char in ")>": | |
| depth -= 1 | |
| elif char == "," and depth == 0: | |
| exception_arg = args_content[:i].strip() | |
| # Remove .class suffix | |
| if exception_arg.endswith(".class"): | |
| return exception_arg[:-6].strip() | |
| return None | |
| if args_content: | |
| exception_arg = args_content.strip() |
The optimization achieves a **13% runtime improvement** (2.31ms → 2.04ms) by replacing Python's `str.endswith()` method call with a direct last-character index check (`code_to_run[-1] != ";"` instead of `not code_to_run.endswith(";")`).
**Key optimization:**
The critical change occurs in the lambda body processing path, which is executed in 2,936 out of 3,943 invocations (74% of calls). By replacing the `endswith()` method call with direct indexing, the code eliminates:
- Method lookup overhead for `endswith`
- Internal string comparison logic
- Function call frame allocation
Line profiler data shows the optimized check (`if code_to_run and code_to_run[-1] != ";"`) runs in 964ns versus 1.24μs for the original `endswith()` call—a 22% improvement on this single line that executes nearly 3,000 times per test run.
**Why this works:**
In CPython, direct character indexing (`[-1]`) is implemented as a simple array lookup in the string's internal buffer, while `endswith()` involves:
1. Method attribute lookup on the string object
2. Argument parsing and validation
3. Internal substring comparison logic
4. Return value marshaling
For a single-character comparison, the indexing approach is significantly faster.
**Test results validation:**
The annotated tests show consistent improvements across all test cases:
- Simple lambda bodies: 17-23% faster (test_simple_lambda_body_*)
- Variable assignments: 6-8% faster (test_variable_assignment_*)
- Batch operations: 14-23% faster (test_many_exception_types, test_long_lambda_bodies_batch)
The optimization is particularly effective for workloads with many assertion transformations, as demonstrated by the large-scale tests (1000+ invocations) showing 17-18% improvements.
**Impact:**
Since `JavaAssertTransformer` is used to process Java test code during optimization workflows, this change directly reduces the time to transform assertion-heavy test files. The function processes each assertion statement individually, so files with hundreds of assertions will see cumulative time savings proportional to the assertion count.
⚡️ Codeflash found optimizations for this PR📄 14% (0.14x) speedup for
|
…2026-02-10T21.52.05 ⚡️ Speed up method `JavaAssertTransformer._generate_exception_replacement` by 14% in PR #1443 (`fix/java-exception-assignment-instrumentation`)
|
This PR is now faster! 🚀 @mashraf-222 accepted my optimizations from: |
…2026-02-10T21.31.59 ⚡️ Speed up method `JavaAssertTransformer._detect_variable_assignment` by 33% in PR #1443 (`fix/java-exception-assignment-instrumentation`)
|
This PR is now faster! 🚀 @mashraf-222 accepted my optimizations from: |
PR Review: Testing assertThrows Variable Assignment FixTest Setup
Tests Executed1. Unit Tests - Java Assertion RemovalCommand: Result: ✅ ALL 76 TESTS PASSED Key test cases for this PR:
2. Transformation VerificationCreated test file with Input: IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> Fibonacci.fibonacci(-1)
);
assertNotNull(exception.getMessage());Output (transformed): IllegalArgumentException exception = null;
try { Fibonacci.fibonacci(-1); } catch (IllegalArgumentException _cf_caught1) { exception = _cf_caught1; } catch (Exception _cf_ignored1) {}
assertNotNull(exception.getMessage());Validation: ✅ Compiled and executed successfully with 3. E2E Java Optimization TestProject: Created
Commands: mvn test -Dtest=ExceptionTest # Original tests ✅ PASSED (3/3)
uv run codeflash --all --no-pr --verbose # E2E optimization
mvn test-compile # Instrumented files ✅ COMPILEDGenerated Files:
Compilation Result: ✅ BUILD SUCCESS Both instrumented files compiled without syntax errors, confirming the PR correctly handles:
Observations1. Fix Behavior:
2. Pre-existing Issue Found: This is unrelated to this PR - it's a pre-existing serialization issue in behavior instrumentation. 3. Instrumentation Modes:
Conclusion✅ PR #1443 PASSES REVIEW The fix correctly handles
All unit tests pass (76/76), manual compilation succeeds, and E2E optimization generates valid instrumented code. Recommendation: Ready to merge to |
…tion Resolved conflicts by merging the best of both branches: - Kept exception_class field from PR for better exception type detection - Adopted more general variable assignment detection from omni-java - Combined exception replacement logic to use exception_class with fallback - Added double catch (specific exception + generic Exception) for robustness - Merged test cases from both branches with updated expectations Changes: - Updated AssertionMatch to include all fields: assigned_var_type, assigned_var_name, exception_class - Lambda extraction now works for all exception assertions - Exception class extraction specifically for assertThrows - Variable assignment detection handles final modifier and fully qualified types - Exception replacement uses exception_class or falls back to assigned_var_type - All 80 tests passing Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Merge Conflicts Resolved ✅Successfully merged Resolution StrategyCombined the best approaches from both branches: From PR branch:
From omni-java base:
Changes Made
Test ResultsAll 80 tests passing ✅ Added test cases:
PR Status
|
Problem
When
assertThrowswas assigned to a variable to validate exception properties, the assertion transformation generated invalid Java syntax that failed Maven compilation:This occurred because the transformation replaced only the
assertThrows(...)call with a try-catch block, leaving the variable assignment intact. In Java, you cannot assign a try-catch statement to a variable.Root Cause
The regex pattern in
_find_junit_assertions()matched only theassertThrows(...)method call, not the preceding variable assignment. The replacement logic in_generate_exception_replacement()generatedtry { code } catch (Exception) {}without considering that the assertion might be assigned to a variable.Resolution
Added logic to:
assertThrowscallsassertThrowsargumentsThis preserves subsequent assertions on the exception object (e.g.,
exception.getMessage()).Testing
test_java_assertion_removal.pypass🤖 Generated with Claude Code