diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index afdab9259..87422cbd5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -83,12 +83,12 @@ jobs: 'codeflash/telemetry/' 'codeflash/tracing/' 'codeflash/verification/' \ 'tests/' 'pyproject.toml' 'uv.lock' - # JS E2E tests: JS language support + shared pipeline + packages + # JS E2E tests: JS language support + shared pipeline + packages + test fixtures check_paths e2e_js \ 'codeflash/languages/javascript/' 'codeflash/languages/base.py' \ 'codeflash/languages/registry.py' 'codeflash/optimization/' \ 'codeflash/verification/' 'packages/' \ - 'tests/scripts/end_to_end_test_js*' + 'code_to_optimize/js/' 'tests/scripts/end_to_end_test_js*' # Java E2E tests: Java language support + shared pipeline + runtime check_paths e2e_java \ diff --git a/code_to_optimize/js/code_to_optimize_ts/tests/data_processor.test.ts b/code_to_optimize/js/code_to_optimize_ts/tests/data_processor.test.ts index 3344bc149..b9720ac29 100644 --- a/code_to_optimize/js/code_to_optimize_ts/tests/data_processor.test.ts +++ b/code_to_optimize/js/code_to_optimize_ts/tests/data_processor.test.ts @@ -24,7 +24,7 @@ describe('DataProcessor', () => { test('handles larger arrays with duplicates', () => { const data: number[] = []; - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 10000; i++) { data.push(i % 20); } const processor = new DataProcessor(data); diff --git a/codeflash-benchmark/codeflash_benchmark/version.py b/codeflash-benchmark/codeflash_benchmark/version.py index 4dfa84c29..0f1baf8bc 100644 --- a/codeflash-benchmark/codeflash_benchmark/version.py +++ b/codeflash-benchmark/codeflash_benchmark/version.py @@ -1,2 +1,2 @@ # These version placeholders will be replaced by uv-dynamic-versioning during build. -__version__ = "0.20.5.post177.dev0+da536db8" +__version__ = "0.20.5.post151.dev0+95b62113" diff --git a/codeflash/languages/javascript/function_optimizer.py b/codeflash/languages/javascript/function_optimizer.py index bd72ce033..bb4b16ce6 100644 --- a/codeflash/languages/javascript/function_optimizer.py +++ b/codeflash/languages/javascript/function_optimizer.py @@ -182,6 +182,11 @@ def line_profiler_step( try: line_profiler_output_path = get_run_tmp_file(Path("line_profiler_output.json")) + # Pre-create with valid empty JSON so the file is never 0 bytes + # even if the JS profiler save() is interrupted (e.g. SIGKILL on timeout) + line_profiler_output_path.parent.mkdir(parents=True, exist_ok=True) + line_profiler_output_path.write_text("{}", encoding="utf-8") + success = self.language_support.instrument_source_for_line_profiler( func_info=self.function_to_optimize, line_profiler_output_file=line_profiler_output_path ) diff --git a/codeflash/languages/javascript/line_profiler.py b/codeflash/languages/javascript/line_profiler.py index 81b38983c..c31588f2f 100644 --- a/codeflash/languages/javascript/line_profiler.py +++ b/codeflash/languages/javascript/line_profiler.py @@ -86,9 +86,10 @@ def _generate_profiler_init(self) -> str: # Serialize line contents map for embedding in JavaScript line_contents_json = json.dumps(getattr(self, "line_contents", {})) - return f""" + return f"""// @ts-nocheck // Codeflash line profiler initialization -// @ts-nocheck +const __codeflash_fs__ = require('fs'); +const __codeflash_path__ = require('path'); const {self.profiler_var} = {{ stats: {{}}, lineContents: {line_contents_json}, @@ -123,19 +124,18 @@ def _generate_profiler_init(self) -> str: this.lastLineTime = now; this.totalHits++; - // Save every 100 hits to ensure we capture results even with --forceExit - if (this.totalHits % 100 === 0) {{ + // Save periodically to capture results even with --forceExit. + // Use 10000 (not 100) to avoid excessive I/O on hot loops. + if (this.totalHits % 10000 === 0) {{ this.save(); }} }}, save: function() {{ - const fs = require('fs'); - const pathModule = require('path'); - const outputDir = pathModule.dirname('{self.output_file.as_posix()}'); + const outputDir = __codeflash_path__.dirname('{self.output_file.as_posix()}'); try {{ - if (!fs.existsSync(outputDir)) {{ - fs.mkdirSync(outputDir, {{ recursive: true }}); + if (!__codeflash_fs__.existsSync(outputDir)) {{ + __codeflash_fs__.mkdirSync(outputDir, {{ recursive: true }}); }} // Merge line contents into stats before saving const statsWithContent = {{}}; @@ -145,7 +145,7 @@ def _generate_profiler_init(self) -> str: content: this.lineContents[key] || '' }}; }} - fs.writeFileSync( + __codeflash_fs__.writeFileSync( '{self.output_file.as_posix()}', JSON.stringify(statsWithContent, null, 2) ); @@ -298,8 +298,11 @@ def parse_results(profile_file: Path) -> dict: return {"timings": {}, "unit": 1e-9, "functions": {}} try: - with profile_file.open("r") as f: - data = json.load(f) + content = profile_file.read_text(encoding="utf-8").strip() + if not content: + logger.warning("Line profiler output file is empty: %s", profile_file) + return {"timings": {}, "unit": 1e-9, "functions": {}} + data = json.loads(content) # Group by file and function timings = {} diff --git a/codeflash/languages/javascript/support.py b/codeflash/languages/javascript/support.py index 768afce9f..2faeb76a3 100644 --- a/codeflash/languages/javascript/support.py +++ b/codeflash/languages/javascript/support.py @@ -2497,13 +2497,17 @@ def instrument_source_for_line_profiler( def parse_line_profile_results(self, line_profiler_output_file: Path) -> dict: from codeflash.languages.javascript.line_profiler import JavaScriptLineProfiler - if line_profiler_output_file.exists(): - parsed_results = JavaScriptLineProfiler.parse_results(line_profiler_output_file) - if parsed_results.get("timings"): - # Format output string for display - str_out = self._format_js_line_profile_output(parsed_results) - return {"timings": parsed_results.get("timings", {}), "unit": 1e-9, "str_out": str_out} - logger.warning("No line profiler output file found at %s", line_profiler_output_file) + if not line_profiler_output_file.exists(): + logger.warning("Line profiler output file not found: %s", line_profiler_output_file) + return {"timings": {}, "unit": 0, "str_out": ""} + + parsed_results = JavaScriptLineProfiler.parse_results(line_profiler_output_file) + if parsed_results.get("timings"): + # Format output string for display + str_out = self._format_js_line_profile_output(parsed_results) + return {"timings": parsed_results.get("timings", {}), "unit": 1e-9, "str_out": str_out} + + logger.warning("Line profiler output file empty or contained no timing data: %s", line_profiler_output_file) return {"timings": {}, "unit": 0, "str_out": ""} def _format_js_line_profile_output(self, parsed_results: dict) -> str: diff --git a/codeflash/version.py b/codeflash/version.py index 4dfa84c29..0f1baf8bc 100644 --- a/codeflash/version.py +++ b/codeflash/version.py @@ -1,2 +1,2 @@ # These version placeholders will be replaced by uv-dynamic-versioning during build. -__version__ = "0.20.5.post177.dev0+da536db8" +__version__ = "0.20.5.post151.dev0+95b62113"