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 b1a8992e Fix remaining missing StringRepresentation visitors
b1a8992e is described below
commit b1a8992eb5dd2ce91feed1f55539701e9d101c36
Author: Gary Gregory <[email protected]>
AuthorDate: Sat Nov 7 07:57:18 2026 -0500
Fix remaining missing StringRepresentation visitors
Add handlers for NestHost, MethodParameter, and StackMapType to prevent
internal errors when rendering these nodes in verifier diagnostics.
Add regression tests for successful rendering, malformed-data fallback,
all stack-map type tags, and verification of a nest member.
---
src/changes/changes.xml | 3 +
.../verifier/statics/StringRepresentation.java | 36 ++++++++++
.../verifier/statics/StringRepresentationTest.java | 79 +++++++++++++++++++++-
3 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 03366817..210cf5fe 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -70,6 +70,9 @@ The <action> type attribute can be add,update,fix,remove.
<action type="fix" dev="ggregory" due-to="Gary
Gregory">Clean up comments for spelling, grammar, and readability.</action>
<action issue="BCEL-341" type="fix" dev="ggregory" due-to="Gary Gregory,
Hippo">Support explicit parsing and generation of compact Code headers in class
file versions 45.0 through 45.2 (#58).</action>
<action type="fix" dev="ggregory" due-to="Gary Gregory,
simpleqt">Fix module descriptor rendering in StringRepresentation
(#543).</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">Fix NestHost rendering in StringRepresentation to prevent internal
errors when verifying nest members.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">Fix MethodParameter rendering in StringRepresentation.</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">Fix StackMapType rendering in StringRepresentation.</action>
<!-- ADD -->
<action type="add" dev="ggregory" due-to="Gary
Gregory">Add and use ClassFormatException.ClassFormatException(String,
Object...).</action>
<action type="add" dev="ggregory" due-to="Nicolas
Baumann, Gary Gregory">Add support for the RuntimeVisibleTypeAnnotations and
RuntimeInvisibleTypeAnnotations attributes.</action>
diff --git
a/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
b/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
index f2296443..03b13c21 100644
--- a/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
+++ b/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java
@@ -56,6 +56,7 @@ import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.LocalVariableTypeTable;
import org.apache.bcel.classfile.Method;
+import org.apache.bcel.classfile.MethodParameter;
import org.apache.bcel.classfile.MethodParameters;
import org.apache.bcel.classfile.Module;
import org.apache.bcel.classfile.ModuleExports;
@@ -64,6 +65,7 @@ import org.apache.bcel.classfile.ModuleOpens;
import org.apache.bcel.classfile.ModulePackages;
import org.apache.bcel.classfile.ModuleProvides;
import org.apache.bcel.classfile.ModuleRequires;
+import org.apache.bcel.classfile.NestHost;
import org.apache.bcel.classfile.NestMembers;
import org.apache.bcel.classfile.Node;
import org.apache.bcel.classfile.ParameterAnnotationEntry;
@@ -75,6 +77,7 @@ import org.apache.bcel.classfile.Signature;
import org.apache.bcel.classfile.SourceFile;
import org.apache.bcel.classfile.StackMap;
import org.apache.bcel.classfile.StackMapEntry;
+import org.apache.bcel.classfile.StackMapType;
import org.apache.bcel.classfile.Synthetic;
import org.apache.bcel.classfile.TypeAnnotations;
import org.apache.bcel.classfile.Unknown;
@@ -378,6 +381,17 @@ public class StringRepresentation extends
org.apache.bcel.classfile.EmptyVisitor
tostring = toString(obj);
}
+ /**
+ * Visits a method parameter.
+ *
+ * @param obj The node to represent.
+ * @since 6.14.0
+ */
+ @Override
+ public void visitMethodParameter(final MethodParameter obj) {
+ tostring = toString(obj);
+ }
+
/**
* @since 6.0
*/
@@ -456,6 +470,17 @@ public class StringRepresentation extends
org.apache.bcel.classfile.EmptyVisitor
tostring = toString(obj);
}
+ /**
+ * Visits the NestHost attribute.
+ *
+ * @param obj The node to represent.
+ * @since 6.14.0
+ */
+ @Override
+ public void visitNestHost(final NestHost obj) {
+ tostring = toString(obj);
+ }
+
/**
* @since 6.4.0
*/
@@ -523,6 +548,17 @@ public class StringRepresentation extends
org.apache.bcel.classfile.EmptyVisitor
tostring = toString(obj);
}
+ /**
+ * Visits a stack map verification type.
+ *
+ * @param obj The node to represent.
+ * @since 6.14.0
+ */
+ @Override
+ public void visitStackMapType(final StackMapType obj) {
+ tostring = toString(obj);
+ }
+
@Override
public void visitSynthetic(final Synthetic obj) {
tostring = toString(obj);
diff --git
a/src/test/java/org/apache/bcel/verifier/statics/StringRepresentationTest.java
b/src/test/java/org/apache/bcel/verifier/statics/StringRepresentationTest.java
index 4d43b2d4..d708c836 100644
---
a/src/test/java/org/apache/bcel/verifier/statics/StringRepresentationTest.java
+++
b/src/test/java/org/apache/bcel/verifier/statics/StringRepresentationTest.java
@@ -47,10 +47,14 @@ import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.MethodParameter;
import org.apache.bcel.classfile.Module;
import org.apache.bcel.classfile.ModuleMainClass;
import org.apache.bcel.classfile.ModulePackages;
+import org.apache.bcel.classfile.NestHost;
import org.apache.bcel.classfile.Node;
+import org.apache.bcel.classfile.StackMapType;
+import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.verifier.VerificationResult;
import org.apache.bcel.verifier.Verifier;
@@ -59,7 +63,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
/**
- * Tests {@link StringRepresentation} with the attributes of a module
descriptor.
+ * Tests {@link StringRepresentation} with class file attributes and their
entries.
* <p>
* A module descriptor contains attributes and entries that a class file
verifier reports when it finds something it does not know, so
* {@link StringRepresentation} must be able to represent them instead of
failing with an internal error.
@@ -111,6 +115,30 @@ class StringRepresentationTest {
assertEquals("<<ModulePackages>>", new
StringRepresentation(modulePackages).toString());
}
+ @Test
+ void testMalformedNestHost() {
+ final ConstantPoolGen pool = new ConstantPoolGen();
+ final int nameIndex = pool.addUtf8("NestHost");
+ final NestHost nestHost = new NestHost(nameIndex, 2, nameIndex,
pool.getConstantPool());
+ assertThrows(ClassFormatException.class, nestHost::toString);
+ assertEquals("<<NestHost>>", new
StringRepresentation(nestHost).toString());
+ }
+
+ @Test
+ void testMalformedStackMapType() {
+ final ConstantPoolGen pool = new ConstantPoolGen();
+ final int utf8Index = pool.addUtf8("NotAClassConstant");
+ final StackMapType type = new StackMapType(Const.ITEM_Object,
utf8Index, pool.getConstantPool());
+ assertThrows(ClassFormatException.class, type::toString);
+ assertEquals("<<StackMapType>>", new
StringRepresentation(type).toString());
+ }
+
+ @Test
+ void testMethodParameter() {
+ final MethodParameter parameter = new MethodParameter();
+ assertEquals(parameter.toString(), new
StringRepresentation(parameter).toString());
+ }
+
@Test
void testModuleAttribute() throws IOException {
final Attribute module = findAttribute(parseModuleInfo(), "Module");
@@ -169,6 +197,16 @@ class StringRepresentationTest {
}
}
+ @Test
+ void testNestHost() {
+ final ConstantPoolGen pool = new ConstantPoolGen();
+ final int nameIndex = pool.addUtf8("NestHost");
+ final int hostIndex = pool.addClass("example.Host");
+ final NestHost nestHost = new NestHost(nameIndex, 2, hostIndex,
pool.getConstantPool());
+ assertEquals("NestHost: example.Host", new
StringRepresentation(nestHost).toString());
+ assertEquals(nestHost.toString(), new
StringRepresentation(nestHost).toString());
+ }
+
@Test
void testOtherModuleDescriptorAttributes() throws IOException {
final JavaClass moduleInfo = parseModuleInfo();
@@ -185,6 +223,16 @@ class StringRepresentationTest {
}
}
+ @Test
+ void testStackMapTypes() {
+ final ConstantPoolGen pool = new ConstantPoolGen();
+ final int classIndex = pool.addClass("example.Value");
+ for (byte tag = Const.ITEM_Bogus; tag <= Const.ITEM_NewObject; tag++) {
+ final StackMapType type = new StackMapType(tag, tag ==
Const.ITEM_Object ? classIndex : 0, pool.getConstantPool());
+ assertEquals(type.toString(), new
StringRepresentation(type).toString(), "Verification type tag " + tag);
+ }
+ }
+
@Test
void testVerifierOnAModuleDescriptor() throws IOException,
ClassNotFoundException {
final JavaClass moduleInfo = parseModuleInfo();
@@ -205,4 +253,33 @@ class StringRepresentationTest {
}
}
}
+
+ @Test
+ void testVerifierOnNestMember() throws IOException, ClassNotFoundException
{
+ final ClassGen generator = new ClassGen("example.Host$Member",
"java.lang.Object", "Host.java",
+ Const.ACC_PUBLIC | Const.ACC_SUPER, new String[0]);
+ generator.setMajor(Const.MAJOR_11);
+ final ConstantPoolGen pool = generator.getConstantPool();
+ final int nameIndex = pool.addUtf8("NestHost");
+ final int hostIndex = pool.addClass("example.Host");
+ generator.addAttribute(new NestHost(nameIndex, 2, hostIndex,
pool.getConstantPool()));
+ // BCEL parses the generated Java 11 class; the JVM never loads it,
including on Java 8.
+ final JavaClass member;
+ try (InputStream input = new
ByteArrayInputStream(generator.getJavaClass().getBytes())) {
+ member = new ClassParser(input, "Host$Member.class").parse();
+ }
+ final JavaClass previous = Repository.addClass(member);
+ try {
+ final Verifier verifier =
VerifierFactory.getVerifier(member.getClassName());
+ verifier.flush();
+ assertEquals(VerificationResult.VR_OK,
assertDoesNotThrow(verifier::doPass2));
+ assertTrue(Arrays.stream(verifier.getMessages()).anyMatch(message
-> message.contains("NestHost: example.Host")
+ && message.contains("is unknown and will therefore be
ignored")));
+ } finally {
+ Repository.removeClass(member);
+ if (previous != null) {
+ Repository.addClass(previous);
+ }
+ }
+ }
}