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 667e888d Propagate runtime-visible flag when parsing parameter 
annotations (#509)
667e888d is described below

commit 667e888d3d3468045f13549a6161b674f6bdba36
Author: Dexter.k <[email protected]>
AuthorDate: Thu Jun 25 19:38:43 2026 +0000

    Propagate runtime-visible flag when parsing parameter annotations (#509)
    
    parameter annotations and nested annotation values were read with a 
hardcoded isRuntimeVisible=false, so a RuntimeVisibleParameterAnnotations 
attribute parsed as invisible and a MethodGen round-trip rewrote it as 
RuntimeInvisibleParameterAnnotations. Thread the flag from the attribute 
through ParameterAnnotationEntry and AnnotationEntry.read, matching Annotations.
---
 .../org/apache/bcel/classfile/AnnotationEntry.java |   2 +-
 .../org/apache/bcel/classfile/ElementValue.java    |  11 ++-
 .../bcel/classfile/ParameterAnnotationEntry.java   |   6 +-
 .../bcel/classfile/ParameterAnnotations.java       |   7 +-
 .../RuntimeInvisibleParameterAnnotations.java      |   2 +-
 .../RuntimeVisibleParameterAnnotations.java        |   2 +-
 .../generic/ParameterAnnotationVisibilityTest.java | 105 +++++++++++++++++++++
 7 files changed, 122 insertions(+), 13 deletions(-)

diff --git a/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java 
b/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java
index afe09d29..4bd0e8d3 100644
--- a/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java
+++ b/src/main/java/org/apache/bcel/classfile/AnnotationEntry.java
@@ -65,7 +65,7 @@ public class AnnotationEntry implements Node {
         final int numElementValuePairs = input.readUnsignedShort();
         for (int i = 0; i < numElementValuePairs; i++) {
             annotationEntry.elementValuePairs
-                .add(new ElementValuePair(input.readUnsignedShort(), 
ElementValue.readElementValue(input, constantPool), constantPool));
+                .add(new ElementValuePair(input.readUnsignedShort(), 
ElementValue.readElementValue(input, constantPool, isRuntimeVisible, 0), 
constantPool));
         }
         return annotationEntry;
     }
diff --git a/src/main/java/org/apache/bcel/classfile/ElementValue.java 
b/src/main/java/org/apache/bcel/classfile/ElementValue.java
index 69e3522b..40e83ea8 100644
--- a/src/main/java/org/apache/bcel/classfile/ElementValue.java
+++ b/src/main/java/org/apache/bcel/classfile/ElementValue.java
@@ -117,7 +117,11 @@ public abstract class ElementValue {
      * @throws IOException if an I/O error occurs.
      * @since 6.7.0
      */
-    public static ElementValue readElementValue(final DataInput input, final 
ConstantPool cpool, int arrayNesting)
+    public static ElementValue readElementValue(final DataInput input, final 
ConstantPool cpool, final int arrayNesting) throws IOException {
+        return readElementValue(input, cpool, false, arrayNesting);
+    }
+
+    static ElementValue readElementValue(final DataInput input, final 
ConstantPool cpool, final boolean isRuntimeVisible, int arrayNesting)
             throws IOException {
         final byte tag = input.readByte();
         switch (tag) {
@@ -139,8 +143,7 @@ public abstract class ElementValue {
             return new ClassElementValue(CLASS, input.readUnsignedShort(), 
cpool);
 
         case ANNOTATION:
-            // TODO isRuntimeVisible
-            return new AnnotationElementValue(ANNOTATION, 
AnnotationEntry.read(input, cpool, false), cpool);
+            return new AnnotationElementValue(ANNOTATION, 
AnnotationEntry.read(input, cpool, isRuntimeVisible), cpool);
 
         case ARRAY:
             arrayNesting++;
@@ -151,7 +154,7 @@ public abstract class ElementValue {
             final int numArrayVals = input.readUnsignedShort();
             final ElementValue[] evalues = new ElementValue[numArrayVals];
             for (int j = 0; j < numArrayVals; j++) {
-                evalues[j] = readElementValue(input, cpool, arrayNesting);
+                evalues[j] = readElementValue(input, cpool, isRuntimeVisible, 
arrayNesting);
             }
             return new ArrayElementValue(ARRAY, evalues, cpool);
 
diff --git 
a/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java 
b/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
index fce2cc75..4cfb5bd1 100644
--- a/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
+++ b/src/main/java/org/apache/bcel/classfile/ParameterAnnotationEntry.java
@@ -65,14 +65,14 @@ public class ParameterAnnotationEntry implements Node {
      *
      * @param input Input stream.
      * @param constantPool the constant pool.
+     * @param isRuntimeVisible whether the contained annotations are runtime 
visible.
      * @throws IOException if an I/O error occurs.
      */
-    ParameterAnnotationEntry(final DataInput input, final ConstantPool 
constantPool) throws IOException {
+    ParameterAnnotationEntry(final DataInput input, final ConstantPool 
constantPool, final boolean isRuntimeVisible) throws IOException {
         final int annotationTableLength = input.readUnsignedShort();
         annotationTable = new AnnotationEntry[annotationTableLength];
         for (int i = 0; i < annotationTableLength; i++) {
-            // TODO isRuntimeVisible
-            annotationTable[i] = AnnotationEntry.read(input, constantPool, 
false);
+            annotationTable[i] = AnnotationEntry.read(input, constantPool, 
isRuntimeVisible);
         }
     }
 
diff --git a/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java 
b/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java
index 48cbd764..bbbd9f72 100644
--- a/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java
+++ b/src/main/java/org/apache/bcel/classfile/ParameterAnnotations.java
@@ -44,14 +44,15 @@ public abstract class ParameterAnnotations extends 
Attribute implements Iterable
      * @param length Content length in bytes.
      * @param input Input stream.
      * @param constantPool Array of constants.
+     * @param isRuntimeVisible whether these parameter annotations are runtime 
visible.
      */
-    ParameterAnnotations(final byte parameterAnnotationType, final int 
nameIndex, final int length, final DataInput input, final ConstantPool 
constantPool)
-        throws IOException {
+    ParameterAnnotations(final byte parameterAnnotationType, final int 
nameIndex, final int length, final DataInput input, final ConstantPool 
constantPool,
+        final boolean isRuntimeVisible) throws IOException {
         this(parameterAnnotationType, nameIndex, length, 
(ParameterAnnotationEntry[]) null, constantPool);
         final int numParameters = input.readUnsignedByte();
         parameterAnnotationTable = new ParameterAnnotationEntry[numParameters];
         for (int i = 0; i < numParameters; i++) {
-            parameterAnnotationTable[i] = new ParameterAnnotationEntry(input, 
constantPool);
+            parameterAnnotationTable[i] = new ParameterAnnotationEntry(input, 
constantPool, isRuntimeVisible);
         }
     }
 
diff --git 
a/src/main/java/org/apache/bcel/classfile/RuntimeInvisibleParameterAnnotations.java
 
b/src/main/java/org/apache/bcel/classfile/RuntimeInvisibleParameterAnnotations.java
index 2a8518a2..27a18f59 100644
--- 
a/src/main/java/org/apache/bcel/classfile/RuntimeInvisibleParameterAnnotations.java
+++ 
b/src/main/java/org/apache/bcel/classfile/RuntimeInvisibleParameterAnnotations.java
@@ -41,6 +41,6 @@ public class RuntimeInvisibleParameterAnnotations extends 
ParameterAnnotations {
      */
     public RuntimeInvisibleParameterAnnotations(final int nameIndex, final int 
length, final DataInput input, final ConstantPool constantPool)
         throws IOException {
-        super(Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, nameIndex, 
length, input, constantPool);
+        super(Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, nameIndex, 
length, input, constantPool, false);
     }
 }
diff --git 
a/src/main/java/org/apache/bcel/classfile/RuntimeVisibleParameterAnnotations.java
 
b/src/main/java/org/apache/bcel/classfile/RuntimeVisibleParameterAnnotations.java
index 167ee53c..51d8491d 100644
--- 
a/src/main/java/org/apache/bcel/classfile/RuntimeVisibleParameterAnnotations.java
+++ 
b/src/main/java/org/apache/bcel/classfile/RuntimeVisibleParameterAnnotations.java
@@ -41,6 +41,6 @@ public class RuntimeVisibleParameterAnnotations extends 
ParameterAnnotations {
      */
     public RuntimeVisibleParameterAnnotations(final int nameIndex, final int 
length, final DataInput input, final ConstantPool constantPool)
         throws IOException {
-        super(Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, nameIndex, 
length, input, constantPool);
+        super(Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, nameIndex, 
length, input, constantPool, true);
     }
 }
diff --git 
a/src/test/java/org/apache/bcel/generic/ParameterAnnotationVisibilityTest.java 
b/src/test/java/org/apache/bcel/generic/ParameterAnnotationVisibilityTest.java
new file mode 100644
index 00000000..f4ddce12
--- /dev/null
+++ 
b/src/test/java/org/apache/bcel/generic/ParameterAnnotationVisibilityTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.bcel.AbstractTest;
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.AnnotationElementValue;
+import org.apache.bcel.classfile.AnnotationEntry;
+import org.apache.bcel.classfile.ArrayElementValue;
+import org.apache.bcel.classfile.ElementValue;
+import org.apache.bcel.classfile.ElementValuePair;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.classfile.ParameterAnnotationEntry;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Checks that the runtime-visible flag is propagated when parameter 
annotations and nested annotation values are parsed
+ * from a class file.
+ */
+class ParameterAnnotationVisibilityTest extends AbstractTest {
+
+    private JavaClass loadTestClass() throws ClassNotFoundException {
+        return getTestJavaClass(PACKAGE_BASE_NAME + 
".data.AnnotatedWithCombinedAnnotation");
+    }
+
+    private Method getConstructor(final JavaClass jc) {
+        for (final Method method : jc.getMethods()) {
+            if (Const.CONSTRUCTOR_NAME.equals(method.getName())) {
+                return method;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    void testNestedAnnotationValueIsRuntimeVisible() throws 
ClassNotFoundException {
+        final JavaClass jc = loadTestClass();
+        boolean found = false;
+        for (final AnnotationEntry outer : jc.getAnnotationEntries()) {
+            for (final ElementValuePair pair : outer.getElementValuePairs()) {
+                final ElementValue value = pair.getValue();
+                if (value instanceof ArrayElementValue) {
+                    for (final ElementValue inner : ((ArrayElementValue) 
value).getElementValuesArray()) {
+                        if (inner instanceof AnnotationElementValue) {
+                            found = true;
+                            assertTrue(((AnnotationElementValue) 
inner).getAnnotationEntry().isRuntimeVisible(),
+                                "nested annotation value parsed from a 
runtime-visible annotation reported as invisible");
+                        }
+                    }
+                }
+            }
+        }
+        assertTrue(found, "no nested annotation values found in test data");
+    }
+
+    @Test
+    void testParsedParameterAnnotationIsRuntimeVisible() throws 
ClassNotFoundException {
+        final JavaClass jc = loadTestClass();
+        final Method init = getConstructor(jc);
+        assertNotNull(init);
+        boolean found = false;
+        for (final ParameterAnnotationEntry pae : 
init.getParameterAnnotationEntries()) {
+            for (final AnnotationEntry ae : pae.getAnnotationEntries()) {
+                found = true;
+                assertTrue(ae.isRuntimeVisible(), "runtime-visible parameter 
annotation parsed as invisible");
+            }
+        }
+        assertTrue(found, "no parameter annotations found in test data");
+    }
+
+    @Test
+    void testParameterAnnotationVisibilitySurvivesMethodGen() throws 
ClassNotFoundException {
+        final JavaClass jc = loadTestClass();
+        final Method init = getConstructor(jc);
+        final ConstantPoolGen cp = new ConstantPoolGen(jc.getConstantPool());
+        final MethodGen mg = new MethodGen(init, jc.getClassName(), cp);
+        mg.getAnnotationsOnParameter(1); // unpack the existing parameter 
annotations before rebuilding
+        final Method out = mg.getMethod();
+        
assertNotNull(out.getAttribute(Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS),
+            "runtime-visible parameter annotation dropped on MethodGen 
round-trip");
+        
assertNull(out.getAttribute(Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS),
+            "runtime-visible parameter annotation downgraded to invisible on 
MethodGen round-trip");
+    }
+}

Reply via email to