This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push:
new e114e702 [BCEL-280] MethodGen.setMaxLocals() should account for
LocalVariableTable entries (#529)
e114e702 is described below
commit e114e702e3eb44ae922583820a05efa81ce91d8a
Author: akashchamp <[email protected]>
AuthorDate: Sat Sep 26 04:15:58 2026 +0530
[BCEL-280] MethodGen.setMaxLocals() should account for LocalVariableTable
entries (#529)
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 <[email protected]>
---
.../java/org/apache/bcel/generic/MethodGen.java | 12 +++++++++-
.../org/apache/bcel/generic/MethodGenTest.java | 26 ++++++++++++++++++++++
2 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/src/main/java/org/apache/bcel/generic/MethodGen.java
b/src/main/java/org/apache/bcel/generic/MethodGen.java
index f3bc120e..f928f42b 100644
--- a/src/main/java/org/apache/bcel/generic/MethodGen.java
+++ b/src/main/java/org/apache/bcel/generic/MethodGen.java
@@ -1192,7 +1192,8 @@ public class MethodGen extends FieldGenOrMethodGen {
}
/**
- * Sets the maximum number of local variables by examining the method
signature and instructions.
+ * Sets the maximum number of local variables by examining the method
signature, the instructions and the local
+ * variable table entries added via {@link #addLocalVariable}.
*/
public void setMaxLocals() { // TODO could be package-protected? (some
tests would need repackaging)
if (il != null) {
@@ -1209,6 +1210,15 @@ public class MethodGen extends FieldGenOrMethodGen {
}
}
}
+ // Local variables may be registered (for example, copied from an
existing LocalVariableTable) without
+ // being referenced by any instruction, e.g. unused variables
emitted by some compilers. Account for
+ // those too, so a later call does not shrink maxLocals below what
those entries require.
+ for (final LocalVariableGen lv : variableList) {
+ final int index = lv.getIndex() + lv.getType().getSize();
+ if (index > max) {
+ max = index;
+ }
+ }
maxLocals = max;
} else {
maxLocals = 0;
diff --git a/src/test/java/org/apache/bcel/generic/MethodGenTest.java
b/src/test/java/org/apache/bcel/generic/MethodGenTest.java
index 6cbd7210..b8cb5ee7 100644
--- a/src/test/java/org/apache/bcel/generic/MethodGenTest.java
+++ b/src/test/java/org/apache/bcel/generic/MethodGenTest.java
@@ -30,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.fail;
import java.util.Arrays;
import java.util.List;
+import org.apache.bcel.Const;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
@@ -143,4 +144,29 @@ class MethodGenTest {
assertNull(lv.getStart(), "scope start");
assertNull(lv.getEnd(), "scope end");
}
+
+ @Test
+ void testSetMaxLocalsAccountsForLocalVariableTable() {
+ // BCEL-280: some compilers (e.g. kotlinc) emit LocalVariableTable
entries for slots that no
+ // instruction in the method body ever touches. setMaxLocals() must
not shrink maxLocals below
+ // what those entries require, or the resulting class file fails
verification with a
+ // "Invalid index ... in LocalVariableTable" ClassFormatError.
+ final InstructionList il = new InstructionList();
+ il.append(InstructionConst.ALOAD_0);
+ il.append(InstructionConst.RETURN);
+
+ final MethodGen mg = new MethodGen(Const.ACC_PUBLIC, Type.VOID,
Type.NO_ARGS, new String[0], "bar", "Foo", il,
+ new ConstantPoolGen());
+
+ final InstructionHandle start = il.getStart();
+ final InstructionHandle end = il.getEnd();
+ // Slot 6 is only referenced through the local variable table below,
never by an instruction.
+ mg.addLocalVariable("unusedByCompiler", Type.INT, 6, start, end);
+ assertEquals(7, mg.getMaxLocals(), "addLocalVariable() should already
have grown maxLocals");
+
+ // Simulate a later pass (e.g. instrumentation) recomputing maxLocals
from the instruction list.
+ mg.setMaxLocals();
+
+ assertEquals(7, mg.getMaxLocals(), "setMaxLocals() dropped a local
variable table entry unreferenced by instructions");
+ }
}