This is an automated email from the ASF dual-hosted git repository. ggregory 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 3974fe2a Add accessors to model and unit tests, Javadoc (#183) 3974fe2a is described below commit 3974fe2a94a23a973cc8ebe3124dc272ee950b35 Author: nbauma109 <nbauma...@users.noreply.github.com> AuthorDate: Fri Jun 21 23:01:25 2024 +0200 Add accessors to model and unit tests, Javadoc (#183) * added accessors to model and unit tests, javadoc comments Co-authored-by: nbauma109 <nbauma...@github.com> * removed getters that are not useful directly for end users * test for null LocalVariableTypeTable * added test for null class attribute * added test for null LocalVariableTypeTable with Code attribute present * added module flags and tests * @since 6.7.1 -> @since 6.8.0 * Fix unit test ConstantPoolModuleAccessTestCase * Update since tags to 6.10.0 * checkstyles fixes * checkstyle fix on EmptyClass * removed expected jdk.internal.org.jline.terminal.spi.JnaSupport * Fix unit test ConstantPoolModuleAccessTestCase * ConstantPoolModuleAccessTestCase: add url in assert * ConstantPoolModuleAccessTestCase: added message in assertion * ConstantPoolModuleAccessTestCase: remove version number in check * ConstantPoolModuleAccessTestCase: update test for Java 21 * ConstantPoolModuleAccessTestCase: update for Java 21 --------- Co-authored-by: nbauma109 <nbauma...@github.com> --- src/main/java/org/apache/bcel/classfile/Code.java | 14 + .../org/apache/bcel/classfile/FieldOrMethod.java | 16 + .../java/org/apache/bcel/classfile/JavaClass.java | 16 + .../java/org/apache/bcel/classfile/Method.java | 15 +- .../java/org/apache/bcel/classfile/Module.java | 60 +++- .../org/apache/bcel/classfile/ModuleExports.java | 47 ++- .../org/apache/bcel/classfile/ModuleOpens.java | 47 ++- .../org/apache/bcel/classfile/ModuleProvides.java | 43 ++- .../org/apache/bcel/classfile/ModuleRequires.java | 37 ++- .../org/apache/bcel/AnonymousClassTestCase.java | 14 + .../bcel/LocalVariableTypeTableTestCase.java | 20 ++ .../ConstantPoolModuleAccessTestCase.java | 358 +++++++++++++++++++++ src/test/java/org/apache/bcel/data/EmptyClass.java | 22 ++ .../bcel/generic/FieldAnnotationsTestCase.java | 17 +- .../GeneratingAnnotatedClassesTestCase.java | 4 + 15 files changed, 695 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/apache/bcel/classfile/Code.java b/src/main/java/org/apache/bcel/classfile/Code.java index 8ec211fb..d94ba66d 100644 --- a/src/main/java/org/apache/bcel/classfile/Code.java +++ b/src/main/java/org/apache/bcel/classfile/Code.java @@ -259,6 +259,20 @@ public final class Code extends Attribute { return null; } + /** + * Gets the local variable type table attribute {@link LocalVariableTypeTable}. + * @return LocalVariableTypeTable of Code, if it has one, null otherwise. + * @since 6.10.0 + */ + public LocalVariableTypeTable getLocalVariableTypeTable() { + for (final Attribute attribute : attributes) { + if (attribute instanceof LocalVariableTypeTable) { + return (LocalVariableTypeTable) attribute; + } + } + return null; + } + /** * @return Number of local variables. */ diff --git a/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java b/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java index 1b2081ed..f67c1be7 100644 --- a/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java +++ b/src/main/java/org/apache/bcel/classfile/FieldOrMethod.java @@ -171,6 +171,22 @@ public abstract class FieldOrMethod extends AccessFlags implements Cloneable, No return attributes; } + /** + * Gets attribute for given tag. + * @return Attribute for given tag, null if not found. + * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values. + * @since 6.10.0 + */ + @SuppressWarnings("unchecked") + public final <T extends Attribute> T getAttribute(final byte tag) { + for (final Attribute attribute : getAttributes()) { + if (attribute.getTag() == tag) { + return (T) attribute; + } + } + return null; + } + /** * @return Constant pool used by this object. */ diff --git a/src/main/java/org/apache/bcel/classfile/JavaClass.java b/src/main/java/org/apache/bcel/classfile/JavaClass.java index d52336d0..f5899989 100644 --- a/src/main/java/org/apache/bcel/classfile/JavaClass.java +++ b/src/main/java/org/apache/bcel/classfile/JavaClass.java @@ -489,6 +489,22 @@ public class JavaClass extends AccessFlags implements Cloneable, Node, Comparabl return attributes; } + /** + * Gets attribute for given tag. + * @return Attribute for given tag, null if not found. + * Refer to {@link org.apache.bcel.Const#ATTR_UNKNOWN} constants named ATTR_* for possible values. + * @since 6.10.0 + */ + @SuppressWarnings("unchecked") + public final <T extends Attribute> T getAttribute(final byte tag) { + for (final Attribute attribute : getAttributes()) { + if (attribute.getTag() == tag) { + return (T) attribute; + } + } + return null; + } + /** * @return class in binary format */ diff --git a/src/main/java/org/apache/bcel/classfile/Method.java b/src/main/java/org/apache/bcel/classfile/Method.java index 57a70ff8..8de79040 100644 --- a/src/main/java/org/apache/bcel/classfile/Method.java +++ b/src/main/java/org/apache/bcel/classfile/Method.java @@ -177,7 +177,7 @@ public final class Method extends FieldOrMethod { } /** - * @return LocalVariableTable of code attribute if any, i.e. the call is forwarded to the Code atribute. + * @return LocalVariableTable of code attribute if any, i.e. the call is forwarded to the Code attribute. */ public LocalVariableTable getLocalVariableTable() { final Code code = getCode(); @@ -187,6 +187,19 @@ public final class Method extends FieldOrMethod { return code.getLocalVariableTable(); } + /** + * Gets the local variable type table attribute {@link LocalVariableTypeTable}. + * @return LocalVariableTypeTable of code attribute if any, i.e. the call is forwarded to the Code attribute. + * @since 6.10.0 + */ + public LocalVariableTypeTable getLocalVariableTypeTable() { + final Code code = getCode(); + if (code == null) { + return null; + } + return code.getLocalVariableTypeTable(); + } + /** * @return Annotations on the parameters of a method * @since 6.0 diff --git a/src/main/java/org/apache/bcel/classfile/Module.java b/src/main/java/org/apache/bcel/classfile/Module.java index 11aea316..5915522c 100644 --- a/src/main/java/org/apache/bcel/classfile/Module.java +++ b/src/main/java/org/apache/bcel/classfile/Module.java @@ -109,7 +109,57 @@ public final class Module extends Attribute { v.visitModule(this); } - // TODO add more getters and setters? + /** + * Gets version for this module. + * @param cp Array of constants + * @return version from constant pool, "0" if version index is 0 + * @since 6.10.0 + */ + public String getVersion(final ConstantPool cp) { + return moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8); + } + + /** + * Gets flags for this module. + * @return module flags + * @since 6.10.0 + */ + public int getModuleFlags() { + return moduleFlags; + } + + /** + * Gets module name. + * @param cp Array of constants + * @return module name + * @since 6.10.0 + */ + public String getModuleName(final ConstantPool cp) { + return cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module); + } + + /** + * Gets the array of class names for this module's uses. + * @param constantPool Array of constants usually obtained from the ClassFile object + * @param compactClassName false for original constant pool value, true to replace '/' with '.' + * @return array of used class names + * @since 6.10.0 + */ + public String[] getUsedClassNames(final ConstantPool constantPool, final boolean compactClassName) { + final String[] usedClassNames = new String[usesCount]; + for (int i = 0; i < usesCount; i++) { + usedClassNames[i] = getClassNameAtIndex(constantPool, usesIndex[i], compactClassName); + } + return usedClassNames; + } + + private static String getClassNameAtIndex(final ConstantPool cp, final int index, final boolean compactClassName) { + final String className = cp.getConstantString(index, Const.CONSTANT_Class); + if (compactClassName) { + return Utility.compactClassName(className, false); + } + return className; + } /** * @return deep copy of this attribute @@ -214,9 +264,9 @@ public final class Module extends Attribute { final ConstantPool cp = super.getConstantPool(); final StringBuilder buf = new StringBuilder(); buf.append("Module:\n"); - buf.append(" name: ").append(Utility.pathToPackage(cp.getConstantString(moduleNameIndex, Const.CONSTANT_Module))).append("\n"); + buf.append(" name: ").append(Utility.pathToPackage(getModuleName(cp))).append("\n"); buf.append(" flags: ").append(String.format("%04x", moduleFlags)).append("\n"); - final String version = moduleVersionIndex == 0 ? "0" : cp.getConstantString(moduleVersionIndex, Const.CONSTANT_Utf8); + final String version = getVersion(cp); buf.append(" version: ").append(version).append("\n"); buf.append(" requires(").append(requiresTable.length).append("):\n"); @@ -236,8 +286,8 @@ public final class Module extends Attribute { buf.append(" uses(").append(usesIndex.length).append("):\n"); for (final int index : usesIndex) { - final String className = cp.getConstantString(index, Const.CONSTANT_Class); - buf.append(" ").append(Utility.compactClassName(className, false)).append("\n"); + final String className = getClassNameAtIndex(cp, index, true); + buf.append(" ").append(className).append("\n"); } buf.append(" provides(").append(providesTable.length).append("):\n"); diff --git a/src/main/java/org/apache/bcel/classfile/ModuleExports.java b/src/main/java/org/apache/bcel/classfile/ModuleExports.java index 463ce4fd..5653e82d 100644 --- a/src/main/java/org/apache/bcel/classfile/ModuleExports.java +++ b/src/main/java/org/apache/bcel/classfile/ModuleExports.java @@ -53,6 +53,43 @@ public final class ModuleExports implements Cloneable, Node { } } + /** + * Gets the flags for this ModuleExports. + * @return the exportsFlags + * @since 6.10.0 + */ + public int getExportsFlags() { + return exportsFlags; + } + + /** + * Gets an array of module names for this ModuleExports. + * @param constantPool Array of constants usually obtained from the ClassFile object + * @return array of module names following 'exports to' + * @since 6.10.0 + */ + public String[] getToModuleNames(final ConstantPool constantPool) { + final String[] toModuleNames = new String[exportsToCount]; + for (int i = 0; i < exportsToCount; i++) { + toModuleNames[i] = getToModuleNameAtIndex(constantPool, exportsToIndex[i]); + } + return toModuleNames; + } + + private static String getToModuleNameAtIndex(final ConstantPool constantPool, final int index) { + return constantPool.getConstantString(index, Const.CONSTANT_Module); + } + + /** + * Gets the exported package name. + * @param constantPool the constant pool from the ClassFile + * @return the exported package name + * @since 6.10.0 + */ + public String getPackageName(final ConstantPool constantPool) { + return constantPool.constantToString(exportsIndex, Const.CONSTANT_Package); + } + /** * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. @@ -64,8 +101,6 @@ public final class ModuleExports implements Cloneable, Node { v.visitModuleExports(this); } - // TODO add more getters and setters? - /** * @return deep copy of this object */ @@ -106,13 +141,13 @@ public final class ModuleExports implements Cloneable, Node { */ public String toString(final ConstantPool constantPool) { final StringBuilder buf = new StringBuilder(); - final String packageName = constantPool.constantToString(exportsIndex, Const.CONSTANT_Package); - buf.append(Utility.compactClassName(packageName, false)); + final String packageName = getPackageName(constantPool); + buf.append(packageName); buf.append(", ").append(String.format("%04x", exportsFlags)); buf.append(", to(").append(exportsToCount).append("):\n"); for (final int index : exportsToIndex) { - final String moduleName = constantPool.getConstantString(index, Const.CONSTANT_Module); - buf.append(" ").append(Utility.compactClassName(moduleName, false)).append("\n"); + final String moduleName = getToModuleNameAtIndex(constantPool, index); + buf.append(" ").append(moduleName).append("\n"); } return buf.substring(0, buf.length() - 1); // remove the last newline } diff --git a/src/main/java/org/apache/bcel/classfile/ModuleOpens.java b/src/main/java/org/apache/bcel/classfile/ModuleOpens.java index 00c0f3a3..f8da77c7 100644 --- a/src/main/java/org/apache/bcel/classfile/ModuleOpens.java +++ b/src/main/java/org/apache/bcel/classfile/ModuleOpens.java @@ -53,6 +53,43 @@ public final class ModuleOpens implements Cloneable, Node { } } + /** + * Gets the flags for this ModuleOpens. + * @return the opensFlags + * @since 6.10.0 + */ + public int getOpensFlags() { + return opensFlags; + } + + /** + * Gets an array of module names for this ModuleOpens. + * @param constantPool Array of constants usually obtained from the ClassFile object + * @return array of module names following 'opens to' + * @since 6.10.0 + */ + public String[] getToModuleNames(final ConstantPool constantPool) { + final String[] toModuleNames = new String[opensToCount]; + for (int i = 0; i < opensToCount; i++) { + toModuleNames[i] = getToModuleNameAtIndex(constantPool, opensToIndex[i]); + } + return toModuleNames; + } + + private static String getToModuleNameAtIndex(final ConstantPool constantPool, final int index) { + return constantPool.getConstantString(index, Const.CONSTANT_Module); + } + + /** + * Gets the opened package name. + * @param constantPool the constant pool from the ClassFile + * @return the opened package name + * @since 6.10.0 + */ + public String getPackageName(final ConstantPool constantPool) { + return constantPool.constantToString(opensIndex, Const.CONSTANT_Package); + } + /** * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. @@ -64,8 +101,6 @@ public final class ModuleOpens implements Cloneable, Node { v.visitModuleOpens(this); } - // TODO add more getters and setters? - /** * @return deep copy of this object */ @@ -106,13 +141,13 @@ public final class ModuleOpens implements Cloneable, Node { */ public String toString(final ConstantPool constantPool) { final StringBuilder buf = new StringBuilder(); - final String packageName = constantPool.constantToString(opensIndex, Const.CONSTANT_Package); - buf.append(Utility.compactClassName(packageName, false)); + final String packageName = getPackageName(constantPool); + buf.append(packageName); buf.append(", ").append(String.format("%04x", opensFlags)); buf.append(", to(").append(opensToCount).append("):\n"); for (final int index : opensToIndex) { - final String moduleName = constantPool.getConstantString(index, Const.CONSTANT_Module); - buf.append(" ").append(Utility.compactClassName(moduleName, false)).append("\n"); + final String moduleName = getToModuleNameAtIndex(constantPool, index); + buf.append(" ").append(moduleName).append("\n"); } return buf.substring(0, buf.length() - 1); // remove the last newline } diff --git a/src/main/java/org/apache/bcel/classfile/ModuleProvides.java b/src/main/java/org/apache/bcel/classfile/ModuleProvides.java index af0778af..8c88f170 100644 --- a/src/main/java/org/apache/bcel/classfile/ModuleProvides.java +++ b/src/main/java/org/apache/bcel/classfile/ModuleProvides.java @@ -51,6 +51,39 @@ public final class ModuleProvides implements Cloneable, Node { } } + /** + * Gets the array of implementation class names for this ModuleProvides. + * @param constantPool Array of constants usually obtained from the ClassFile object + * @param compactClassName false for original constant pool value, true to replace '/' with '.' + * @return array of implementation class names + * @since 6.10.0 + */ + public String[] getImplementationClassNames(final ConstantPool constantPool, final boolean compactClassName) { + final String[] implementationClassNames = new String[providesWithCount]; + for (int i = 0; i < providesWithCount; i++) { + implementationClassNames[i] = getImplementationClassNameAtIndex(constantPool, providesWithIndex[i], compactClassName); + } + return implementationClassNames; + } + + private static String getImplementationClassNameAtIndex(final ConstantPool constantPool, final int index, final boolean compactClassName) { + final String className = constantPool.getConstantString(index, Const.CONSTANT_Class); + if (compactClassName) { + return Utility.compactClassName(className, false); + } + return className; + } + + /** + * Gets the interface name for this ModuleProvides. + * @param constantPool Array of constants usually obtained from the ClassFile object + * @return interface name + * @since 6.10.0 + */ + public String getInterfaceName(final ConstantPool constantPool) { + return constantPool.constantToString(providesIndex, Const.CONSTANT_Class); + } + /** * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. @@ -62,8 +95,6 @@ public final class ModuleProvides implements Cloneable, Node { v.visitModuleProvides(this); } - // TODO add more getters and setters? - /** * @return deep copy of this object */ @@ -103,12 +134,12 @@ public final class ModuleProvides implements Cloneable, Node { */ public String toString(final ConstantPool constantPool) { final StringBuilder buf = new StringBuilder(); - final String interfaceName = constantPool.constantToString(providesIndex, Const.CONSTANT_Class); - buf.append(Utility.compactClassName(interfaceName, false)); + final String interfaceName = getInterfaceName(constantPool); + buf.append(interfaceName); buf.append(", with(").append(providesWithCount).append("):\n"); for (final int index : providesWithIndex) { - final String className = constantPool.getConstantString(index, Const.CONSTANT_Class); - buf.append(" ").append(Utility.compactClassName(className, false)).append("\n"); + final String className = getImplementationClassNameAtIndex(constantPool, index, true); + buf.append(" ").append(className).append("\n"); } return buf.substring(0, buf.length() - 1); // remove the last newline } diff --git a/src/main/java/org/apache/bcel/classfile/ModuleRequires.java b/src/main/java/org/apache/bcel/classfile/ModuleRequires.java index 371c35c6..678b0843 100644 --- a/src/main/java/org/apache/bcel/classfile/ModuleRequires.java +++ b/src/main/java/org/apache/bcel/classfile/ModuleRequires.java @@ -48,6 +48,35 @@ public final class ModuleRequires implements Cloneable, Node { requiresVersionIndex = file.readUnsignedShort(); } + /** + * Gets the flags for this ModuleRequires. + * @return the requiresFlags + * @since 6.10.0 + */ + public int getRequiresFlags() { + return requiresFlags; + } + + /** + * Gets the required version from the constant pool. + * @param constantPool Array of constants usually obtained from the ClassFile object + * @return required version, "0" if version index is 0. + * @since 6.10.0 + */ + public String getVersion(final ConstantPool constantPool) { + return requiresVersionIndex == 0 ? "0" : constantPool.getConstantString(requiresVersionIndex, Const.CONSTANT_Utf8); + } + + /** + * Gets the module name from the constant pool. + * @param constantPool Array of constants usually obtained from the ClassFile object + * @return module name + * @since 6.10.0 + */ + public String getModuleName(final ConstantPool constantPool) { + return constantPool.constantToString(requiresIndex, Const.CONSTANT_Module); + } + /** * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. @@ -59,8 +88,6 @@ public final class ModuleRequires implements Cloneable, Node { v.visitModuleRequires(this); } - // TODO add more getters and setters? - /** * @return deep copy of this object */ @@ -98,10 +125,10 @@ public final class ModuleRequires implements Cloneable, Node { */ public String toString(final ConstantPool constantPool) { final StringBuilder buf = new StringBuilder(); - final String moduleName = constantPool.constantToString(requiresIndex, Const.CONSTANT_Module); - buf.append(Utility.compactClassName(moduleName, false)); + final String moduleName = getModuleName(constantPool); + buf.append(moduleName); buf.append(", ").append(String.format("%04x", requiresFlags)); - final String version = requiresVersionIndex == 0 ? "0" : constantPool.getConstantString(requiresVersionIndex, Const.CONSTANT_Utf8); + final String version = getVersion(constantPool); buf.append(", ").append(version); return buf.toString(); } diff --git a/src/test/java/org/apache/bcel/AnonymousClassTestCase.java b/src/test/java/org/apache/bcel/AnonymousClassTestCase.java index 1d65fa96..ed6f0dd7 100644 --- a/src/test/java/org/apache/bcel/AnonymousClassTestCase.java +++ b/src/test/java/org/apache/bcel/AnonymousClassTestCase.java @@ -17,10 +17,15 @@ package org.apache.bcel; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.apache.bcel.classfile.InnerClass; +import org.apache.bcel.classfile.InnerClasses; import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.data.EmptyClass; import org.junit.jupiter.api.Test; public class AnonymousClassTestCase extends AbstractTestCase { @@ -45,6 +50,15 @@ public class AnonymousClassTestCase extends AbstractTestCase { assertFalse(clazz.isNested(), "regular outer classes are not nested"); } + @Test + public void testRegularClassInnerClasses() throws ClassNotFoundException { + final JavaClass clazz = getTestJavaClass(PACKAGE_BASE_NAME + ".data.AnonymousClassTest"); + final InnerClasses innerClasses = clazz.getAttribute(Const.ATTR_INNER_CLASSES); + final InnerClass[] innerClassArray = innerClasses.getInnerClasses(); + assertEquals(3, innerClassArray.length); + assertNull(Repository.lookupClass(EmptyClass.class).getAttribute(Const.ATTR_INNER_CLASSES)); + } + @Test public void testStaticInnerClassIsNotAnonymous() throws ClassNotFoundException { final JavaClass clazz = getTestJavaClass(PACKAGE_BASE_NAME + ".data.AnonymousClassTest$Y"); diff --git a/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java b/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java index 6d5068ef..6855069e 100644 --- a/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java +++ b/src/test/java/org/apache/bcel/LocalVariableTypeTableTestCase.java @@ -21,6 +21,7 @@ import java.util.LinkedList; import java.util.List; import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.LocalVariableTypeTable; import org.apache.bcel.classfile.Method; import org.apache.bcel.generic.ACONST_NULL; import org.apache.bcel.generic.ALOAD; @@ -34,6 +35,9 @@ import org.apache.bcel.generic.MethodGen; import org.apache.bcel.generic.Type; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + public class LocalVariableTypeTableTestCase extends AbstractTestCase { public class TestClassLoader extends ClassLoader { @@ -143,4 +147,20 @@ public class LocalVariableTypeTableTestCase extends AbstractTestCase { method = cls.getDeclaredMethod("d", List.class, String.class); method.invoke(null, new LinkedList<>(), "d2"); } + + @Test + public void testGetLocalVariableTypeTable() throws ClassNotFoundException, NoSuchMethodException, SecurityException { + final JavaClass testJavaClass = getTestJavaClass("org/apache/commons/lang3/function/TriFunction"); + final String expectedToString = "LocalVariableTypes(startPc = 0, length = 17, index = 0:org.apache.commons.lang3.function.TriFunction<T, U, V, R> this)"; + for (final Method method : testJavaClass.getMethods()) { + if ("lambda$andThen$0".equals(method.getName())) { + final LocalVariableTypeTable localVariableTypeTable = method.getLocalVariableTypeTable(); + assertEquals(expectedToString, localVariableTypeTable.toString()); + } + if ("apply".equals(method.getName())) { + assertNull(method.getLocalVariableTypeTable()); + } + } + assertNull(Repository.lookupClass(Object.class).getMethod(Object.class.getMethod("toString")).getLocalVariableTypeTable()); + } } diff --git a/src/test/java/org/apache/bcel/classfile/ConstantPoolModuleAccessTestCase.java b/src/test/java/org/apache/bcel/classfile/ConstantPoolModuleAccessTestCase.java new file mode 100644 index 00000000..5202c4be --- /dev/null +++ b/src/test/java/org/apache/bcel/classfile/ConstantPoolModuleAccessTestCase.java @@ -0,0 +1,358 @@ +/* + * 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 + * + * http://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.classfile; + +import org.apache.bcel.Const; +import org.junit.jupiter.api.Test; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests {@code module-info.class} files. + */ +public class ConstantPoolModuleAccessTestCase { + + @Test + public void testJREModules() throws Exception { + final Enumeration<URL> moduleURLs = getClass().getClassLoader().getResources("module-info.class"); + while (moduleURLs.hasMoreElements()) { + final URL url = moduleURLs.nextElement(); + try (InputStream inputStream = url.openStream()) { + final ClassParser classParser = new ClassParser(inputStream, "module-info.class"); + final JavaClass javaClass = classParser.parse(); + final ConstantPool constantPool = javaClass.getConstantPool(); + final EmptyVisitor visitor = new EmptyVisitor() { + @Override + public void visitModule(final Module obj) { + if (url.getPath().contains("/commons-")) { + assertEquals(4096, obj.getModuleFlags(), url.toString()); + } else { + assertEquals(0, obj.getModuleFlags(), url.toString()); + } + final String[] usedClassNames = obj.getUsedClassNames(constantPool, true); + if (url.getPath().contains("junit-jupiter-engine")) { + assertEquals(1, usedClassNames.length); + assertEquals("org.junit.jupiter.api.extension.Extension", usedClassNames[0]); + } else if (url.getPath().contains("junit-platform-launcher")) { + final List<String> expected = new ArrayList<>(); + expected.add("org.junit.platform.engine.TestEngine"); + expected.add("org.junit.platform.launcher.LauncherDiscoveryListener"); + expected.add("org.junit.platform.launcher.LauncherInterceptor"); + expected.add("org.junit.platform.launcher.LauncherSessionListener"); + expected.add("org.junit.platform.launcher.PostDiscoveryFilter"); + expected.add("org.junit.platform.launcher.TestExecutionListener"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.rmi/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("java.rmi.server.RMIClassLoaderSpi"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.xml/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("javax.xml.datatype.DatatypeFactory"); + expected.add("javax.xml.parsers.DocumentBuilderFactory"); + expected.add("javax.xml.parsers.SAXParserFactory"); + expected.add("javax.xml.stream.XMLEventFactory"); + expected.add("javax.xml.stream.XMLInputFactory"); + expected.add("javax.xml.stream.XMLOutputFactory"); + expected.add("javax.xml.transform.TransformerFactory"); + expected.add("javax.xml.validation.SchemaFactory"); + expected.add("javax.xml.xpath.XPathFactory"); + expected.add("org.xml.sax.XMLReader"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.datatransfer/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("sun.datatransfer.DesktopDatatransferService"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.desktop/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("java.awt.im.spi.InputMethodDescriptor"); + expected.add("javax.accessibility.AccessibilityProvider"); + expected.add("javax.imageio.spi.ImageInputStreamSpi"); + expected.add("javax.imageio.spi.ImageOutputStreamSpi"); + expected.add("javax.imageio.spi.ImageReaderSpi"); + expected.add("javax.imageio.spi.ImageTranscoderSpi"); + expected.add("javax.imageio.spi.ImageWriterSpi"); + expected.add("javax.print.PrintServiceLookup"); + expected.add("javax.print.StreamPrintServiceFactory"); + expected.add("javax.sound.midi.spi.MidiDeviceProvider"); + expected.add("javax.sound.midi.spi.MidiFileReader"); + expected.add("javax.sound.midi.spi.MidiFileWriter"); + expected.add("javax.sound.midi.spi.SoundbankReader"); + expected.add("javax.sound.sampled.spi.AudioFileReader"); + expected.add("javax.sound.sampled.spi.AudioFileWriter"); + expected.add("javax.sound.sampled.spi.FormatConversionProvider"); + expected.add("javax.sound.sampled.spi.MixerProvider"); + expected.add("sun.swing.InteropProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.naming/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("javax.naming.ldap.StartTlsResponse"); + expected.add("javax.naming.spi.InitialContextFactory"); + if (javaClass.getMajor() > Const.MAJOR_11) { + expected.add("javax.naming.ldap.spi.LdapDnsProvider"); + } + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.prefs/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("java.util.prefs.PreferencesFactory"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.base/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("java.lang.System$LoggerFinder"); + expected.add("java.net.ContentHandlerFactory"); + if (javaClass.getMajor() > Const.MAJOR_17) { + expected.add("java.net.spi.InetAddressResolverProvider"); + } + expected.add("java.net.spi.URLStreamHandlerProvider"); + expected.add("java.nio.channels.spi.AsynchronousChannelProvider"); + expected.add("java.nio.channels.spi.SelectorProvider"); + expected.add("java.nio.charset.spi.CharsetProvider"); + expected.add("java.nio.file.spi.FileSystemProvider"); + expected.add("java.nio.file.spi.FileTypeDetector"); + expected.add("java.security.Provider"); + expected.add("java.text.spi.BreakIteratorProvider"); + expected.add("java.text.spi.CollatorProvider"); + expected.add("java.text.spi.DateFormatProvider"); + expected.add("java.text.spi.DateFormatSymbolsProvider"); + expected.add("java.text.spi.DecimalFormatSymbolsProvider"); + expected.add("java.text.spi.NumberFormatProvider"); + expected.add("java.time.chrono.AbstractChronology"); + expected.add("java.time.chrono.Chronology"); + expected.add("java.time.zone.ZoneRulesProvider"); + if (javaClass.getMajor() > Const.MAJOR_11) { + expected.add("java.util.random.RandomGenerator"); + } + expected.add("java.util.spi.CalendarDataProvider"); + expected.add("java.util.spi.CalendarNameProvider"); + expected.add("java.util.spi.CurrencyNameProvider"); + expected.add("java.util.spi.LocaleNameProvider"); + expected.add("java.util.spi.ResourceBundleControlProvider"); + expected.add("java.util.spi.ResourceBundleProvider"); + expected.add("java.util.spi.TimeZoneNameProvider"); + expected.add("java.util.spi.ToolProvider"); + expected.add("javax.security.auth.spi.LoginModule"); + if (javaClass.getMajor() > Const.MAJOR_17) { + expected.add("jdk.internal.io.JdkConsoleProvider"); + } + expected.add("jdk.internal.logger.DefaultLoggerFinder"); + expected.add("sun.text.spi.JavaTimeDateTimePatternProvider"); + expected.add("sun.util.locale.provider.LocaleDataMetaInfo"); + expected.add("sun.util.resources.LocaleData$CommonResourceBundleProvider"); + expected.add("sun.util.resources.LocaleData$SupplementaryResourceBundleProvider"); + expected.add("sun.util.spi.CalendarProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.management.agent/module-info.class") && javaClass.getMajor() < Const.MAJOR_21) { + final List<String> expected = new ArrayList<>(); + expected.add("jdk.internal.agent.spi.AgentProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.management/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("javax.management.remote.JMXConnectorProvider"); + expected.add("javax.management.remote.JMXConnectorServerProvider"); + expected.add("sun.management.spi.PlatformMBeanProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.sql/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("java.sql.Driver"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.httpserver/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("com.sun.net.httpserver.spi.HttpServerProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.sql.rowset/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("javax.sql.rowset.RowSetFactory"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.compiler/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("javax.tools.DocumentationTool"); + expected.add("javax.tools.JavaCompiler"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/java.scripting/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("javax.script.ScriptEngineFactory"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.dynalink/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("jdk.dynalink.linker.GuardingDynamicLinkerExporter"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.jdi/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("com.sun.jdi.connect.Connector"); + expected.add("com.sun.jdi.connect.spi.TransportService"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.compiler/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("javax.annotation.processing.Processor"); + expected.add("com.sun.source.util.Plugin"); + if (javaClass.getMajor() > Const.MAJOR_11) { + expected.add("com.sun.tools.doclint.DocLint"); + } + expected.add("com.sun.tools.javac.platform.PlatformProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.jconsole/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("com.sun.tools.jconsole.JConsolePlugin"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.attach/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("com.sun.tools.attach.spi.AttachProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.jshell/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("jdk.jshell.spi.ExecutionControlProvider"); + expected.add("jdk.internal.editor.spi.BuildInEditorProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.internal.le/module-info.class")) { + final List<String> expected = new ArrayList<>(); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.jlink/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("jdk.tools.jlink.plugin.Plugin"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.internal.jvmstat/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("sun.jvmstat.monitor.MonitoredHostService"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.jpackage/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("jdk.jpackage.internal.Bundler"); + expected.add("jdk.jpackage.internal.Bundlers"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.naming.ldap/module-info.class")) { + final List<String> expected = new ArrayList<>(); + expected.add("com.sun.jndi.ldap.spi.LdapDnsProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/jdk.jsobject/module-info.class") && javaClass.getMajor() == Const.MAJOR_11) { + final List<String> expected = new ArrayList<>(); + expected.add("jdk.internal.netscape.javascript.spi.JSObjectProvider"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else if (url.getPath().contains("/org/assertj/assertj-core/")) { + final List<String> expected = new ArrayList<>(); + expected.add("org.assertj.core.configuration.Configuration"); + expected.add("org.assertj.core.presentation.Representation"); + assertEquals(expected, Arrays.asList(usedClassNames)); + } else { + assertEquals(0, usedClassNames.length, "Found " + Arrays.toString(usedClassNames) + " in " + url.getPath()); + } + super.visitModule(obj); + } + + @Override + public void visitModuleExports(final ModuleExports obj) { + assertEquals(0, obj.getExportsFlags(), url.toString()); + final String packageName = obj.getPackageName(constantPool); + final String[] toModuleNames = obj.getToModuleNames(constantPool); + if (url.getPath().contains("junit-platform-commons")) { + final List<String> expected = new ArrayList<>(); + expected.add("org.junit.jupiter.api"); + expected.add("org.junit.jupiter.engine"); + expected.add("org.junit.jupiter.migrationsupport"); + expected.add("org.junit.jupiter.params"); + expected.add("org.junit.platform.console"); + expected.add("org.junit.platform.engine"); + expected.add("org.junit.platform.launcher"); + expected.add("org.junit.platform.reporting"); + expected.add("org.junit.platform.runner"); + expected.add("org.junit.platform.suite.api"); + switch (packageName) { + case "org.junit.platform.commons.util": + expected.add("org.junit.platform.suite.commons"); + expected.add("org.junit.platform.suite.engine"); + expected.add("org.junit.platform.testkit"); + expected.add("org.junit.vintage.engine"); + assertEquals(expected, Arrays.asList(toModuleNames)); + break; + case "org.junit.platform.commons.logging": + expected.add("org.junit.platform.suite.engine"); + expected.add("org.junit.platform.testkit"); + expected.add("org.junit.vintage.engine"); + assertEquals(expected, Arrays.asList(toModuleNames)); + break; + default: + assertEquals(0, toModuleNames.length); + break; + } + } + super.visitModuleExports(obj); + } + + @Override + public void visitModuleProvides(final ModuleProvides obj) { + final String interfaceName = obj.getInterfaceName(constantPool); + final String[] implementationClassNames = obj.getImplementationClassNames(constantPool, true); + if (url.getPath().contains("junit-jupiter-engine")) { + assertEquals("org.junit.platform.engine.TestEngine", interfaceName); + assertEquals(1, implementationClassNames.length); + assertEquals("org.junit.jupiter.engine.JupiterTestEngine", implementationClassNames[0]); + } else if (url.getPath().contains("junit-platform-launcher")) { + assertEquals("org.junit.platform.launcher.TestExecutionListener", interfaceName); + assertEquals(1, implementationClassNames.length); + assertEquals("org.junit.platform.launcher.listeners.UniqueIdTrackingListener", implementationClassNames[0]); + } + super.visitModuleProvides(obj); + } + + @Override + public void visitModuleOpens(final ModuleOpens obj) { + assertEquals(0, obj.getOpensFlags(), url.toString()); + final String packageName = obj.getPackageName(constantPool); + final String[] toModuleNames = obj.getToModuleNames(constantPool); + if (url.getPath().contains("junit-jupiter-engine")) { + assertEquals("org.junit.jupiter.engine.extension", packageName); + assertEquals(1, toModuleNames.length); + assertEquals("org.junit.platform.commons", toModuleNames[0]); + } + if (url.getPath().contains("junit-jupiter-api")) { + assertEquals("org.junit.jupiter.api.condition", packageName); + assertEquals(1, toModuleNames.length); + assertEquals("org.junit.platform.commons", toModuleNames[0]); + } + super.visitModuleOpens(obj); + } + + @Override + public void visitModuleRequires(final ModuleRequires obj) { + if (url.getPath().contains("junit-jupiter-engine")) { + final String moduleName = obj.getModuleName(constantPool); + final Map<String, Integer> expected = new HashMap<>(); + expected.put("java.base", 32768); + expected.put("org.apiguardian.api", 64); + expected.put("org.junit.jupiter.api", 0); + expected.put("org.junit.platform.commons", 0); + expected.put("org.junit.platform.engine", 0); + expected.put("org.opentest4j", 0); + assertTrue(expected.containsKey(moduleName)); + assertEquals(expected.get(moduleName), obj.getRequiresFlags(), moduleName); + } + super.visitModuleRequires(obj); + } + }; + javaClass.accept(new DescendingVisitor(javaClass, visitor)); + } + } + } +} diff --git a/src/test/java/org/apache/bcel/data/EmptyClass.java b/src/test/java/org/apache/bcel/data/EmptyClass.java new file mode 100644 index 00000000..b10c1d03 --- /dev/null +++ b/src/test/java/org/apache/bcel/data/EmptyClass.java @@ -0,0 +1,22 @@ +/* + * 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 + * + * http://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.data; + +public class EmptyClass { +} + diff --git a/src/test/java/org/apache/bcel/generic/FieldAnnotationsTestCase.java b/src/test/java/org/apache/bcel/generic/FieldAnnotationsTestCase.java index 31d66f3b..5ba8db0f 100644 --- a/src/test/java/org/apache/bcel/generic/FieldAnnotationsTestCase.java +++ b/src/test/java/org/apache/bcel/generic/FieldAnnotationsTestCase.java @@ -17,13 +17,8 @@ package org.apache.bcel.generic; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.io.IOException; - import org.apache.bcel.AbstractTestCase; +import org.apache.bcel.Const; import org.apache.bcel.classfile.AnnotationEntry; import org.apache.bcel.classfile.ElementValuePair; import org.apache.bcel.classfile.Field; @@ -31,6 +26,14 @@ import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.util.SyntheticRepository; import org.junit.jupiter.api.Test; +import java.io.File; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class FieldAnnotationsTestCase extends AbstractTestCase { // helper methods public void checkAnnotatedField(final JavaClass clazz, final String fieldname, final String AnnotationEntryName, final String AnnotationEntryElementName, @@ -40,6 +43,8 @@ public class FieldAnnotationsTestCase extends AbstractTestCase { final AnnotationEntry[] fieldAnnotationEntrys = f.getAnnotationEntries(); if (f.getName().equals(fieldname)) { checkAnnotationEntry(fieldAnnotationEntrys[0], AnnotationEntryName, AnnotationEntryElementName, AnnotationEntryElementValue); + assertNotNull(f.getAttribute(Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS)); + assertNull(f.getAttribute(Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS)); } } } diff --git a/src/test/java/org/apache/bcel/generic/GeneratingAnnotatedClassesTestCase.java b/src/test/java/org/apache/bcel/generic/GeneratingAnnotatedClassesTestCase.java index 056453c1..c2924d34 100644 --- a/src/test/java/org/apache/bcel/generic/GeneratingAnnotatedClassesTestCase.java +++ b/src/test/java/org/apache/bcel/generic/GeneratingAnnotatedClassesTestCase.java @@ -17,6 +17,8 @@ package org.apache.bcel.generic; import static org.junit.jupiter.api.Assertions.assertEquals; +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 static org.junit.jupiter.api.Assertions.fail; @@ -89,6 +91,8 @@ public class GeneratingAnnotatedClassesTestCase extends AbstractTestCase { assertSimpleElementValue(annos[0]); } } + assertNotNull(method.getAttribute(Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS)); + assertNull(method.getAttribute(Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS)); } private void assertSimpleElementValue(final AnnotationEntry anno) {