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 88916bee Match wide local variable instruction length to dumped bytes
(#525)
88916bee is described below
commit 88916beeb3aeb2501017646d3affbb2ede18f473
Author: Naveed Khan <[email protected]>
AuthorDate: Sat Jul 18 14:29:17 2026 +0000
Match wide local variable instruction length to dumped bytes (#525)
---
.../bcel/generic/LocalVariableInstruction.java | 3 +-
.../bcel/generic/LocalVariableInstructionTest.java | 85 ++++++++++++++++++++++
2 files changed, 87 insertions(+), 1 deletion(-)
diff --git
a/src/main/java/org/apache/bcel/generic/LocalVariableInstruction.java
b/src/main/java/org/apache/bcel/generic/LocalVariableInstruction.java
index 45e5db88..7be73e07 100644
--- a/src/main/java/org/apache/bcel/generic/LocalVariableInstruction.java
+++ b/src/main/java/org/apache/bcel/generic/LocalVariableInstruction.java
@@ -145,7 +145,8 @@ public abstract class LocalVariableInstruction extends
Instruction implements Ty
protected void initFromFile(final ByteSequence bytes, final boolean wide)
throws IOException {
if (wide) {
n = bytes.readUnsignedShort();
- super.setLength(4);
+ // dump() emits the wide prefix only when the index needs it, so
match that width here.
+ super.setLength(wide() ? 4 : 2);
} else {
final short opcode = super.getOpcode();
if (opcode >= Const.ILOAD && opcode <= Const.ALOAD || opcode >=
Const.ISTORE && opcode <= Const.ASTORE) {
diff --git
a/src/test/java/org/apache/bcel/generic/LocalVariableInstructionTest.java
b/src/test/java/org/apache/bcel/generic/LocalVariableInstructionTest.java
new file mode 100644
index 00000000..77cf7b0d
--- /dev/null
+++ b/src/test/java/org/apache/bcel/generic/LocalVariableInstructionTest.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.bcel.generic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.util.ByteSequence;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link LocalVariableInstruction} parsing of the {@code wide} prefix.
+ */
+class LocalVariableInstructionTest {
+
+ private static int dumpedLength(final Instruction instruction) throws
IOException {
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try (DataOutputStream dos = new DataOutputStream(bos)) {
+ instruction.dump(dos);
+ }
+ return bos.toByteArray().length;
+ }
+
+ private static Instruction readInstruction(final byte[] code) throws
IOException {
+ try (ByteSequence bytes = new ByteSequence(code)) {
+ return Instruction.readInstruction(bytes);
+ }
+ }
+
+ /**
+ * A {@code wide} prefix whose index does not fit in a byte must keep the
four-byte encoding.
+ */
+ @Test
+ void testWideLoadWithLargeIndexStaysWide() throws IOException {
+ // wide, iload, index 0x012C (300)
+ final Instruction instruction = readInstruction(new byte[] {(byte)
Const.WIDE, (byte) Const.ILOAD, 0x01, 0x2C});
+ assertEquals(300, ((ILOAD) instruction).getIndex());
+ assertEquals(4, instruction.getLength());
+ assertEquals(instruction.getLength(), dumpedLength(instruction));
+ }
+
+ /**
+ * {@code dump} emits the {@code wide} prefix only when the index needs
it, so a {@code wide} load whose index fits in a byte must report the two-byte
+ * length it actually writes rather than the four bytes it was read from.
+ */
+ @Test
+ void testWideLoadWithSmallIndexLengthMatchesDump() throws IOException {
+ // wide, iload, index 0x0005
+ final Instruction instruction = readInstruction(new byte[] {(byte)
Const.WIDE, (byte) Const.ILOAD, 0x00, 0x05});
+ assertEquals(5, ((ILOAD) instruction).getIndex());
+ assertEquals(dumpedLength(instruction), instruction.getLength());
+ }
+
+ /**
+ * The store family shares the same base-class parsing, so it must
round-trip its length too.
+ */
+ @Test
+ void testWideStoreWithSmallIndexLengthMatchesDump() throws IOException {
+ // wide, istore, index 0x0007
+ final Instruction instruction = readInstruction(new byte[] {(byte)
Const.WIDE, (byte) Const.ISTORE, 0x00, 0x07});
+ assertEquals(7, ((ISTORE) instruction).getIndex());
+ assertEquals(dumpedLength(instruction), instruction.getLength());
+ }
+}