Skip to content

Use exclusive live-range end in LocalVariableTable.getLocalVariable#517

Merged
garydgregory merged 2 commits into
apache:masterfrom
rootvector2:localvariable-live-range-end
Jul 6, 2026
Merged

Use exclusive live-range end in LocalVariableTable.getLocalVariable#517
garydgregory merged 2 commits into
apache:masterfrom
rootvector2:localvariable-live-range-end

Conversation

@rootvector2

Copy link
Copy Markdown
Contributor

LocalVariableTable.getLocalVariable(index, pc) treats pc == startPc + getLength() as in scope, but JVMS 4.7.13 defines a local variable's live range as the half-open interval [start_pc, start_pc + length), so a query at start_pc + length returns a variable that is already out of scope and reports the wrong variable as live at that program counter for a parsed class. The generation side already treats the end as exclusive (MethodGen derives length from end - start). Found while auditing the range checks in the local-variable attributes. The added test builds a variable live over [2, 5) and asserts getLocalVariable(1, 5) is null, which fails before the change.

  • Read the contribution guidelines for this project.
  • Read the ASF Generative Tooling Guidance if you use Artificial Intelligence (AI).
  • I used AI to create any part of, or all of, this pull request. Which AI tool was used to create this pull request, and to what extent did it contribute?
  • Run a successful build using the default Maven goal with mvn; that's mvn on the command line by itself.
  • Write unit tests that match behavioral changes, where the tests fail if the changes to the runtime are not applied. This may not always be possible, but it is a best practice.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Each commit in the pull request should have a meaningful subject line and body.

JVMS 4.7.13 defines a local variable's live range as the half-open interval [start_pc, start_pc + length), so a lookup at start_pc + length must not match. Change the upper comparison from pc <= endPc to pc < endPc.
@garydgregory

Copy link
Copy Markdown
Member

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.

Pull request overview

Fixes LocalVariableTable#getLocalVariable(int index, int pc) to match JVMS 4.7.13 local-variable live-range semantics by treating the end of the range as exclusive, preventing out-of-scope variables from being reported as live at start_pc + length.

Changes:

  • Update LocalVariableTable#getLocalVariable(index, pc) to use a half-open interval check (pc < startPc + length).
  • Add a unit test that asserts pc == startPc + length is out of scope.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/main/java/org/apache/bcel/classfile/LocalVariableTable.java Adjusts the live-range end check to be exclusive per JVMS.
src/test/java/org/apache/bcel/classfile/LocalVariableTableTest.java Adds coverage for the exclusive-end behavior of getLocalVariable(index, pc).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +38 to +41
final ConstantPool constantPool = new ConstantPool(new Constant[1]);
// Variable in slot 1, live at pcs 2, 3, 4 (start 2, length 3).
final LocalVariable variable = new LocalVariable(2, 3, 0, 0, 1, constantPool);
final LocalVariableTable table = new LocalVariableTable(0, 0, new LocalVariable[] {variable}, constantPool);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed in ac35334, the variable now points at valid utf8 name/signature entries so a failing assertion prints the LocalVariable instead of throwing ClassFormatException.

@garydgregory

Copy link
Copy Markdown
Member

Hi @rootvector2
Thank you for the PR.

Please see the comment from copilot, it might be worth fixing. In the future, you can run copilot on your own PRs to see if it finds anything interesting.

Point the test variable at real Utf8 name and signature entries so a
failed assertion stringifies the LocalVariable instead of throwing
ClassFormatException. Also link the JVMS se25 spec in the javadoc.
@rootvector2

Copy link
Copy Markdown
Contributor Author

Done, the test now uses real utf8 entries at indices 1 and 2 so a failed assertion stays readable, and the test javadoc links the se25 spec you referenced. Full default mvn goal is still green. Will run copilot on my own PRs going forward.

@garydgregory garydgregory merged commit d5d17bd into apache:master Jul 6, 2026
21 of 22 checks passed
@garydgregory

Copy link
Copy Markdown
Member

Merged 🚀 Thank you @rootvector2
Do you have any thoughts on the Java 27-ea failures? I'm not sure how likely byte codes and class file formats are likely to change until the release in September, I would guess not.

@rootvector2

Copy link
Copy Markdown
Contributor Author

I looked at the 27-ea job: the only failure is ConstantPoolModuleAccessTest.testJREModules on java.base. 27-ea swapped the internal sun.text.spi.JavaTimeDateTimePatternProvider for the new java.time.format.DateTimeFormatterPatternProvider in the uses list, so the hardcoded expectations drifted. bcel parses the v71 module-info.class fine, so it's not a format change, and I agree the format itself is unlikely to move before September. the test already handles this kind of drift with getMajor() conditionals, so a > Const.MAJOR_26 branch covering those two entries would green it.

thanks for merging this.

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