Repository navigation
feat: brace-mode multi-statement support for expression evaluator (#8) - #13
Merged
Merged
Conversation
Closes #8 User input wrapped in `{ ... }` is now spliced into the wrapper's method body verbatim instead of being treated as a single expression. The mode is detected by a tokenizer-aware brace-match that ignores braces inside string/char/text-block literals, so `"{x}".length()` still routes through expression mode while `{ try { return foo(); } catch (...) }` routes through block mode. In block mode the user is responsible for `return X;` statements; a trailing `return null;` fallthrough guard keeps the wrapper type-correct if the user block doesn't end with a return. This automatically extends to every eval call site routing through `evaluate()` -- jdwp_evaluate_expression, jdwp_assert_expression, breakpoint conditions, logpoint expressions and conditions, exception logpoints, field watchpoint conditions/expressions, and watchers. Tool descriptions on all 8 eval-bearing tool methods/params updated with a `(supports `{ ...; return X; }` block syntax)` suffix so the feature is discoverable from any eval-shaped param in isolation. Tests added: 9 in JdiExpressionEvaluatorRewriteTest covering block-mode detection, nested braces, brace-inside-string-literal escapes, and expression-mode fallback for `{x}.foo()`-style inputs.
- Javadoc on generateSourceCode and isBlockMode: replace malformed
`{@code {}` and `{@code }}` inline tags (the `}` closes the inline
tag prematurely) with plain '{' and '}' references in prose.
- isBlockMode now skips Java line (`//…`) and block (`/*…*/`) comments
alongside string/char/text-block literals. Without this, valid blocks
like `{ // }` + body or `{ /* } */ … }` would be misclassified as
expression-mode and fail compilation.
- Unterminated comment that swallows the trailing `}` is detected
(skip past `n`) and reported as not-block-mode rather than left
silently miscounted.
- generateSourceCode block-mode body now wraps the user body in
`if (__mcpFallthroughGuard) { ... } return null;` where the guard
is a non-final local set to true. JLS §15.29 says only final
variables initialised with constant expressions are constant
expressions, so the compiler cannot prove the post-if `return null;`
unreachable. Fixes the "unreachable code" compile error when the
user's block ends with an explicit `return X;` (the previous trailing
`return null;` was statically unreachable in that case).
- evaluate() now SKIPS the bare-field-reference rewrite when the input
is in block mode. The rewriter is identifier-level and cannot tell
a field reference from a local-variable declaration; in block mode
`int count = 1; ...` would otherwise become
`int _this.count = 1; ...`. Block-mode users are expected to use
explicit `this.field` / `_this.field` references; the keyword
rewrite in generateSourceCode still handles `this.field` for them.
Tests added (4 in JdiExpressionEvaluatorRewriteTest):
- isBlockMode skips `}` inside a `/* ... */` block comment
- isBlockMode skips `}` inside a `//` line comment
- isBlockMode handles mixed nested braces + comment-buried braces
- isBlockMode tolerates unterminated block comment (returns false
because the comment swallows the trailing `}`)
…branch
The literal was `{a;}+b;` which has no trailing `}`, so isBlockMode rejected
it at the cheap end-char guard and never reached the early `depth == 0`
branch the test claims to cover. Use `{a;}+b;}` so the input clears the
end-char guard and the rejection comes from the brace-balance walk.
# Conflicts: # jdwp-mcp-server/src/main/java/one/edee/mcp/jdwp/evaluation/JdiExpressionEvaluator.java
novoj
added a commit
that referenced
this pull request
May 24, 2026
Every eval-bearing surface (jdwp_evaluate_expression, jdwp_assert_expression,
breakpoint conditions, logpoint expressions/conditions, exception logpoints,
field-watchpoint conditions/expressions, and watchers) now accepts a
brace-wrapped statement body in addition to a single expression. An input that
starts with `{` and ends with the matching `}` is spliced into the wrapper
method verbatim; the user writes `return X;` and a trailing `return null;`
fallthrough guard keeps the wrapper type-correct. This unlocks intermediate
locals, try/catch, early returns, and loops at a breakpoint. Resolves #8.
Mode detection is tokenizer-aware: braces inside string/char/text-block
literals don't trigger block mode, so `"{x}".length()` stays in expression
mode. All eight eval-bearing tool params advertise the block syntax in their
descriptions so it's discoverable in isolation. The bare-field `this.field`
auto-rewrite is skipped in block mode (the identifier-level rewriter can't
distinguish a field reference from a local declaration); block-mode users use
explicit this.field / _this.field.
Lands the work from PR #11, which had merged into its stacked base
(fix/eval-target-package-wrapper) rather than main and so missed 2.3.0;
re-targeted to main via PR #13.
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.
Re-targets the block-mode work onto
main. PR #11 was merged into its stackedbase
fix/eval-target-package-wrapper(PR #10) rather thanmain; since PR #10had already merged to main, PR #11's content dead-ended on that branch and never
reached main (so it missed the 2.3.0 release). This PR brings it to main with the
correct base.
What it adds
Brace-mode multi-statement support for the expression evaluator (closes #8): an
input wrapped in
{ ... }is compiled as a statement body rather than a singleexpression, letting you declare locals and run multiple statements.
Conflict resolution carried in
This branch already has
mainmerged in (commitaa73206) with the one semanticconflict in
JdiExpressionEvaluator.javaresolved: main's PR #10 refactor movedthe field-rewrite into
evaluateInPackage(), so PR #11's!isBlockMode(...)guardwas ported there (the bare-identifier rewrite is skipped in block mode, where a
statement body may declare locals the identifier-level rewriter would mistake for
field references).
Verification
./mvnw -pl jdwp-mcp-server -am test→ BUILD SUCCESS, 935 tests pass, NullAway/ECJclean. Diff vs
mainis exactly the block-mode feature (evaluator + JDWPTools +rewrite test).