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());
+    }
+}

Reply via email to