diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java index 9bb9e92382..0bab02efa0 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java @@ -68,9 +68,17 @@ public AnnotationVisitor(final int api) { * calls. May be {@literal null}. */ public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) { - if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) { + if (api != Opcodes.ASM8 + && api != Opcodes.ASM7 + && api != Opcodes.ASM6 + && api != Opcodes.ASM5 + && api != Opcodes.ASM4 + && api != Opcodes.ASM9_EXPERIMENTAL) { throw new IllegalArgumentException("Unsupported api " + api); } + if (api == Opcodes.ASM9_EXPERIMENTAL) { + Constants.checkAsmExperimental(this); + } this.api = api; this.av = annotationVisitor; } diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java index 79a13fcead..69beb1b28b 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationWriter.java @@ -112,7 +112,7 @@ final class AnnotationWriter extends AnnotationVisitor { final boolean useNamedValues, final ByteVector annotation, final AnnotationWriter previousAnnotation) { - super(Opcodes.ASM7); + super(/* latest api = */ Opcodes.ASM8); this.symbolTable = symbolTable; this.useNamedValues = useNamedValues; this.annotation = annotation; diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java index 84ec8c5592..66181aeb97 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java @@ -30,7 +30,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.logging.Logger; /** * A parser to make a {@link ClassVisitor} visit a ClassFile structure, as defined in the Java @@ -44,8 +43,6 @@ */ public class ClassReader { - private static final Logger LOGGER = Logger.getLogger(ClassReader.class.getName()); - /** * A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed * nor visited. @@ -53,10 +50,11 @@ public class ClassReader { public static final int SKIP_CODE = 1; /** - * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, LocalVariableTypeTable - * and LineNumberTable attributes. If this flag is set these attributes are neither parsed nor - * visited (i.e. {@link ClassVisitor#visitSource}, {@link MethodVisitor#visitLocalVariable} and - * {@link MethodVisitor#visitLineNumber} are not called). + * A flag to skip the SourceFile, SourceDebugExtension, LocalVariableTable, + * LocalVariableTypeTable, LineNumberTable and MethodParameters attributes. If this flag is set + * these attributes are neither parsed nor visited (i.e. {@link ClassVisitor#visitSource}, {@link + * MethodVisitor#visitLocalVariable}, {@link MethodVisitor#visitLineNumber} and {@link + * MethodVisitor#visitParameter} are not called). */ public static final int SKIP_DEBUG = 2; @@ -193,9 +191,6 @@ public ClassReader( this.b = classFileBuffer; // Check the class' major_version. This field is after the magic and minor_version fields, which // use 4 and 2 bytes respectively. - if (checkClassVersion && readShort(classFileOffset + 6) == Opcodes.V15) { - LOGGER.warning("Unsupported class file major version " + readShort(classFileOffset + 6)); - } if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V15) { throw new IllegalArgumentException( "Unsupported class file major version " + readShort(classFileOffset + 6)); @@ -472,6 +467,10 @@ public void accept( String nestHostClass = null; // - The offset of the NestMembers attribute, or 0. int nestMembersOffset = 0; + // - The offset of the PermittedSubtypes attribute, or 0 + int permittedSubtypesOffset = 0; + // - The offset of the Record attribute, or 0. + int recordOffset = 0; // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). // This list in the reverse order or their order in the ClassFile structure. Attribute attributes = null; @@ -494,6 +493,8 @@ public void accept( nestHostClass = readClass(currentAttributeOffset, charBuffer); } else if (Constants.NEST_MEMBERS.equals(attributeName)) { nestMembersOffset = currentAttributeOffset; + } else if (Constants.PERMITTED_SUBTYPES.equals(attributeName)) { + permittedSubtypesOffset = currentAttributeOffset; } else if (Constants.SIGNATURE.equals(attributeName)) { signature = readUTF8(currentAttributeOffset, charBuffer); } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) { @@ -511,6 +512,8 @@ public void accept( runtimeInvisibleAnnotationsOffset = currentAttributeOffset; } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { runtimeInvisibleTypeAnnotationsOffset = currentAttributeOffset; + } else if (Constants.RECORD.equals(attributeName)) { + recordOffset = currentAttributeOffset; } else if (Constants.MODULE.equals(attributeName)) { moduleOffset = currentAttributeOffset; } else if (Constants.MODULE_MAIN_CLASS.equals(attributeName)) { @@ -668,6 +671,17 @@ public void accept( } } + // Visit the PermittedSubtypes attribute. + if (permittedSubtypesOffset != 0) { + int numberOfPermittedSubtypes = readUnsignedShort(permittedSubtypesOffset); + int currentPermittedSubtypeOffset = permittedSubtypesOffset + 2; + while (numberOfPermittedSubtypes-- > 0) { + classVisitor.visitPermittedSubtypeExperimental( + readClass(currentPermittedSubtypeOffset, charBuffer)); + currentPermittedSubtypeOffset += 2; + } + } + // Visit the InnerClasses attribute. if (innerClassesOffset != 0) { int numberOfClasses = readUnsignedShort(innerClassesOffset); @@ -682,6 +696,15 @@ public void accept( } } + // Visit Record components. + if (recordOffset != 0) { + int recordComponentsCount = readUnsignedShort(recordOffset); + recordOffset += 2; + while (recordComponentsCount-- > 0) { + recordOffset = readRecordComponent(classVisitor, context, recordOffset); + } + } + // Visit the fields and methods. int fieldsCount = readUnsignedShort(currentOffset); currentOffset += 2; @@ -829,6 +852,180 @@ private void readModuleAttributes( moduleVisitor.visitEnd(); } + /** + * Reads a record component and visit it. + * + * @param classVisitor the current class visitor + * @param context information about the class being parsed. + * @param recordComponentOffset the offset of the current record component. + * @return the offset of the first byte following the record component. + */ + private int readRecordComponent( + final ClassVisitor classVisitor, final Context context, final int recordComponentOffset) { + char[] charBuffer = context.charBuffer; + + int currentOffset = recordComponentOffset; + String name = readUTF8(currentOffset, charBuffer); + String descriptor = readUTF8(currentOffset + 2, charBuffer); + currentOffset += 4; + + // Read the record component attributes (the variables are ordered as in Section 4.7 of the + // JVMS). + + // Attribute offsets exclude the attribute_name_index and attribute_length fields. + // - The string corresponding to the Signature attribute, or null. + String signature = null; + // - The offset of the RuntimeVisibleAnnotations attribute, or 0. + int runtimeVisibleAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleAnnotations attribute, or 0. + int runtimeInvisibleAnnotationsOffset = 0; + // - The offset of the RuntimeVisibleTypeAnnotations attribute, or 0. + int runtimeVisibleTypeAnnotationsOffset = 0; + // - The offset of the RuntimeInvisibleTypeAnnotations attribute, or 0. + int runtimeInvisibleTypeAnnotationsOffset = 0; + // - The non standard attributes (linked with their {@link Attribute#nextAttribute} field). + // This list in the reverse order or their order in the ClassFile structure. + Attribute attributes = null; + + int attributesCount = readUnsignedShort(currentOffset); + currentOffset += 2; + while (attributesCount-- > 0) { + // Read the attribute_info's attribute_name and attribute_length fields. + String attributeName = readUTF8(currentOffset, charBuffer); + int attributeLength = readInt(currentOffset + 2); + currentOffset += 6; + // The tests are sorted in decreasing frequency order (based on frequencies observed on + // typical classes). + if (Constants.SIGNATURE.equals(attributeName)) { + signature = readUTF8(currentOffset, charBuffer); + } else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeVisibleTypeAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleAnnotationsOffset = currentOffset; + } else if (Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS.equals(attributeName)) { + runtimeInvisibleTypeAnnotationsOffset = currentOffset; + } else { + Attribute attribute = + readAttribute( + context.attributePrototypes, + attributeName, + currentOffset, + attributeLength, + charBuffer, + -1, + null); + attribute.nextAttribute = attributes; + attributes = attribute; + } + currentOffset += attributeLength; + } + + RecordComponentVisitor recordComponentVisitor = + classVisitor.visitRecordComponent(name, descriptor, signature); + if (recordComponentVisitor == null) { + return currentOffset; + } + + // Visit the RuntimeVisibleAnnotations attribute. + if (runtimeVisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleAnnotations attribute. + if (runtimeInvisibleAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + recordComponentVisitor.visitAnnotation(annotationDescriptor, /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeVisibleTypeAnnotations attribute. + if (runtimeVisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeVisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeVisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + recordComponentVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ true), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the RuntimeInvisibleTypeAnnotations attribute. + if (runtimeInvisibleTypeAnnotationsOffset != 0) { + int numAnnotations = readUnsignedShort(runtimeInvisibleTypeAnnotationsOffset); + int currentAnnotationOffset = runtimeInvisibleTypeAnnotationsOffset + 2; + while (numAnnotations-- > 0) { + // Parse the target_type, target_info and target_path fields. + currentAnnotationOffset = readTypeAnnotationTarget(context, currentAnnotationOffset); + // Parse the type_index field. + String annotationDescriptor = readUTF8(currentAnnotationOffset, charBuffer); + currentAnnotationOffset += 2; + // Parse num_element_value_pairs and element_value_pairs and visit these values. + currentAnnotationOffset = + readElementValues( + recordComponentVisitor.visitTypeAnnotation( + context.currentTypeAnnotationTarget, + context.currentTypeAnnotationTargetPath, + annotationDescriptor, + /* visible = */ false), + currentAnnotationOffset, + /* named = */ true, + charBuffer); + } + } + + // Visit the non standard attributes. + while (attributes != null) { + // Copy and reset the nextAttribute field so that it can also be used in FieldWriter. + Attribute nextAttribute = attributes.nextAttribute; + attributes.nextAttribute = null; + recordComponentVisitor.visitAttribute(attributes); + attributes = nextAttribute; + } + + // Visit the end of the field. + recordComponentVisitor.visitEnd(); + return currentOffset; + } + /** * Reads a JVMS field_info structure and makes the given visitor visit it. * @@ -1155,7 +1352,7 @@ private int readMethod( } // Visit the MethodParameters attribute. - if (methodParametersOffset != 0) { + if (methodParametersOffset != 0 && (context.parsingOptions & SKIP_DEBUG) == 0) { int parametersCount = readByte(methodParametersOffset); int currentParameterOffset = methodParametersOffset + 1; while (parametersCount-- > 0) { @@ -1327,113 +1524,113 @@ private void readCode( final int bytecodeOffset = currentOffset - bytecodeStartOffset; final int opcode = classBuffer[currentOffset] & 0xFF; switch (opcode) { - case Constants.NOP: - case Constants.ACONST_NULL: - case Constants.ICONST_M1: - case Constants.ICONST_0: - case Constants.ICONST_1: - case Constants.ICONST_2: - case Constants.ICONST_3: - case Constants.ICONST_4: - case Constants.ICONST_5: - case Constants.LCONST_0: - case Constants.LCONST_1: - case Constants.FCONST_0: - case Constants.FCONST_1: - case Constants.FCONST_2: - case Constants.DCONST_0: - case Constants.DCONST_1: - case Constants.IALOAD: - case Constants.LALOAD: - case Constants.FALOAD: - case Constants.DALOAD: - case Constants.AALOAD: - case Constants.BALOAD: - case Constants.CALOAD: - case Constants.SALOAD: - case Constants.IASTORE: - case Constants.LASTORE: - case Constants.FASTORE: - case Constants.DASTORE: - case Constants.AASTORE: - case Constants.BASTORE: - case Constants.CASTORE: - case Constants.SASTORE: - case Constants.POP: - case Constants.POP2: - case Constants.DUP: - case Constants.DUP_X1: - case Constants.DUP_X2: - case Constants.DUP2: - case Constants.DUP2_X1: - case Constants.DUP2_X2: - case Constants.SWAP: - case Constants.IADD: - case Constants.LADD: - case Constants.FADD: - case Constants.DADD: - case Constants.ISUB: - case Constants.LSUB: - case Constants.FSUB: - case Constants.DSUB: - case Constants.IMUL: - case Constants.LMUL: - case Constants.FMUL: - case Constants.DMUL: - case Constants.IDIV: - case Constants.LDIV: - case Constants.FDIV: - case Constants.DDIV: - case Constants.IREM: - case Constants.LREM: - case Constants.FREM: - case Constants.DREM: - case Constants.INEG: - case Constants.LNEG: - case Constants.FNEG: - case Constants.DNEG: - case Constants.ISHL: - case Constants.LSHL: - case Constants.ISHR: - case Constants.LSHR: - case Constants.IUSHR: - case Constants.LUSHR: - case Constants.IAND: - case Constants.LAND: - case Constants.IOR: - case Constants.LOR: - case Constants.IXOR: - case Constants.LXOR: - case Constants.I2L: - case Constants.I2F: - case Constants.I2D: - case Constants.L2I: - case Constants.L2F: - case Constants.L2D: - case Constants.F2I: - case Constants.F2L: - case Constants.F2D: - case Constants.D2I: - case Constants.D2L: - case Constants.D2F: - case Constants.I2B: - case Constants.I2C: - case Constants.I2S: - case Constants.LCMP: - case Constants.FCMPL: - case Constants.FCMPG: - case Constants.DCMPL: - case Constants.DCMPG: - case Constants.IRETURN: - case Constants.LRETURN: - case Constants.FRETURN: - case Constants.DRETURN: - case Constants.ARETURN: - case Constants.RETURN: - case Constants.ARRAYLENGTH: - case Constants.ATHROW: - case Constants.MONITORENTER: - case Constants.MONITOREXIT: + case Opcodes.NOP: + case Opcodes.ACONST_NULL: + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.IALOAD: + case Opcodes.LALOAD: + case Opcodes.FALOAD: + case Opcodes.DALOAD: + case Opcodes.AALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + case Opcodes.IASTORE: + case Opcodes.LASTORE: + case Opcodes.FASTORE: + case Opcodes.DASTORE: + case Opcodes.AASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + case Opcodes.POP: + case Opcodes.POP2: + case Opcodes.DUP: + case Opcodes.DUP_X1: + case Opcodes.DUP_X2: + case Opcodes.DUP2: + case Opcodes.DUP2_X1: + case Opcodes.DUP2_X2: + case Opcodes.SWAP: + case Opcodes.IADD: + case Opcodes.LADD: + case Opcodes.FADD: + case Opcodes.DADD: + case Opcodes.ISUB: + case Opcodes.LSUB: + case Opcodes.FSUB: + case Opcodes.DSUB: + case Opcodes.IMUL: + case Opcodes.LMUL: + case Opcodes.FMUL: + case Opcodes.DMUL: + case Opcodes.IDIV: + case Opcodes.LDIV: + case Opcodes.FDIV: + case Opcodes.DDIV: + case Opcodes.IREM: + case Opcodes.LREM: + case Opcodes.FREM: + case Opcodes.DREM: + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + case Opcodes.ISHL: + case Opcodes.LSHL: + case Opcodes.ISHR: + case Opcodes.LSHR: + case Opcodes.IUSHR: + case Opcodes.LUSHR: + case Opcodes.IAND: + case Opcodes.LAND: + case Opcodes.IOR: + case Opcodes.LOR: + case Opcodes.IXOR: + case Opcodes.LXOR: + case Opcodes.I2L: + case Opcodes.I2F: + case Opcodes.I2D: + case Opcodes.L2I: + case Opcodes.L2F: + case Opcodes.L2D: + case Opcodes.F2I: + case Opcodes.F2L: + case Opcodes.F2D: + case Opcodes.D2I: + case Opcodes.D2L: + case Opcodes.D2F: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + case Opcodes.LCMP: + case Opcodes.FCMPL: + case Opcodes.FCMPG: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + case Opcodes.IRETURN: + case Opcodes.LRETURN: + case Opcodes.FRETURN: + case Opcodes.DRETURN: + case Opcodes.ARETURN: + case Opcodes.RETURN: + case Opcodes.ARRAYLENGTH: + case Opcodes.ATHROW: + case Opcodes.MONITORENTER: + case Opcodes.MONITOREXIT: case Constants.ILOAD_0: case Constants.ILOAD_1: case Constants.ILOAD_2: @@ -1476,24 +1673,24 @@ private void readCode( case Constants.ASTORE_3: currentOffset += 1; break; - case Constants.IFEQ: - case Constants.IFNE: - case Constants.IFLT: - case Constants.IFGE: - case Constants.IFGT: - case Constants.IFLE: - case Constants.IF_ICMPEQ: - case Constants.IF_ICMPNE: - case Constants.IF_ICMPLT: - case Constants.IF_ICMPGE: - case Constants.IF_ICMPGT: - case Constants.IF_ICMPLE: - case Constants.IF_ACMPEQ: - case Constants.IF_ACMPNE: - case Constants.GOTO: - case Constants.JSR: - case Constants.IFNULL: - case Constants.IFNONNULL: + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + case Opcodes.IF_ICMPEQ: + case Opcodes.IF_ICMPNE: + case Opcodes.IF_ICMPLT: + case Opcodes.IF_ICMPGE: + case Opcodes.IF_ICMPGT: + case Opcodes.IF_ICMPLE: + case Opcodes.IF_ACMPEQ: + case Opcodes.IF_ACMPNE: + case Opcodes.GOTO: + case Opcodes.JSR: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: createLabel(bytecodeOffset + readShort(currentOffset + 1), labels); currentOffset += 3; break; @@ -1526,27 +1723,27 @@ private void readCode( break; case Constants.WIDE: switch (classBuffer[currentOffset + 1] & 0xFF) { - case Constants.ILOAD: - case Constants.FLOAD: - case Constants.ALOAD: - case Constants.LLOAD: - case Constants.DLOAD: - case Constants.ISTORE: - case Constants.FSTORE: - case Constants.ASTORE: - case Constants.LSTORE: - case Constants.DSTORE: - case Constants.RET: + case Opcodes.ILOAD: + case Opcodes.FLOAD: + case Opcodes.ALOAD: + case Opcodes.LLOAD: + case Opcodes.DLOAD: + case Opcodes.ISTORE: + case Opcodes.FSTORE: + case Opcodes.ASTORE: + case Opcodes.LSTORE: + case Opcodes.DSTORE: + case Opcodes.RET: currentOffset += 4; break; - case Constants.IINC: + case Opcodes.IINC: currentOffset += 6; break; default: throw new IllegalArgumentException(); } break; - case Constants.TABLESWITCH: + case Opcodes.TABLESWITCH: // Skip 0 to 3 padding bytes. currentOffset += 4 - (bytecodeOffset & 3); // Read the default label and the number of table entries. @@ -1559,7 +1756,7 @@ private void readCode( currentOffset += 4; } break; - case Constants.LOOKUPSWITCH: + case Opcodes.LOOKUPSWITCH: // Skip 0 to 3 padding bytes. currentOffset += 4 - (bytecodeOffset & 3); // Read the default label and the number of switch cases. @@ -1572,44 +1769,44 @@ private void readCode( currentOffset += 8; } break; - case Constants.ILOAD: - case Constants.LLOAD: - case Constants.FLOAD: - case Constants.DLOAD: - case Constants.ALOAD: - case Constants.ISTORE: - case Constants.LSTORE: - case Constants.FSTORE: - case Constants.DSTORE: - case Constants.ASTORE: - case Constants.RET: - case Constants.BIPUSH: - case Constants.NEWARRAY: - case Constants.LDC: + case Opcodes.ILOAD: + case Opcodes.LLOAD: + case Opcodes.FLOAD: + case Opcodes.DLOAD: + case Opcodes.ALOAD: + case Opcodes.ISTORE: + case Opcodes.LSTORE: + case Opcodes.FSTORE: + case Opcodes.DSTORE: + case Opcodes.ASTORE: + case Opcodes.RET: + case Opcodes.BIPUSH: + case Opcodes.NEWARRAY: + case Opcodes.LDC: currentOffset += 2; break; - case Constants.SIPUSH: + case Opcodes.SIPUSH: case Constants.LDC_W: case Constants.LDC2_W: - case Constants.GETSTATIC: - case Constants.PUTSTATIC: - case Constants.GETFIELD: - case Constants.PUTFIELD: - case Constants.INVOKEVIRTUAL: - case Constants.INVOKESPECIAL: - case Constants.INVOKESTATIC: - case Constants.NEW: - case Constants.ANEWARRAY: - case Constants.CHECKCAST: - case Constants.INSTANCEOF: - case Constants.IINC: + case Opcodes.GETSTATIC: + case Opcodes.PUTSTATIC: + case Opcodes.GETFIELD: + case Opcodes.PUTFIELD: + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKESPECIAL: + case Opcodes.INVOKESTATIC: + case Opcodes.NEW: + case Opcodes.ANEWARRAY: + case Opcodes.CHECKCAST: + case Opcodes.INSTANCEOF: + case Opcodes.IINC: currentOffset += 3; break; - case Constants.INVOKEINTERFACE: - case Constants.INVOKEDYNAMIC: + case Opcodes.INVOKEINTERFACE: + case Opcodes.INVOKEDYNAMIC: currentOffset += 5; break; - case Constants.MULTIANEWARRAY: + case Opcodes.MULTIANEWARRAY: currentOffset += 4; break; default: @@ -1876,113 +2073,113 @@ private void readCode( // Visit the instruction at this bytecode offset. int opcode = classBuffer[currentOffset] & 0xFF; switch (opcode) { - case Constants.NOP: - case Constants.ACONST_NULL: - case Constants.ICONST_M1: - case Constants.ICONST_0: - case Constants.ICONST_1: - case Constants.ICONST_2: - case Constants.ICONST_3: - case Constants.ICONST_4: - case Constants.ICONST_5: - case Constants.LCONST_0: - case Constants.LCONST_1: - case Constants.FCONST_0: - case Constants.FCONST_1: - case Constants.FCONST_2: - case Constants.DCONST_0: - case Constants.DCONST_1: - case Constants.IALOAD: - case Constants.LALOAD: - case Constants.FALOAD: - case Constants.DALOAD: - case Constants.AALOAD: - case Constants.BALOAD: - case Constants.CALOAD: - case Constants.SALOAD: - case Constants.IASTORE: - case Constants.LASTORE: - case Constants.FASTORE: - case Constants.DASTORE: - case Constants.AASTORE: - case Constants.BASTORE: - case Constants.CASTORE: - case Constants.SASTORE: - case Constants.POP: - case Constants.POP2: - case Constants.DUP: - case Constants.DUP_X1: - case Constants.DUP_X2: - case Constants.DUP2: - case Constants.DUP2_X1: - case Constants.DUP2_X2: - case Constants.SWAP: - case Constants.IADD: - case Constants.LADD: - case Constants.FADD: - case Constants.DADD: - case Constants.ISUB: - case Constants.LSUB: - case Constants.FSUB: - case Constants.DSUB: - case Constants.IMUL: - case Constants.LMUL: - case Constants.FMUL: - case Constants.DMUL: - case Constants.IDIV: - case Constants.LDIV: - case Constants.FDIV: - case Constants.DDIV: - case Constants.IREM: - case Constants.LREM: - case Constants.FREM: - case Constants.DREM: - case Constants.INEG: - case Constants.LNEG: - case Constants.FNEG: - case Constants.DNEG: - case Constants.ISHL: - case Constants.LSHL: - case Constants.ISHR: - case Constants.LSHR: - case Constants.IUSHR: - case Constants.LUSHR: - case Constants.IAND: - case Constants.LAND: - case Constants.IOR: - case Constants.LOR: - case Constants.IXOR: - case Constants.LXOR: - case Constants.I2L: - case Constants.I2F: - case Constants.I2D: - case Constants.L2I: - case Constants.L2F: - case Constants.L2D: - case Constants.F2I: - case Constants.F2L: - case Constants.F2D: - case Constants.D2I: - case Constants.D2L: - case Constants.D2F: - case Constants.I2B: - case Constants.I2C: - case Constants.I2S: - case Constants.LCMP: - case Constants.FCMPL: - case Constants.FCMPG: - case Constants.DCMPL: - case Constants.DCMPG: - case Constants.IRETURN: - case Constants.LRETURN: - case Constants.FRETURN: - case Constants.DRETURN: - case Constants.ARETURN: - case Constants.RETURN: - case Constants.ARRAYLENGTH: - case Constants.ATHROW: - case Constants.MONITORENTER: - case Constants.MONITOREXIT: + case Opcodes.NOP: + case Opcodes.ACONST_NULL: + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.IALOAD: + case Opcodes.LALOAD: + case Opcodes.FALOAD: + case Opcodes.DALOAD: + case Opcodes.AALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + case Opcodes.IASTORE: + case Opcodes.LASTORE: + case Opcodes.FASTORE: + case Opcodes.DASTORE: + case Opcodes.AASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + case Opcodes.POP: + case Opcodes.POP2: + case Opcodes.DUP: + case Opcodes.DUP_X1: + case Opcodes.DUP_X2: + case Opcodes.DUP2: + case Opcodes.DUP2_X1: + case Opcodes.DUP2_X2: + case Opcodes.SWAP: + case Opcodes.IADD: + case Opcodes.LADD: + case Opcodes.FADD: + case Opcodes.DADD: + case Opcodes.ISUB: + case Opcodes.LSUB: + case Opcodes.FSUB: + case Opcodes.DSUB: + case Opcodes.IMUL: + case Opcodes.LMUL: + case Opcodes.FMUL: + case Opcodes.DMUL: + case Opcodes.IDIV: + case Opcodes.LDIV: + case Opcodes.FDIV: + case Opcodes.DDIV: + case Opcodes.IREM: + case Opcodes.LREM: + case Opcodes.FREM: + case Opcodes.DREM: + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + case Opcodes.ISHL: + case Opcodes.LSHL: + case Opcodes.ISHR: + case Opcodes.LSHR: + case Opcodes.IUSHR: + case Opcodes.LUSHR: + case Opcodes.IAND: + case Opcodes.LAND: + case Opcodes.IOR: + case Opcodes.LOR: + case Opcodes.IXOR: + case Opcodes.LXOR: + case Opcodes.I2L: + case Opcodes.I2F: + case Opcodes.I2D: + case Opcodes.L2I: + case Opcodes.L2F: + case Opcodes.L2D: + case Opcodes.F2I: + case Opcodes.F2L: + case Opcodes.F2D: + case Opcodes.D2I: + case Opcodes.D2L: + case Opcodes.D2F: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + case Opcodes.LCMP: + case Opcodes.FCMPL: + case Opcodes.FCMPG: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + case Opcodes.IRETURN: + case Opcodes.LRETURN: + case Opcodes.FRETURN: + case Opcodes.DRETURN: + case Opcodes.ARETURN: + case Opcodes.RETURN: + case Opcodes.ARRAYLENGTH: + case Opcodes.ATHROW: + case Opcodes.MONITORENTER: + case Opcodes.MONITOREXIT: methodVisitor.visitInsn(opcode); currentOffset += 1; break; @@ -2034,24 +2231,24 @@ private void readCode( methodVisitor.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3); currentOffset += 1; break; - case Constants.IFEQ: - case Constants.IFNE: - case Constants.IFLT: - case Constants.IFGE: - case Constants.IFGT: - case Constants.IFLE: - case Constants.IF_ICMPEQ: - case Constants.IF_ICMPNE: - case Constants.IF_ICMPLT: - case Constants.IF_ICMPGE: - case Constants.IF_ICMPGT: - case Constants.IF_ICMPLE: - case Constants.IF_ACMPEQ: - case Constants.IF_ACMPNE: - case Constants.GOTO: - case Constants.JSR: - case Constants.IFNULL: - case Constants.IFNONNULL: + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + case Opcodes.IF_ICMPEQ: + case Opcodes.IF_ICMPNE: + case Opcodes.IF_ICMPLT: + case Opcodes.IF_ICMPGE: + case Opcodes.IF_ICMPGT: + case Opcodes.IF_ICMPLE: + case Opcodes.IF_ACMPEQ: + case Opcodes.IF_ACMPNE: + case Opcodes.GOTO: + case Opcodes.JSR: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: methodVisitor.visitJumpInsn( opcode, labels[currentBytecodeOffset + readShort(currentOffset + 1)]); currentOffset += 3; @@ -2132,7 +2329,7 @@ private void readCode( currentOffset += 4; } break; - case Constants.TABLESWITCH: + case Opcodes.TABLESWITCH: { // Skip 0 to 3 padding bytes. currentOffset += 4 - (currentBytecodeOffset & 3); @@ -2149,7 +2346,7 @@ private void readCode( methodVisitor.visitTableSwitchInsn(low, high, defaultLabel, table); break; } - case Constants.LOOKUPSWITCH: + case Opcodes.LOOKUPSWITCH: { // Skip 0 to 3 padding bytes. currentOffset += 4 - (currentBytecodeOffset & 3); @@ -2167,30 +2364,30 @@ private void readCode( methodVisitor.visitLookupSwitchInsn(defaultLabel, keys, values); break; } - case Constants.ILOAD: - case Constants.LLOAD: - case Constants.FLOAD: - case Constants.DLOAD: - case Constants.ALOAD: - case Constants.ISTORE: - case Constants.LSTORE: - case Constants.FSTORE: - case Constants.DSTORE: - case Constants.ASTORE: - case Constants.RET: + case Opcodes.ILOAD: + case Opcodes.LLOAD: + case Opcodes.FLOAD: + case Opcodes.DLOAD: + case Opcodes.ALOAD: + case Opcodes.ISTORE: + case Opcodes.LSTORE: + case Opcodes.FSTORE: + case Opcodes.DSTORE: + case Opcodes.ASTORE: + case Opcodes.RET: methodVisitor.visitVarInsn(opcode, classBuffer[currentOffset + 1] & 0xFF); currentOffset += 2; break; - case Constants.BIPUSH: - case Constants.NEWARRAY: + case Opcodes.BIPUSH: + case Opcodes.NEWARRAY: methodVisitor.visitIntInsn(opcode, classBuffer[currentOffset + 1]); currentOffset += 2; break; - case Constants.SIPUSH: + case Opcodes.SIPUSH: methodVisitor.visitIntInsn(opcode, readShort(currentOffset + 1)); currentOffset += 3; break; - case Constants.LDC: + case Opcodes.LDC: methodVisitor.visitLdcInsn(readConst(classBuffer[currentOffset + 1] & 0xFF, charBuffer)); currentOffset += 2; break; @@ -2199,14 +2396,14 @@ private void readCode( methodVisitor.visitLdcInsn(readConst(readUnsignedShort(currentOffset + 1), charBuffer)); currentOffset += 3; break; - case Constants.GETSTATIC: - case Constants.PUTSTATIC: - case Constants.GETFIELD: - case Constants.PUTFIELD: - case Constants.INVOKEVIRTUAL: - case Constants.INVOKESPECIAL: - case Constants.INVOKESTATIC: - case Constants.INVOKEINTERFACE: + case Opcodes.GETSTATIC: + case Opcodes.PUTSTATIC: + case Opcodes.GETFIELD: + case Opcodes.PUTFIELD: + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKESPECIAL: + case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEINTERFACE: { int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)]; int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)]; @@ -2227,7 +2424,7 @@ private void readCode( } break; } - case Constants.INVOKEDYNAMIC: + case Opcodes.INVOKEDYNAMIC: { int cpInfoOffset = cpInfoOffsets[readUnsignedShort(currentOffset + 1)]; int nameAndTypeCpInfoOffset = cpInfoOffsets[readUnsignedShort(cpInfoOffset + 2)]; @@ -2249,19 +2446,19 @@ private void readCode( currentOffset += 5; break; } - case Constants.NEW: - case Constants.ANEWARRAY: - case Constants.CHECKCAST: - case Constants.INSTANCEOF: + case Opcodes.NEW: + case Opcodes.ANEWARRAY: + case Opcodes.CHECKCAST: + case Opcodes.INSTANCEOF: methodVisitor.visitTypeInsn(opcode, readClass(currentOffset + 1, charBuffer)); currentOffset += 3; break; - case Constants.IINC: + case Opcodes.IINC: methodVisitor.visitIincInsn( classBuffer[currentOffset + 1] & 0xFF, classBuffer[currentOffset + 2]); currentOffset += 3; break; - case Constants.MULTIANEWARRAY: + case Opcodes.MULTIANEWARRAY: methodVisitor.visitMultiANewArrayInsn( readClass(currentOffset + 1, charBuffer), classBuffer[currentOffset + 3] & 0xFF); currentOffset += 4; diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java index 7f97d47a9b..3c384de83c 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassVisitor.java @@ -30,9 +30,9 @@ /** * A visitor to visit a Java class. The methods of this class must be called in the following order: * {@code visit} [ {@code visitSource} ] [ {@code visitModule} ][ {@code visitNestHost} ][ {@code - * visitOuterClass} ] ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code - * visitAttribute} )* ( {@code visitNestMember} | {@code visitInnerClass} | {@code visitField} | - * {@code visitMethod} )* {@code visitEnd}. + * visitPermittedSubtype} ][ {@code visitOuterClass} ] ( {@code visitAnnotation} | {@code + * visitTypeAnnotation} | {@code visitAttribute} )* ( {@code visitNestMember} | {@code + * visitInnerClass} | {@code visitField} | {@code visitMethod} )* {@code visitEnd}. * * @author Eric Bruneton */ @@ -61,14 +61,23 @@ public ClassVisitor(final int api) { * Constructs a new {@link ClassVisitor}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link + * Opcodes#ASM8}. * @param classVisitor the class visitor to which this visitor must delegate method calls. May be * null. */ public ClassVisitor(final int api, final ClassVisitor classVisitor) { - if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) { + if (api != Opcodes.ASM8 + && api != Opcodes.ASM7 + && api != Opcodes.ASM6 + && api != Opcodes.ASM5 + && api != Opcodes.ASM4 + && api != Opcodes.ASM9_EXPERIMENTAL) { throw new IllegalArgumentException("Unsupported api " + api); } + if (api == Opcodes.ASM9_EXPERIMENTAL) { + Constants.checkAsmExperimental(this); + } this.api = api; this.cv = classVisitor; } @@ -240,6 +249,24 @@ public void visitNestMember(final String nestMember) { } } + /** + * Experimental, use at your own risk. This method will be renamed when it becomes stable, this + * will break existing code using it. Visits a permitted subtypes. A permitted subtypes is one + * of the allowed subtypes of the current class. + * + * @param permittedSubtype the internal name of a permitted subtype. + * @deprecated this API is experimental. + */ + @Deprecated + public void visitPermittedSubtypeExperimental(final String permittedSubtype) { + if (api != Opcodes.ASM9_EXPERIMENTAL) { + throw new UnsupportedOperationException("This feature requires ASM9_EXPERIMENTAL"); + } + if (cv != null) { + cv.visitPermittedSubtypeExperimental(permittedSubtype); + } + } + /** * Visits information about an inner class. This inner class is not necessarily a member of the * class being visited. @@ -259,6 +286,27 @@ public void visitInnerClass( } } + /** + * Visits a record component of the class. + * + * @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} if the record component + * type does not use generic types. + * @return a visitor to visit this record component annotations and attributes, or {@literal null} + * if this class visitor is not interested in visiting these annotations and attributes. + */ + public RecordComponentVisitor visitRecordComponent( + final String name, final String descriptor, final String signature) { + if (api < Opcodes.ASM8) { + throw new UnsupportedOperationException("This feature requires ASM8"); + } + if (cv != null) { + return cv.visitRecordComponent(name, descriptor, signature); + } + return null; + } + /** * Visits a field of the class. * diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java index 5bdf68c674..3e6f7631d3 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassWriter.java @@ -177,6 +177,26 @@ public class ClassWriter extends ClassVisitor { /** The 'classes' array of the NestMembers attribute, or {@literal null}. */ private ByteVector nestMemberClasses; + /** The number_of_classes field of the PermittedSubtypes attribute, or 0. */ + private int numberOfPermittedSubtypeClasses; + + /** The 'classes' array of the PermittedSubtypes attribute, or {@literal null}. */ + private ByteVector permittedSubtypeClasses; + + /** + * The record components of this class, stored in a linked list of {@link RecordComponentWriter} + * linked via their {@link RecordComponentWriter#delegate} field. This field stores the first + * element of this list. + */ + private RecordComponentWriter firstRecordComponent; + + /** + * The record components of this class, stored in a linked list of {@link RecordComponentWriter} + * linked via their {@link RecordComponentWriter#delegate} field. This field stores the last + * element of this list. + */ + private RecordComponentWriter lastRecordComponent; + /** * The first non standard attribute of this class. The next ones can be accessed with the {@link * Attribute#nextAttribute} field. May be {@literal null}. @@ -234,7 +254,7 @@ public ClassWriter(final int flags) { * maximum stack size nor the stack frames will be computed for these methods. */ public ClassWriter(final ClassReader classReader, final int flags) { - super(Opcodes.ASM7); + super(/* latest api = */ Opcodes.ASM8); symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader); if ((flags & COMPUTE_FRAMES) != 0) { this.compute = MethodWriter.COMPUTE_ALL_FRAMES; @@ -352,6 +372,22 @@ public final void visitNestMember(final String nestMember) { nestMemberClasses.putShort(symbolTable.addConstantClass(nestMember).index); } + /** + * Experimental, use at your own risk. + * + * @param permittedSubtype the internal name of a permitted subtype. + * @deprecated this API is experimental. + */ + @Override + @Deprecated + public final void visitPermittedSubtypeExperimental(final String permittedSubtype) { + if (permittedSubtypeClasses == null) { + permittedSubtypeClasses = new ByteVector(); + } + ++numberOfPermittedSubtypeClasses; + permittedSubtypeClasses.putShort(symbolTable.addConstantClass(permittedSubtype).index); + } + @Override public final void visitInnerClass( final String name, final String outerName, final String innerName, final int access) { @@ -377,6 +413,19 @@ public final void visitInnerClass( // and throw an exception if there is a difference? } + @Override + public final RecordComponentVisitor visitRecordComponent( + final String name, final String descriptor, final String signature) { + RecordComponentWriter recordComponentWriter = + new RecordComponentWriter(symbolTable, name, descriptor, signature); + if (firstRecordComponent == null) { + firstRecordComponent = recordComponentWriter; + } else { + lastRecordComponent.delegate = recordComponentWriter; + } + return lastRecordComponent = recordComponentWriter; + } + @Override public final FieldVisitor visitField( final int access, @@ -447,6 +496,7 @@ public byte[] toByteArray() { size += methodWriter.computeMethodInfoSize(); methodWriter = (MethodWriter) methodWriter.mv; } + // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. int attributesCount = 0; if (innerClasses != null) { @@ -526,6 +576,24 @@ public byte[] toByteArray() { size += 8 + nestMemberClasses.length; symbolTable.addConstantUtf8(Constants.NEST_MEMBERS); } + if (permittedSubtypeClasses != null) { + ++attributesCount; + size += 8 + permittedSubtypeClasses.length; + symbolTable.addConstantUtf8(Constants.PERMITTED_SUBTYPES); + } + int recordComponentCount = 0; + int recordSize = 0; + if (firstRecordComponent != null) { + RecordComponentWriter recordComponentWriter = firstRecordComponent; + while (recordComponentWriter != null) { + ++recordComponentCount; + recordSize += recordComponentWriter.computeRecordComponentInfoSize(); + recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate; + } + ++attributesCount; + size += 8 + recordSize; + symbolTable.addConstantUtf8(Constants.RECORD); + } if (firstAttribute != null) { attributesCount += firstAttribute.getAttributeCount(); size += firstAttribute.computeAttributesSize(symbolTable); @@ -630,6 +698,24 @@ public byte[] toByteArray() { .putShort(numberOfNestMemberClasses) .putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length); } + if (permittedSubtypeClasses != null) { + result + .putShort(symbolTable.addConstantUtf8(Constants.PERMITTED_SUBTYPES)) + .putInt(permittedSubtypeClasses.length + 2) + .putShort(numberOfPermittedSubtypeClasses) + .putByteArray(permittedSubtypeClasses.data, 0, permittedSubtypeClasses.length); + } + if (firstRecordComponent != null) { + result + .putShort(symbolTable.addConstantUtf8(Constants.RECORD)) + .putInt(recordSize + 2) + .putShort(recordComponentCount); + RecordComponentWriter recordComponentWriter = firstRecordComponent; + while (recordComponentWriter != null) { + recordComponentWriter.putRecordComponentInfo(result); + recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate; + } + } if (firstAttribute != null) { firstAttribute.putAttributes(symbolTable, result); } @@ -666,6 +752,10 @@ private byte[] replaceAsmInstructions(final byte[] classFile, final boolean hasF nestHostClassIndex = 0; numberOfNestMemberClasses = 0; nestMemberClasses = null; + numberOfPermittedSubtypeClasses = 0; + permittedSubtypeClasses = null; + firstRecordComponent = null; + lastRecordComponent = null; firstAttribute = null; compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING; new ClassReader(classFile, 0, /* checkClassVersion = */ false) @@ -694,6 +784,11 @@ private Attribute[] getAttributePrototypes() { methodWriter.collectAttributePrototypes(attributePrototypes); methodWriter = (MethodWriter) methodWriter.mv; } + RecordComponentWriter recordComponentWriter = firstRecordComponent; + while (recordComponentWriter != null) { + recordComponentWriter.collectAttributePrototypes(attributePrototypes); + recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate; + } return attributePrototypes.toArray(); } diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Constants.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Constants.java index 6cbbf42e07..89330ef012 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Constants.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Constants.java @@ -26,6 +26,10 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF // THE POSSIBILITY OF SUCH DAMAGE. package jersey.repackaged.org.objectweb.asm; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Pattern; /** * Defines additional JVM opcodes, access flags and constants which are not part of the ASM public @@ -34,7 +38,7 @@ * @see JVMS 6 * @author Eric Bruneton */ -final class Constants implements Opcodes { +final class Constants { // The ClassFile attribute names, in the order they are defined in // https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.7-300. @@ -68,6 +72,8 @@ final class Constants implements Opcodes { static final String MODULE_MAIN_CLASS = "ModuleMainClass"; static final String NEST_HOST = "NestHost"; static final String NEST_MEMBERS = "NestMembers"; + static final String PERMITTED_SUBTYPES = "PermittedSubtypes"; + static final String RECORD = "Record"; // ASM specific access flags. // WARNING: the 16 least significant bits must NOT be used, to avoid conflicts with standard @@ -140,7 +146,7 @@ final class Constants implements Opcodes { // Constants to convert between normal and wide jump instructions. // The delta between the GOTO_W and JSR_W opcodes and GOTO and JUMP. - static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - GOTO; + static final int WIDE_JUMP_OPCODE_DELTA = GOTO_W - Opcodes.GOTO; // Constants to convert JVM opcodes to the equivalent ASM specific opcodes, and vice versa. @@ -153,25 +159,62 @@ final class Constants implements Opcodes { // ASM specific opcodes, used for long forward jump instructions. - static final int ASM_IFEQ = IFEQ + ASM_OPCODE_DELTA; - static final int ASM_IFNE = IFNE + ASM_OPCODE_DELTA; - static final int ASM_IFLT = IFLT + ASM_OPCODE_DELTA; - static final int ASM_IFGE = IFGE + ASM_OPCODE_DELTA; - static final int ASM_IFGT = IFGT + ASM_OPCODE_DELTA; - static final int ASM_IFLE = IFLE + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPEQ = IF_ICMPEQ + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPNE = IF_ICMPNE + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPLT = IF_ICMPLT + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPGE = IF_ICMPGE + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPGT = IF_ICMPGT + ASM_OPCODE_DELTA; - static final int ASM_IF_ICMPLE = IF_ICMPLE + ASM_OPCODE_DELTA; - static final int ASM_IF_ACMPEQ = IF_ACMPEQ + ASM_OPCODE_DELTA; - static final int ASM_IF_ACMPNE = IF_ACMPNE + ASM_OPCODE_DELTA; - static final int ASM_GOTO = GOTO + ASM_OPCODE_DELTA; - static final int ASM_JSR = JSR + ASM_OPCODE_DELTA; - static final int ASM_IFNULL = IFNULL + ASM_IFNULL_OPCODE_DELTA; - static final int ASM_IFNONNULL = IFNONNULL + ASM_IFNULL_OPCODE_DELTA; + static final int ASM_IFEQ = Opcodes.IFEQ + ASM_OPCODE_DELTA; + static final int ASM_IFNE = Opcodes.IFNE + ASM_OPCODE_DELTA; + static final int ASM_IFLT = Opcodes.IFLT + ASM_OPCODE_DELTA; + static final int ASM_IFGE = Opcodes.IFGE + ASM_OPCODE_DELTA; + static final int ASM_IFGT = Opcodes.IFGT + ASM_OPCODE_DELTA; + static final int ASM_IFLE = Opcodes.IFLE + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPEQ = Opcodes.IF_ICMPEQ + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPNE = Opcodes.IF_ICMPNE + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPLT = Opcodes.IF_ICMPLT + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPGE = Opcodes.IF_ICMPGE + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPGT = Opcodes.IF_ICMPGT + ASM_OPCODE_DELTA; + static final int ASM_IF_ICMPLE = Opcodes.IF_ICMPLE + ASM_OPCODE_DELTA; + static final int ASM_IF_ACMPEQ = Opcodes.IF_ACMPEQ + ASM_OPCODE_DELTA; + static final int ASM_IF_ACMPNE = Opcodes.IF_ACMPNE + ASM_OPCODE_DELTA; + static final int ASM_GOTO = Opcodes.GOTO + ASM_OPCODE_DELTA; + static final int ASM_JSR = Opcodes.JSR + ASM_OPCODE_DELTA; + static final int ASM_IFNULL = Opcodes.IFNULL + ASM_IFNULL_OPCODE_DELTA; + static final int ASM_IFNONNULL = Opcodes.IFNONNULL + ASM_IFNULL_OPCODE_DELTA; static final int ASM_GOTO_W = 220; private Constants() {} + + static void checkAsmExperimental(final Object caller) { + Class callerClass = caller.getClass(); + String internalName = callerClass.getName().replace('.', '/'); + if (!isWhitelisted(internalName)) { + checkIsPreview(callerClass.getClassLoader().getResourceAsStream(internalName + ".class")); + } + } + + static boolean isWhitelisted(final String internalName) { + if (!internalName.startsWith("org/objectweb/asm/")) { + return false; + } + String member = "(Annotation|Class|Field|Method|Module|RecordComponent|Signature)"; + return internalName.contains("Test$") + || Pattern.matches( + "org/objectweb/asm/util/Trace" + member + "Visitor(\\$.*)?", internalName) + || Pattern.matches( + "org/objectweb/asm/util/Check" + member + "Adapter(\\$.*)?", internalName); + } + + static void checkIsPreview(final InputStream classInputStream) { + if (classInputStream == null) { + throw new IllegalStateException("Bytecode not available, can't check class version"); + } + int minorVersion; + try (DataInputStream callerClassStream = new DataInputStream(classInputStream); ) { + callerClassStream.readInt(); + minorVersion = callerClassStream.readUnsignedShort(); + } catch (IOException ioe) { + throw new IllegalStateException("I/O error, can't check class version", ioe); + } + if (minorVersion != 0xFFFF) { + throw new IllegalStateException( + "ASM9_EXPERIMENTAL can only be used by classes compiled with --enable-preview"); + } + } } diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java index 64d39d3729..eb531cf840 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldVisitor.java @@ -38,7 +38,8 @@ public abstract class FieldVisitor { /** * The ASM API version implemented by this visitor. The value of this field must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link + * Opcodes#ASM8}. */ protected final int api; @@ -49,7 +50,8 @@ public abstract class FieldVisitor { * Constructs a new {@link FieldVisitor}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link + * Opcodes#ASM8}. */ public FieldVisitor(final int api) { this(api, null); @@ -59,14 +61,23 @@ public FieldVisitor(final int api) { * Constructs a new {@link FieldVisitor}. * * @param api the ASM API version implemented by this visitor. Must be one of {@link - * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6} or {@link Opcodes#ASM7}. + * Opcodes#ASM4}, {@link Opcodes#ASM5}, {@link Opcodes#ASM6}, {@link Opcodes#ASM7} or {@link + * Opcodes#ASM8}. * @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be * null. */ public FieldVisitor(final int api, final FieldVisitor fieldVisitor) { - if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) { + if (api != Opcodes.ASM8 + && api != Opcodes.ASM7 + && api != Opcodes.ASM6 + && api != Opcodes.ASM5 + && api != Opcodes.ASM4 + && api != Opcodes.ASM9_EXPERIMENTAL) { throw new IllegalArgumentException("Unsupported api " + api); } + if (api == Opcodes.ASM9_EXPERIMENTAL) { + Constants.checkAsmExperimental(this); + } this.api = api; this.fv = fieldVisitor; } diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldWriter.java index 93f48020dd..95617fb7a2 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldWriter.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/FieldWriter.java @@ -124,7 +124,7 @@ final class FieldWriter extends FieldVisitor { final String descriptor, final String signature, final Object constantValue) { - super(Opcodes.ASM7); + super(/* latest api = */ Opcodes.ASM8); this.symbolTable = symbolTable; this.accessFlags = access; this.nameIndex = symbolTable.addConstantUtf8(name); diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java index e88b6bf605..b59be69d9d 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodVisitor.java @@ -80,9 +80,17 @@ public MethodVisitor(final int api) { * be null. */ public MethodVisitor(final int api, final MethodVisitor methodVisitor) { - if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) { + if (api != Opcodes.ASM8 + && api != Opcodes.ASM7 + && api != Opcodes.ASM6 + && api != Opcodes.ASM5 + && api != Opcodes.ASM4 + && api != Opcodes.ASM9_EXPERIMENTAL) { throw new IllegalArgumentException("Unsupported api " + api); } + if (api == Opcodes.ASM9_EXPERIMENTAL) { + Constants.checkAsmExperimental(this); + } this.api = api; this.mv = methodVisitor; } @@ -534,7 +542,7 @@ public void visitLdcInsn(final Object value) { || (value instanceof Type && ((Type) value).getSort() == Type.METHOD))) { throw new UnsupportedOperationException(REQUIRES_ASM5); } - if (api != Opcodes.ASM7 && value instanceof ConstantDynamic) { + if (api < Opcodes.ASM7 && value instanceof ConstantDynamic) { throw new UnsupportedOperationException("This feature requires ASM7"); } if (mv != null) { diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java index ecded6684b..9231c564e0 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/MethodWriter.java @@ -592,7 +592,7 @@ final class MethodWriter extends MethodVisitor { final String signature, final String[] exceptions, final int compute) { - super(Opcodes.ASM7); + super(/* latest api = */ Opcodes.ASM8); this.symbolTable = symbolTable; this.accessFlags = "".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access; this.nameIndex = symbolTable.addConstantUtf8(name); diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java index ff5062b0b6..3285b091c6 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleVisitor.java @@ -66,9 +66,17 @@ public ModuleVisitor(final int api) { * be null. */ public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) { - if (api != Opcodes.ASM7 && api != Opcodes.ASM6) { + if (api != Opcodes.ASM8 + && api != Opcodes.ASM7 + && api != Opcodes.ASM6 + && api != Opcodes.ASM5 + && api != Opcodes.ASM4 + && api != Opcodes.ASM9_EXPERIMENTAL) { throw new IllegalArgumentException("Unsupported api " + api); } + if (api == Opcodes.ASM9_EXPERIMENTAL) { + Constants.checkAsmExperimental(this); + } this.api = api; this.mv = moduleVisitor; } diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleWriter.java index 9877ceb5d7..19329059f7 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleWriter.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ModuleWriter.java @@ -94,7 +94,7 @@ final class ModuleWriter extends ModuleVisitor { private int mainClassIndex; ModuleWriter(final SymbolTable symbolTable, final int name, final int access, final int version) { - super(Opcodes.ASM7); + super(/* latest api = */ Opcodes.ASM8); this.symbolTable = symbolTable; this.moduleNameIndex = name; this.moduleFlags = access; diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java index 6a1019fd0a..9e26c7b25b 100644 --- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java @@ -47,6 +47,15 @@ public interface Opcodes { int ASM5 = 5 << 16 | 0 << 8; int ASM6 = 6 << 16 | 0 << 8; int ASM7 = 7 << 16 | 0 << 8; + int ASM8 = 8 << 16 | 0 << 8; + + /** + * Experimental, use at your own risk. This field will be renamed when it becomes stable, this + * will break existing code using it. Only code compiled with --enable-preview can use this. + * + * @deprecated This API is experimental. + */ + @Deprecated int ASM9_EXPERIMENTAL = 1 << 24 | 9 << 16 | 0 << 8; /* * Internal flags used to redirect calls to deprecated methods. For instance, if a visitOldStuff @@ -307,7 +316,7 @@ public interface Opcodes { int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module * int ACC_ANNOTATION = 0x2000; // class int ACC_ENUM = 0x4000; // class(?) field inner - int ACC_MANDATED = 0x8000; // parameter, module, module * + int ACC_MANDATED = 0x8000; // field, method, parameter, module, module * int ACC_MODULE = 0x8000; // class // ASM specific access flags. diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentVisitor.java new file mode 100644 index 0000000000..f361d2f70c --- /dev/null +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentVisitor.java @@ -0,0 +1,150 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package jersey.repackaged.org.objectweb.asm; + +/** + * A visitor to visit a record component. The methods of this class must be called in the following + * order: ( {@code visitAnnotation} | {@code visitTypeAnnotation} | {@code visitAttribute} )* {@code + * visitEnd}. + * + * @author Remi Forax + * @author Eric Bruneton + */ +public abstract class RecordComponentVisitor { + /** + * The ASM API version implemented by this visitor. The value of this field must be {@link + * Opcodes#ASM8}. + */ + protected final int api; + + /** + * The record visitor to which this visitor must delegate method calls. May be {@literal null}. + */ + /*package-private*/ RecordComponentVisitor delegate; + + /** + * Constructs a new {@link RecordComponentVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM8}. + */ + public RecordComponentVisitor(final int api) { + this(api, null); + } + + /** + * Constructs a new {@link RecordComponentVisitor}. + * + * @param api the ASM API version implemented by this visitor. Must be {@link Opcodes#ASM8}. + * @param recordComponentVisitor the record component visitor to which this visitor must delegate + * method calls. May be null. + */ + public RecordComponentVisitor( + final int api, final RecordComponentVisitor recordComponentVisitor) { + if (api != Opcodes.ASM8 + && api != Opcodes.ASM7 + && api != Opcodes.ASM6 + && api != Opcodes.ASM5 + && api != Opcodes.ASM4 + && api != Opcodes.ASM9_EXPERIMENTAL) { + throw new IllegalArgumentException("Unsupported api " + api); + } + if (api == Opcodes.ASM9_EXPERIMENTAL) { + Constants.checkAsmExperimental(this); + } + this.api = api; + this.delegate = recordComponentVisitor; + } + + /** + * The record visitor to which this visitor must delegate method calls. May be {@literal null}. + * + * @return the record visitor to which this visitor must delegate method calls or {@literal null}. + */ + public RecordComponentVisitor getDelegate() { + return delegate; + } + + /** + * Visits an annotation of the record component. + * + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { + if (delegate != null) { + return delegate.visitAnnotation(descriptor, visible); + } + return null; + } + + /** + * Visits an annotation on a type in the record component signature. + * + * @param typeRef a reference to the annotated type. The sort of this type reference must be + * {@link TypeReference#CLASS_TYPE_PARAMETER}, {@link + * TypeReference#CLASS_TYPE_PARAMETER_BOUND} or {@link TypeReference#CLASS_EXTENDS}. See + * {@link TypeReference}. + * @param typePath the path to the annotated type argument, wildcard bound, array element type, or + * static inner type within 'typeRef'. May be {@literal null} if the annotation targets + * 'typeRef' as a whole. + * @param descriptor the class descriptor of the annotation class. + * @param visible {@literal true} if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or {@literal null} if this visitor is not + * interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation( + final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { + if (delegate != null) { + return delegate.visitTypeAnnotation(typeRef, typePath, descriptor, visible); + } + return null; + } + + /** + * Visits a non standard attribute of the record component. + * + * @param attribute an attribute. + */ + public void visitAttribute(final Attribute attribute) { + if (delegate != null) { + delegate.visitAttribute(attribute); + } + } + + /** + * Visits the end of the record component. This method, which is the last one to be called, is + * used to inform the visitor that everything have been visited. + */ + public void visitEnd() { + if (delegate != null) { + delegate.visitEnd(); + } + } +} diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentWriter.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentWriter.java new file mode 100644 index 0000000000..8248170161 --- /dev/null +++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/RecordComponentWriter.java @@ -0,0 +1,225 @@ +// ASM: a very small and fast Java bytecode manipulation framework +// Copyright (c) 2000-2011 INRIA, France Telecom +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holders nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE. +package jersey.repackaged.org.objectweb.asm; + +final class RecordComponentWriter extends RecordComponentVisitor { + /** Where the constants used in this RecordComponentWriter must be stored. */ + private final SymbolTable symbolTable; + + // Note: fields are ordered as in the record_component_info structure, and those related to + // attributes are ordered as in Section 4.7 of the JVMS. + + /** The name_index field of the Record attribute. */ + private final int nameIndex; + + /** The descriptor_index field of the the Record attribute. */ + private final int descriptorIndex; + + /** + * The signature_index field of the Signature attribute of this record component, or 0 if there is + * no Signature attribute. + */ + private int signatureIndex; + + /** + * The last runtime visible annotation of this record component. The previous ones can be accessed + * with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleAnnotation; + + /** + * The last runtime invisible annotation of this record component. The previous ones can be + * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleAnnotation; + + /** + * The last runtime visible type annotation of this record component. The previous ones can be + * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeVisibleTypeAnnotation; + + /** + * The last runtime invisible type annotation of this record component. The previous ones can be + * accessed with the {@link AnnotationWriter#previousAnnotation} field. May be {@literal null}. + */ + private AnnotationWriter lastRuntimeInvisibleTypeAnnotation; + + /** + * The first non standard attribute of this record component. The next ones can be accessed with + * the {@link Attribute#nextAttribute} field. May be {@literal null}. + * + *

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 @@ ${project.version} - 7.2 + 8.0 2.3.6 1.1 3.3.2 diff --git a/tests/integration/asm/src/test/java/org/glassfish/jersey/integration/asm/AnnotatedClassVisitorTest.java b/tests/integration/asm/src/test/java/org/glassfish/jersey/integration/asm/AnnotatedClassVisitorTest.java index 708684d841..54dbdc6a91 100644 --- a/tests/integration/asm/src/test/java/org/glassfish/jersey/integration/asm/AnnotatedClassVisitorTest.java +++ b/tests/integration/asm/src/test/java/org/glassfish/jersey/integration/asm/AnnotatedClassVisitorTest.java @@ -16,11 +16,17 @@ package org.glassfish.jersey.integration.asm; import jersey.repackaged.org.objectweb.asm.ClassVisitor; +import jersey.repackaged.org.objectweb.asm.Opcodes; import org.glassfish.jersey.server.internal.scanning.AnnotationAcceptingListener; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @@ -75,4 +81,97 @@ public void testInheritedMethodsFromClassVisitor() { } Assert.assertThat(containsAllMethods, Matchers.is(true)); } + + @Test + public void testCorrectOpcodeAsmIsUsedInAnnotationAcceptingListener() { + final int asmOpcode = getMaxValueOfField("ASM", 6); + final AnnotationAcceptingListener aal = new AnnotationAcceptingListener(); + + String aalOpcode = null; + try { + final Field classVisitorField = aal.getClass().getDeclaredField("classVisitor"); + classVisitorField.setAccessible(true); + final Object classVisitor = classVisitorField.get(aal); + final Field opcodeField = classVisitor.getClass().getSuperclass().getDeclaredField("api"); + opcodeField.setAccessible(true); + aalOpcode = String.valueOf(((Integer) opcodeField.get(classVisitor)) >> 16); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + + Assert.assertEquals( + "You need to set: \nAnnotatedClassVisitor() {\n super(Opcodes.ASM" + asmOpcode + ");\n}", + String.valueOf(asmOpcode), aalOpcode + ); + } + + @Test + public void testWarningOpcodeInClassReaderWrapperSetCorrectly() { + final Integer jdkVersion = getMaxValueOfField("V", 13); + + Class classReaderWrapper = null; + for (Class innerClass : AnnotationAcceptingListener.class.getDeclaredClasses()) { + if (innerClass.getName().contains("ClassReaderWrapper")) { + classReaderWrapper = innerClass; + break; + } + } + + Integer warnFieldValue = 0; + try { + final Field warnField = classReaderWrapper.getDeclaredField("WARN_VERSION"); + warnField.setAccessible(true); + warnFieldValue = (Integer) warnField.get(null) - (Opcodes.V1_1 & 0x00FF) + 1; + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + + Assert.assertEquals( + "You need to set ClassReaderWrapper.WARN_VERSION=Opcodes.V" + jdkVersion, + jdkVersion, warnFieldValue + ); + } + + @Test + public void testLoggerInClassReaderWrapper() throws IOException { + final String warningMsg = "Unsupported class file major version"; + + final Integer maxOpcode = getMaxValueOfField("V", 13); + final byte[] array = new byte[10]; + array[7] = (byte) ((maxOpcode.byteValue() + Opcodes.V1_1) & 0x00FF); + + final ByteArrayOutputStream log = new ByteArrayOutputStream(500); + final PrintStream saveErr = System.err; + + try { + System.setErr(new PrintStream(log)); + try { + new AnnotationAcceptingListener().process("", new ByteArrayInputStream(array)); + } catch (ArrayIndexOutOfBoundsException aioobe) { + //expected, given array is too small for a class file + } + } finally { + System.setErr(saveErr); + } + + final String message = new String(log.toByteArray()); + Assert.assertTrue( + "The WARNING `" + warningMsg + "` has not been printed for a class with byte code version " + array[7], + message.contains(warningMsg) + ); + } + + private static int getMaxValueOfField(String fieldPrefix, int initialValue) { + int value = initialValue; + do { + try { + value++; + Field field = Opcodes.class.getField(fieldPrefix + value); + } catch (NoSuchFieldException e) { + value--; + break; + } + } while (true); + return value; + } }