Replace SimpleEvaluationProvider with ECJ compile+inject evaluator#8
Open
Replace SimpleEvaluationProvider with ECJ compile+inject evaluator#8
Conversation
Full Java expression evaluation via ECJ compilation and JDI bytecode injection. Generates synthetic class from expression, compiles with ECJ (or javac fallback), injects into debuggee via ClassLoader.defineClass over JDWP, and invokes the eval method. Supports: arithmetic, casts, ternary, constructors, method calls with args, chained calls, lambdas (with explicit casts), collections, arrays, null checks, instanceof, static fields/methods, string operations, and this-context in instance methods. Known limitations: private field access (use getters), generic type erasure (use explicit casts), unimported types (use FQCNs). - Bump Java adapter requirement from 11 to 17 (ECJ 3.40 needs JDK 17+) - Add ECJ 3.40.0 dependency (3.3MB single JAR, zero transitive deps) - Add 38 new integration tests (expression eval + edge cases) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When ECJ compilation fails with "not visible" errors, automatically rewrite field accesses to use reflection (getDeclaredField + setAccessible). Works for both this-context (this.secret) and local objects (obj.secret). Generated __dbg_get helper walks the class hierarchy to find fields. Casts return values to the correct type (including primitive boxing) so expressions like this.count + local compile correctly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Support `dbg launch --runtime java -- -cp <classpath> <MainClass>` syntax mirroring the JDK `java -cp` convention. Required for Spring Boot and Maven/Gradle projects where classes and dependencies are on a classpath. Automatically derives source paths from non-JAR classpath entries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…imeout - Resolve short filenames in breakpoints (e.g. "User.java" → full path) by searching sourcePaths. Error with candidates on ambiguous matches. - Auto-detect Maven/Gradle source roots: target/classes → src/main/java - Reduce continue() timeout from 30s to 500ms grace period (matching CDP) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…imeout Split DapRuntimeConfig strategy per language into separate files: - src/dap/runtimes/types.ts — typed interface with JSDoc for new contributors - src/dap/runtimes/java.ts, python.ts, lldb.ts — per-language configs - src/dap/runtimes/index.ts — registry Interface improvements: - Rename resolveCommand → getAdapterCommand (clearer intent) - buildLaunchArgs takes UserLaunchInput object instead of positional args - Returns typed DapLaunchArgs with sourcePaths, DapAttachArgs with host/port - JSDoc on every method explaining what to implement for a new language continue() no longer blocks for 30s — uses 500ms grace period (matching CDP). Tests explicitly call waitForStop() when they need to wait for a breakpoint. runTo() internally waits for the temporary breakpoint to hit. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ContinueOptions + createStoppedWaiter + waitForStop with a single
WaitForStopOptions interface and waitUntilStopped() primitive.
- continue(options?): waitForStop defaults to false (fire-and-forget)
- step(mode, options?): waitForStop defaults to true
- pause/launch/attach: use waitUntilStopped() directly
Tests use continue({ waitForStop: true, throwOnTimeout: true }) instead
of separate continue() + waitForStop() calls.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CDP: replace createPauseWaiter with waitUntilStopped(WaitForStopOptions) matching the DAP primitive. continue/step now use the same options pattern. - Add TimeoutError class to distinguish timeouts from real CDP errors - Centralize constants: WAIT_PAUSE_TIMEOUT_MS (5s), WAIT_MAYBE_PAUSE_TIMEOUT_MS (500ms) - Default: continue waits 5s (catches immediate breakpoints), step waits 5s and throws on timeout Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The Java debugger's expression evaluator (
SimpleEvaluationProvider) only supported simple variable names, field chains (a.b.c), and no-arg method calls (obj.method()). Any real debugging expression — arithmetic, method calls with arguments, ternary, constructors — returned:This made the Java debugger significantly less useful than the Node.js/Bun debugger for AI agents that need to inspect runtime state.
Before / After
How it works
ClassLoader.defineClass()over JDWPClass.forName(name, true, loader)__eval(...)passing pre-captured local variable valuesgetDeclaredField+setAccessible) with proper type castingChanges
SimpleEvaluationProviderwithCompilingEvaluationProvider(561 lines)Known limitations
List<String>becomesList((String)n).length()java.util.stream.Collectors.toList()fieldNamewithoutthis.won't resolvethis.fieldNameTest plan
🤖 Generated with Claude Code