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 8ded2ae5aa..76647901d8 100644 --- a/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java +++ b/src/main/java/org/apache/bcel/verifier/statics/StringRepresentation.java @@ -57,6 +57,13 @@ import org.apache.bcel.classfile.LocalVariableTypeTable; import org.apache.bcel.classfile.Method; import org.apache.bcel.classfile.MethodParameters; +import org.apache.bcel.classfile.Module; +import org.apache.bcel.classfile.ModuleExports; +import org.apache.bcel.classfile.ModuleMainClass; +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.NestMembers; import org.apache.bcel.classfile.Node; import org.apache.bcel.classfile.ParameterAnnotationEntry; @@ -379,6 +386,76 @@ public void visitMethodParameters(final MethodParameters obj) { tostring = toString(obj); } + /** + * Visits the Module attribute of a module descriptor. + * + * @since 6.14.0 + */ + @Override + public void visitModule(final Module obj) { + tostring = toString(obj); + } + + /** + * Visits a ModuleExports entry of the Module attribute. + * + * @since 6.14.0 + */ + @Override + public void visitModuleExports(final ModuleExports obj) { + tostring = toString(obj); + } + + /** + * Visits the ModuleMainClass attribute of a module descriptor. + * + * @since 6.14.0 + */ + @Override + public void visitModuleMainClass(final ModuleMainClass obj) { + tostring = toString(obj); + } + + /** + * Visits a ModuleOpens entry of the Module attribute. + * + * @since 6.14.0 + */ + @Override + public void visitModuleOpens(final ModuleOpens obj) { + tostring = toString(obj); + } + + /** + * Visits the ModulePackages attribute of a module descriptor. + * + * @since 6.14.0 + */ + @Override + public void visitModulePackages(final ModulePackages obj) { + tostring = toString(obj); + } + + /** + * Visits a ModuleProvides entry of the Module attribute. + * + * @since 6.14.0 + */ + @Override + public void visitModuleProvides(final ModuleProvides obj) { + tostring = toString(obj); + } + + /** + * Visits a ModuleRequires entry of the Module attribute. + * + * @since 6.14.0 + */ + @Override + public void visitModuleRequires(final ModuleRequires obj) { + tostring = toString(obj); + } + /** * @since 6.4.0 */ diff --git a/src/test/java/org/apache/bcel/verifier/statics/StringRepresentationTest.java b/src/test/java/org/apache/bcel/verifier/statics/StringRepresentationTest.java new file mode 100644 index 0000000000..858467d681 --- /dev/null +++ b/src/test/java/org/apache/bcel/verifier/statics/StringRepresentationTest.java @@ -0,0 +1,125 @@ +/* + * 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.verifier.statics; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.bcel.Repository; +import org.apache.bcel.classfile.Attribute; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.Module; +import org.apache.bcel.classfile.ModuleMainClass; +import org.apache.bcel.classfile.ModulePackages; +import org.apache.bcel.classfile.Node; +import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.verifier.Verifier; +import org.apache.bcel.verifier.VerifierFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link StringRepresentation} with the attributes of a module descriptor. + *

+ * 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. + *

+ */ +class StringRepresentationTest { + + private static final Path MODULE_INFO_PATH = Paths.get("src/test/resources/jpms/java17/commons-io/module-info.class"); + + private static JavaClass parseModuleInfo() throws IOException { + try (InputStream inputStream = Files.newInputStream(MODULE_INFO_PATH)) { + return new ClassParser(inputStream, MODULE_INFO_PATH.toString()).parse(); + } + } + + private static Attribute findAttribute(final JavaClass javaClass, final String name) { + for (final Attribute attribute : javaClass.getAttributes()) { + if (name.equals(attribute.getName())) { + return attribute; + } + } + fail("Missing attribute: " + name); + return null; + } + + @AfterEach + void afterEach() { + VerifierFactory.clear(); + } + + @Test + void testModuleAttribute() throws IOException { + final Attribute module = findAttribute(parseModuleInfo(), "Module"); + assertEquals(module.toString(), new StringRepresentation(module).toString()); + } + + @Test + void testModuleEntries() throws IOException { + final Module module = (Module) findAttribute(parseModuleInfo(), "Module"); + final List entries = new ArrayList<>(); + entries.addAll(Arrays.asList(module.getRequiresTable())); + entries.addAll(Arrays.asList(module.getExportsTable())); + entries.addAll(Arrays.asList(module.getOpensTable())); + entries.addAll(Arrays.asList(module.getProvidesTable())); + assertFalse(entries.isEmpty(), "The module descriptor is expected to have entries"); + for (final Node entry : entries) { + assertEquals(entry.toString(), new StringRepresentation(entry).toString()); + } + } + + @Test + void testOtherModuleDescriptorAttributes() throws IOException { + final JavaClass moduleInfo = parseModuleInfo(); + final ConstantPoolGen constantPoolGen = new ConstantPoolGen(moduleInfo.getConstantPool()); + final ConstantPool constantPool = constantPoolGen.getConstantPool(); + final ModulePackages modulePackages = new ModulePackages(constantPoolGen.addUtf8("ModulePackages"), 0, + new int[] {constantPoolGen.addClass("org.apache.commons.io.IOUtils")}, constantPool); + final ModuleMainClass moduleMainClass = new ModuleMainClass(constantPoolGen.addUtf8("ModuleMainClass"), 0, moduleInfo.getClassNameIndex(), + constantPool); + for (final Node node : Arrays.asList(modulePackages, moduleMainClass)) { + assertNotNull(new StringRepresentation(node).toString(), node.getClass().getName()); + } + } + + @Test + void testVerifierOnAModuleDescriptor() throws IOException { + final JavaClass moduleInfo = parseModuleInfo(); + Repository.addClass(moduleInfo); + final Verifier verifier = VerifierFactory.getVerifier(moduleInfo.getClassName()); + assertNotNull(assertDoesNotThrow(verifier::doPass2)); + } +}