Skip to content

[BCEL-280] MethodGen.setMaxLocals() should account for LocalVariableTable entries - #529

Merged
garydgregory merged 1 commit into
apache:masterfrom
akashchamp:BCEL-280-setMaxLocals-lvt
Sep 25, 2026
Merged

garydgregory merged 1 commit into
apache:masterfrom
akashchamp:BCEL-280-setMaxLocals-lvt

Conversation

@akashchamp

Copy link
Copy Markdown
Contributor

Fixes BCEL-280.

Problem

MethodGen.setMaxLocals() recomputes maxLocals only by scanning the
InstructionList for LocalVariableInstruction/RET/IINC operands. It
never consults the LocalVariableGen entries registered through
addLocalVariable().

Some compilers (e.g. kotlinc) emit LocalVariableTable entries for local
slots that no instruction in the method body ever references — for example
unused loop variables. If BCEL copies such a method and something later
calls setMaxLocals() again (e.g. an instrumentation pass), it silently
shrinks maxLocals below what the local variable table entries require,
producing a class file that a stricter verifier/toolchain rejects with
Invalid index N in LocalVariableTable, as reported in the issue.

Fix

setMaxLocals() now also takes each registered LocalVariableGen's
index + type.getSize() into account, the same bound addLocalVariable()
already enforces when a variable is added directly. This is a minimal,
additive change to the existing scan; behavior for methods with no such
"orphan" local variable entries is unchanged.

Testing

  • Added MethodGenTest.testSetMaxLocalsAccountsForLocalVariableTable(),
    which builds a method with a LocalVariableTable entry for a slot no
    instruction touches. It fails on unpatched code (maxLocals drops from
    7 to 1 after setMaxLocals()) and passes with the fix.
  • Ran the full suite (mvn test): 3138 tests, only the two
    BCELifierTest.testJavapCompareJava25KnownBroken cases fail, and they
    fail identically on unmodified master in this environment (a
    pre-existing, environment-specific javap/Java 25 issue unrelated to
    this change).
  • Manually built a class with ClassGen/MethodGen reproducing the
    reported shape (a method with an unreferenced LocalVariableTable
    entry, setMaxLocals() called after addLocalVariable()), dumped it to
    a real .class file, and loaded/ran it with a JVM URLClassLoader:
    maxLocals now comes out correct (7, matching the pre-setMaxLocals()
    value) instead of dropping.

I used AI (Claude/Anthropic) to help analyze the root cause, draft the fix
and the regression test, and write this description. I read the relevant
source and JIRA history myself, reproduced the bug before changing
anything, and ran the verification described above before opening this PR.

…able entries

setMaxLocals() only scanned the InstructionList for
LocalVariableInstruction/RET/IINC operands, ignoring any
LocalVariableGen entries registered via addLocalVariable(). Some
compilers (e.g. kotlinc) emit LocalVariableTable entries for local
slots that no instruction in the method body ever touches, such as
unused loop variables. When code later calls setMaxLocals() (for
example after instrumentation), it silently shrinks maxLocals below
what the local variable table requires, producing a class file that
fails verification with "Invalid index ... in LocalVariableTable".

setMaxLocals() now also considers each registered LocalVariableGen's
index + type size, mirroring the bound addLocalVariable() already
enforces when a variable is added directly.

Added MethodGenTest.testSetMaxLocalsAccountsForLocalVariableTable(),
which fails on unpatched code (maxLocals drops from 7 to 1) and
passes with the fix.

Generated-by: Claude (claude-sonnet-5)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

No unresolved issues; the fix and regression test address the reported bug.

Review effort: Lite
Findings: None

What changed in this PR

Fixes BCEL-280 by preserving local slots referenced only by LocalVariableTable entries.

Changes:

  • Include LocalVariableGen bounds when recalculating maxLocals.
  • Add regression coverage for unreferenced local-variable slots.
File Summary
src/​test/​java/​org/​apache/​bcel/​generic/​MethodGenTest.java Tests preservation of required local slots.
src/​main/​java/​org/​apache/​bcel/​generic/​MethodGen.java Accounts for local-variable table entries.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@garydgregory
garydgregory merged commit e114e70 into apache:master Sep 25, 2026
21 of 22 checks passed
garydgregory added a commit that referenced this pull request Sep 25, 2026
@garydgregory

Copy link
Copy Markdown
Member

@akashchamp
Thank you for the PR, merged 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants