WARNING: this list stores the attributes in the reverse order of their visit.
+ * firstAttribute is actually the last attribute visited in {@link #visitAttribute(Attribute)}.
+ * The {@link #putRecordComponentInfo(ByteVector)} method writes the attributes in the order
+ * defined by this list, i.e. in the reverse order specified by the user.
+ */
+ private Attribute firstAttribute;
+
+ /**
+ * Constructs a new {@link RecordComponentWriter}.
+ *
+ * @param symbolTable where the constants used in this RecordComponentWriter must be stored.
+ * @param name the record component name.
+ * @param descriptor the record component descriptor (see {@link Type}).
+ * @param signature the record component signature. May be {@literal null}.
+ */
+ RecordComponentWriter(
+ final SymbolTable symbolTable,
+ final String name,
+ final String descriptor,
+ final String signature) {
+ super(/* latest api = */ Opcodes.ASM8);
+ this.symbolTable = symbolTable;
+ this.nameIndex = symbolTable.addConstantUtf8(name);
+ this.descriptorIndex = symbolTable.addConstantUtf8(descriptor);
+ if (signature != null) {
+ this.signatureIndex = symbolTable.addConstantUtf8(signature);
+ }
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Implementation of the FieldVisitor abstract class
+ // -----------------------------------------------------------------------------------------------
+
+ @Override
+ public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
+ if (visible) {
+ return lastRuntimeVisibleAnnotation =
+ AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation);
+ } else {
+ return lastRuntimeInvisibleAnnotation =
+ AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation);
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitTypeAnnotation(
+ final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
+ if (visible) {
+ return lastRuntimeVisibleTypeAnnotation =
+ AnnotationWriter.create(
+ symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation);
+ } else {
+ return lastRuntimeInvisibleTypeAnnotation =
+ AnnotationWriter.create(
+ symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation);
+ }
+ }
+
+ @Override
+ public void visitAttribute(final Attribute attribute) {
+ // Store the attributes in the reverse order of their visit by this method.
+ attribute.nextAttribute = firstAttribute;
+ firstAttribute = attribute;
+ }
+
+ @Override
+ public void visitEnd() {
+ // Nothing to do.
+ }
+
+ // -----------------------------------------------------------------------------------------------
+ // Utility methods
+ // -----------------------------------------------------------------------------------------------
+
+ /**
+ * Returns the size of the record component JVMS structure generated by this
+ * RecordComponentWriter. Also adds the names of the attributes of this record component in the
+ * constant pool.
+ *
+ * @return the size in bytes of the record_component_info of the Record attribute.
+ */
+ int computeRecordComponentInfoSize() {
+ // name_index, descriptor_index and attributes_count fields use 6 bytes.
+ int size = 6;
+ size += Attribute.computeAttributesSize(symbolTable, 0, signatureIndex);
+ size +=
+ AnnotationWriter.computeAnnotationsSize(
+ lastRuntimeVisibleAnnotation,
+ lastRuntimeInvisibleAnnotation,
+ lastRuntimeVisibleTypeAnnotation,
+ lastRuntimeInvisibleTypeAnnotation);
+ if (firstAttribute != null) {
+ size += firstAttribute.computeAttributesSize(symbolTable);
+ }
+ return size;
+ }
+
+ /**
+ * Puts the content of the record component generated by this RecordComponentWriter into the given
+ * ByteVector.
+ *
+ * @param output where the record_component_info structure must be put.
+ */
+ void putRecordComponentInfo(final ByteVector output) {
+ output.putShort(nameIndex).putShort(descriptorIndex);
+ // Compute and put the attributes_count field.
+ // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
+ int attributesCount = 0;
+ if (signatureIndex != 0) {
+ ++attributesCount;
+ }
+ if (lastRuntimeVisibleAnnotation != null) {
+ ++attributesCount;
+ }
+ if (lastRuntimeInvisibleAnnotation != null) {
+ ++attributesCount;
+ }
+ if (lastRuntimeVisibleTypeAnnotation != null) {
+ ++attributesCount;
+ }
+ if (lastRuntimeInvisibleTypeAnnotation != null) {
+ ++attributesCount;
+ }
+ if (firstAttribute != null) {
+ attributesCount += firstAttribute.getAttributeCount();
+ }
+ output.putShort(attributesCount);
+ Attribute.putAttributes(symbolTable, 0, signatureIndex, output);
+ AnnotationWriter.putAnnotations(
+ symbolTable,
+ lastRuntimeVisibleAnnotation,
+ lastRuntimeInvisibleAnnotation,
+ lastRuntimeVisibleTypeAnnotation,
+ lastRuntimeInvisibleTypeAnnotation,
+ output);
+ if (firstAttribute != null) {
+ firstAttribute.putAttributes(symbolTable, output);
+ }
+ }
+
+ /**
+ * Collects the attributes of this record component into the given set of attribute prototypes.
+ *
+ * @param attributePrototypes a set of attribute prototypes.
+ */
+ final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
+ attributePrototypes.addAttributes(firstAttribute);
+ }
+}
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java
index 31bf0bfa36..e52af5109a 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/SymbolTable.java
@@ -31,11 +31,11 @@
* The constant pool entries, the BootstrapMethods attribute entries and the (ASM specific) type
* table entries of a class.
*
+ * @author Eric Bruneton
* @see JVMS
* 4.4
* @see JVMS
* 4.7.23
- * @author Eric Bruneton
*/
final class SymbolTable {
@@ -1046,8 +1046,10 @@ Symbol addBootstrapMethod(
// bootstrap methods. We must therefore add the bootstrap method arguments to the constant pool
// and BootstrapMethods attribute first, so that the BootstrapMethods attribute is not modified
// while adding the given bootstrap method to it, in the rest of this method.
- for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
- addConstant(bootstrapMethodArgument);
+ int numBootstrapArguments = bootstrapMethodArguments.length;
+ int[] bootstrapMethodArgumentIndexes = new int[numBootstrapArguments];
+ for (int i = 0; i < numBootstrapArguments; i++) {
+ bootstrapMethodArgumentIndexes[i] = addConstant(bootstrapMethodArguments[i]).index;
}
// Write the bootstrap method in the BootstrapMethods table. This is necessary to be able to
@@ -1062,10 +1064,10 @@ Symbol addBootstrapMethod(
bootstrapMethodHandle.getDesc(),
bootstrapMethodHandle.isInterface())
.index);
- int numBootstrapArguments = bootstrapMethodArguments.length;
+
bootstrapMethodsAttribute.putShort(numBootstrapArguments);
- for (Object bootstrapMethodArgument : bootstrapMethodArguments) {
- bootstrapMethodsAttribute.putShort(addConstant(bootstrapMethodArgument).index);
+ for (int i = 0; i < numBootstrapArguments; i++) {
+ bootstrapMethodsAttribute.putShort(bootstrapMethodArgumentIndexes[i]);
}
// Compute the length and the hash code of the bootstrap method.
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
index d73e36866b..a43e3bad77 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
@@ -16,6 +16,7 @@
package org.glassfish.jersey.server.internal.scanning;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
@@ -24,10 +25,12 @@
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
+import java.util.logging.Logger;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.ext.Provider;
+import jersey.repackaged.org.objectweb.asm.RecordComponentVisitor;
import org.glassfish.jersey.internal.OsgiRegistry;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.server.internal.LocalizationMessages;
@@ -145,7 +148,7 @@ public boolean accept(final String name) {
}
public void process(final String name, final InputStream in) throws IOException {
- new ClassReader(in).accept(classVisitor, 0);
+ new ClassReaderWrapper(in).accept(classVisitor, 0);
}
//
@@ -166,9 +169,10 @@ private final class AnnotatedClassVisitor extends ClassVisitor {
private boolean isAnnotated;
private AnnotatedClassVisitor() {
- super(Opcodes.ASM7);
+ super(Opcodes.ASM8);
}
+ @Override
public void visit(final int version, final int access, final String name,
final String signature, final String superName, final String[] interfaces) {
className = name;
@@ -176,11 +180,13 @@ public void visit(final int version, final int access, final String name,
isAnnotated = false;
}
+ @Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
isAnnotated |= annotations.contains(desc);
return null;
}
+ @Override
public void visitInnerClass(final String name, final String outerName,
final String innerName, final int access) {
// If the name of the class that was visited is equal
@@ -195,6 +201,7 @@ public void visitInnerClass(final String name, final String outerName,
}
}
+ @Override
public void visitEnd() {
if (isScoped && isAnnotated) {
// Correctly scoped and annotated
@@ -203,11 +210,13 @@ public void visitEnd() {
}
}
+ @Override
public void visitOuterClass(final String string, final String string0,
final String string1) {
// Do nothing
}
+ @Override
public FieldVisitor visitField(final int i, final String string,
final String string0, final String string1,
final Object object) {
@@ -215,14 +224,17 @@ public FieldVisitor visitField(final int i, final String string,
return null;
}
+ @Override
public void visitSource(final String string, final String string0) {
// Do nothing
}
+ @Override
public void visitAttribute(final Attribute attribute) {
// Do nothing
}
+ @Override
public MethodVisitor visitMethod(final int i, final String string,
final String string0, final String string1,
final String[] string2) {
@@ -230,19 +242,34 @@ public MethodVisitor visitMethod(final int i, final String string,
return null;
}
+ @Override
public ModuleVisitor visitModule(final String name, final int access, final String version) {
// Do nothing
return null;
}
+ @Override
public void visitNestHost(final String nestHost) {
// do nothing
}
+ @Override
public void visitNestMember(final String nestMember) {
// do nothing
}
+ @Override
+ public void visitPermittedSubtypeExperimental(String permittedSubtype) {
+ // do nothing
+ }
+
+ @Override
+ public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
+ // do nothing
+ return null;
+ }
+
+ @Override
public AnnotationVisitor visitTypeAnnotation(
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
//do nothing
@@ -273,4 +300,80 @@ private Class getClassForName(final String className) {
}
}
+
+ private static class ClassReaderWrapper {
+ private static final Logger LOGGER = Logger.getLogger(ClassReader.class.getName());
+ private static final int WARN_VERSION = Opcodes.V15;
+ private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
+
+ private final byte[] b;
+ private ClassReaderWrapper(InputStream inputStream) throws IOException {
+ this.b = readStream(inputStream);
+ }
+
+ private void accept(final ClassVisitor classVisitor, final int parsingOptions) {
+ final int originalVersion = getMajorVersion(b);
+ if (originalVersion == WARN_VERSION + 1) {
+ // temporarily downgrade version to bypass check in ASM
+ setMajorVersion(WARN_VERSION, b);
+ LOGGER.warning("Unsupported class file major version " + originalVersion);
+ }
+ final ClassReader classReader = new ClassReader(b);
+ setMajorVersion(originalVersion, b);
+ classReader.accept(classVisitor, parsingOptions);
+ }
+
+ /**
+ * Sets major version number in given bytes of class (unsigned two bytes at
+ * offset 6).
+ *
+ * @param majorVersion
+ * major version of bytecode to set
+ * @param b
+ * bytes of class
+ * @see #getMajorVersion(byte[])
+ */
+ private static void setMajorVersion(final int majorVersion, final byte[] b) {
+ b[6] = (byte) (majorVersion >>> 8);
+ b[7] = (byte) majorVersion;
+ }
+
+ /**
+ * Gets major version number from given bytes of class (unsigned two bytes
+ * at offset 6).
+ *
+ * @param b
+ * bytes of class
+ * @return major version of bytecode
+ * @see JVMS 4.1 - The class File Format
+ */
+ private static int getMajorVersion(final byte[] b) {
+ return ((b[6] & 0xFF) << 8) | (b[7] & 0xFF);
+ }
+
+ /**
+ * Reads the given input stream and returns its content as a byte array.
+ *
+ * @param inputStream an input stream.
+ * @return the content of the given input stream.
+ * @throws IOException if a problem occurs during reading.
+ */
+ private static byte[] readStream(final InputStream inputStream) throws IOException {
+ if (inputStream == null) {
+ throw new IOException("Class not found");
+ }
+ try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+ byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
+ int bytesRead;
+ while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
+ outputStream.write(data, 0, bytesRead);
+ }
+ outputStream.flush();
+ return outputStream.toByteArray();
+ } finally {
+ inputStream.close();
+ }
+ }
+ }
}
diff --git a/pom.xml b/pom.xml
index 6710e7d6d7..7c3750b196 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2070,7 +2070,7 @@