* ===================================== - * |.DIM|KIND|FLAG|...............VALUE| + * |...DIM|KIND|.F|...............VALUE| * ===================================== ** *
Output frames can contain abstract types of any kind and with a positive or negative array * dimension (and even unassigned types, represented by 0 - which does not correspond to any valid * abstract type value). Input frames can only contain CONSTANT_KIND, REFERENCE_KIND or - * UNINITIALIZED_KIND abstract types of positive or null array dimension. In all cases the type - * table contains only internal type names (array type descriptors are forbidden - array dimensions - * must be represented through the DIM field). + * UNINITIALIZED_KIND abstract types of positive or {@literal null} array dimension. In all cases + * the type table contains only internal type names (array type descriptors are forbidden - array + * dimensions must be represented through the DIM field). * *
The LONG and DOUBLE types are always represented by using two slots (LONG + TOP or DOUBLE + * TOP), for local variables as well as in the operand stack. This is necessary to be able to @@ -129,18 +129,25 @@ class Frame { private static final int ITEM_ASM_CHAR = 11; private static final int ITEM_ASM_SHORT = 12; + // The size and offset in bits of each field of an abstract type. + + private static final int DIM_SIZE = 6; + private static final int KIND_SIZE = 4; + private static final int FLAGS_SIZE = 2; + private static final int VALUE_SIZE = 32 - DIM_SIZE - KIND_SIZE - FLAGS_SIZE; + + private static final int DIM_SHIFT = KIND_SIZE + FLAGS_SIZE + VALUE_SIZE; + private static final int KIND_SHIFT = FLAGS_SIZE + VALUE_SIZE; + private static final int FLAGS_SHIFT = VALUE_SIZE; + // Bitmasks to get each field of an abstract type. - private static final int DIM_MASK = 0xF0000000; - private static final int KIND_MASK = 0x0F000000; - private static final int FLAGS_MASK = 0x00F00000; - private static final int VALUE_MASK = 0x000FFFFF; + private static final int DIM_MASK = ((1 << DIM_SIZE) - 1) << DIM_SHIFT; + private static final int KIND_MASK = ((1 << KIND_SIZE) - 1) << KIND_SHIFT; + private static final int VALUE_MASK = (1 << VALUE_SIZE) - 1; // Constants to manipulate the DIM field of an abstract type. - /** The number of right shift bits to use to get the array dimensions of an abstract type. */ - private static final int DIM_SHIFT = 28; - /** The constant to be added to an abstract type to get one with one more array dimension. */ private static final int ARRAY_OF = +1 << DIM_SHIFT; @@ -149,11 +156,11 @@ class Frame { // Possible values for the KIND field of an abstract type. - private static final int CONSTANT_KIND = 0x01000000; - private static final int REFERENCE_KIND = 0x02000000; - private static final int UNINITIALIZED_KIND = 0x03000000; - private static final int LOCAL_KIND = 0x04000000; - private static final int STACK_KIND = 0x05000000; + private static final int CONSTANT_KIND = 1 << KIND_SHIFT; + private static final int REFERENCE_KIND = 2 << KIND_SHIFT; + private static final int UNINITIALIZED_KIND = 3 << KIND_SHIFT; + private static final int LOCAL_KIND = 4 << KIND_SHIFT; + private static final int STACK_KIND = 5 << KIND_SHIFT; // Possible flags for the FLAGS field of an abstract type. @@ -162,7 +169,7 @@ class Frame { * concrete type is LONG or DOUBLE, TOP should be used instead (because the value has been * partially overridden with an xSTORE instruction). */ - private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 0x00100000 & FLAGS_MASK; + private static final int TOP_IF_LONG_OR_DOUBLE_FLAG = 1 << FLAGS_SHIFT; // Useful predefined abstract types (all the possible CONSTANT_KIND types). @@ -540,7 +547,8 @@ private void push(final int abstractType) { * @param descriptor a type or method descriptor (in which case its return type is pushed). */ private void push(final SymbolTable symbolTable, final String descriptor) { - int typeDescriptorOffset = descriptor.charAt(0) == '(' ? descriptor.indexOf(')') + 1 : 0; + int typeDescriptorOffset = + descriptor.charAt(0) == '(' ? Type.getReturnTypeOffset(descriptor) : 0; int abstractType = getAbstractTypeFromDescriptor(symbolTable, descriptor, typeDescriptorOffset); if (abstractType != 0) { push(abstractType); @@ -1103,6 +1111,42 @@ void execute( // Frame merging methods, used in the second step of the stack map frame computation algorithm // ----------------------------------------------------------------------------------------------- + /** + * Computes the concrete output type corresponding to a given abstract output type. + * + * @param abstractOutputType an abstract output type. + * @param numStack the size of the input stack, used to resolve abstract output types of + * STACK_KIND kind. + * @return the concrete output type corresponding to 'abstractOutputType'. + */ + private int getConcreteOutputType(final int abstractOutputType, final int numStack) { + int dim = abstractOutputType & DIM_MASK; + int kind = abstractOutputType & KIND_MASK; + if (kind == LOCAL_KIND) { + // By definition, a LOCAL_KIND type designates the concrete type of a local variable at + // the beginning of the basic block corresponding to this frame (which is known when + // this method is called, but was not when the abstract type was computed). + int concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + return concreteOutputType; + } else if (kind == STACK_KIND) { + // By definition, a STACK_KIND type designates the concrete type of a local variable at + // the beginning of the basic block corresponding to this frame (which is known when + // this method is called, but was not when the abstract type was computed). + int concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; + if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 + && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { + concreteOutputType = TOP; + } + return concreteOutputType; + } else { + return abstractOutputType; + } + } + /** * Merges the input frame of the given {@link Frame} with the input and output frames of this * {@link Frame}. Returns {@literal true} if the given frame has been changed by this operation @@ -1137,29 +1181,7 @@ final boolean merge( // value at the beginning of the block. concreteOutputType = inputLocals[i]; } else { - int dim = abstractOutputType & DIM_MASK; - int kind = abstractOutputType & KIND_MASK; - if (kind == LOCAL_KIND) { - // By definition, a LOCAL_KIND type designates the concrete type of a local variable at - // the beginning of the basic block corresponding to this frame (which is known when - // this method is called, but was not when the abstract type was computed). - concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else if (kind == STACK_KIND) { - // By definition, a STACK_KIND type designates the concrete type of a local variable at - // the beginning of the basic block corresponding to this frame (which is known when - // this method is called, but was not when the abstract type was computed). - concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else { - concreteOutputType = abstractOutputType; - } + concreteOutputType = getConcreteOutputType(abstractOutputType, numStack); } } else { // If the local variable has never been assigned in this basic block, it is equal to its @@ -1213,25 +1235,8 @@ final boolean merge( // Then, do this for the stack operands that have pushed in the basic block (this code is the // same as the one above for local variables). for (int i = 0; i < outputStackTop; ++i) { - int concreteOutputType; int abstractOutputType = outputStack[i]; - int dim = abstractOutputType & DIM_MASK; - int kind = abstractOutputType & KIND_MASK; - if (kind == LOCAL_KIND) { - concreteOutputType = dim + inputLocals[abstractOutputType & VALUE_MASK]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else if (kind == STACK_KIND) { - concreteOutputType = dim + inputStack[numStack - (abstractOutputType & VALUE_MASK)]; - if ((abstractOutputType & TOP_IF_LONG_OR_DOUBLE_FLAG) != 0 - && (concreteOutputType == LONG || concreteOutputType == DOUBLE)) { - concreteOutputType = TOP; - } - } else { - concreteOutputType = abstractOutputType; - } + int concreteOutputType = getConcreteOutputType(abstractOutputType, numStack); if (initializations != null) { concreteOutputType = getInitializedType(symbolTable, concreteOutputType); } @@ -1248,10 +1253,10 @@ final boolean merge( * @param symbolTable the type table to use to lookup and store type {@link Symbol}. * @param sourceType the abstract type with which the abstract type array element must be merged. * This type should be of {@link #CONSTANT_KIND}, {@link #REFERENCE_KIND} or {@link - * #UNINITIALIZED_KIND} kind, with positive or null array dimensions. + * #UNINITIALIZED_KIND} kind, with positive or {@literal null} array dimensions. * @param dstTypes an array of abstract types. These types should be of {@link #CONSTANT_KIND}, - * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or null array - * dimensions. + * {@link #REFERENCE_KIND} or {@link #UNINITIALIZED_KIND} kind, with positive or {@literal + * null} array dimensions. * @param dstIndex the index of the type that must be merged in dstTypes. * @return {@literal true} if the type array has been modified by this operation. */ diff --git a/spring-core/src/main/java/org/springframework/asm/Label.java b/spring-core/src/main/java/org/springframework/asm/Label.java index 0004179aa34e..e9e2f9e0d2e6 100644 --- a/spring-core/src/main/java/org/springframework/asm/Label.java +++ b/spring-core/src/main/java/org/springframework/asm/Label.java @@ -227,7 +227,8 @@ public class Label { /** * The maximum height reached by the output stack, relatively to the top of the input stack, in - * the basic block corresponding to this label. This maximum is always positive or null. + * the basic block corresponding to this label. This maximum is always positive or {@literal + * null}. */ short outputStackMax; @@ -264,12 +265,12 @@ public class Label { Edge outgoingEdges; /** - * The next element in the list of labels to which this label belongs, or null if it does not - * belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} sentinel, in - * order to ensure that this field is null if and only if this label does not belong to a list of - * labels. Note that there can be several lists of labels at the same time, but that a label can - * belong to at most one list at a time (unless some lists share a common tail, but this is not - * used in practice). + * The next element in the list of labels to which this label belongs, or {@literal null} if it + * does not belong to any list. All lists of labels must end with the {@link #EMPTY_LIST} + * sentinel, in order to ensure that this field is null if and only if this label does not belong + * to a list of labels. Note that there can be several lists of labels at the same time, but that + * a label can belong to at most one list at a time (unless some lists share a common tail, but + * this is not used in practice). * *
List of labels are used in {@link MethodWriter#computeAllFrames} and {@link * MethodWriter#computeMaxStackAndLocal} to compute stack map frames and the maximum stack size, diff --git a/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java b/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java index 5c404ac68f85..0f987f072919 100644 --- a/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java +++ b/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java @@ -56,7 +56,9 @@ public abstract class MethodVisitor { */ protected final int api; - /** The method visitor to which this visitor must delegate method calls. May be null. */ + /** + * The method visitor to which this visitor must delegate method calls. May be {@literal null}. + */ protected MethodVisitor mv; /** @@ -78,8 +80,8 @@ public MethodVisitor(final int api) { * be null. */ public MethodVisitor(final int api, final MethodVisitor methodVisitor) { - if (api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4 && api != Opcodes.ASM7) { - throw new IllegalArgumentException(); + if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) { + throw new IllegalArgumentException("Unsupported api " + api); } this.api = api; this.mv = methodVisitor; @@ -92,7 +94,7 @@ public MethodVisitor(final int api, final MethodVisitor methodVisitor) { /** * Visits a parameter of this method. * - * @param name parameter name or null if none is provided. + * @param name parameter name or {@literal null} if none is provided. * @param access the parameter's access flags, only {@code ACC_FINAL}, {@code ACC_SYNTHETIC} * or/and {@code ACC_MANDATED} are allowed (see {@link Opcodes}). */ @@ -395,14 +397,8 @@ public void visitFieldInsn( @Deprecated public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor) { - if (api >= Opcodes.ASM5) { - boolean isInterface = opcode == Opcodes.INVOKEINTERFACE; - visitMethodInsn(opcode, owner, name, descriptor, isInterface); - return; - } - if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, descriptor); - } + int opcodeAndSource = opcode | (api < Opcodes.ASM5 ? Opcodes.SOURCE_DEPRECATED : 0); + visitMethodInsn(opcodeAndSource, owner, name, descriptor, opcode == Opcodes.INVOKEINTERFACE); } /** @@ -422,15 +418,15 @@ public void visitMethodInsn( final String name, final String descriptor, final boolean isInterface) { - if (api < Opcodes.ASM5) { + if (api < Opcodes.ASM5 && (opcode & Opcodes.SOURCE_DEPRECATED) == 0) { if (isInterface != (opcode == Opcodes.INVOKEINTERFACE)) { - throw new IllegalArgumentException("INVOKESPECIAL/STATIC on interfaces requires ASM5"); + throw new UnsupportedOperationException("INVOKESPECIAL/STATIC on interfaces requires ASM5"); } visitMethodInsn(opcode, owner, name, descriptor); return; } if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + mv.visitMethodInsn(opcode & ~Opcodes.SOURCE_MASK, owner, name, descriptor, isInterface); } } diff --git a/spring-core/src/main/java/org/springframework/asm/MethodWriter.java b/spring-core/src/main/java/org/springframework/asm/MethodWriter.java index 7719a2db90c9..bc16e433a195 100644 --- a/spring-core/src/main/java/org/springframework/asm/MethodWriter.java +++ b/spring-core/src/main/java/org/springframework/asm/MethodWriter.java @@ -654,37 +654,26 @@ public AnnotationVisitor visitAnnotationDefault() { @Override public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { - // Create a ByteVector to hold an 'annotation' JVMS structure. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. - ByteVector annotation = new ByteVector(); - // Write type_index and reserve space for num_element_value_pairs. - annotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); if (visible) { return lastRuntimeVisibleAnnotation = - new AnnotationWriter(symbolTable, annotation, lastRuntimeVisibleAnnotation); + AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation); } else { return lastRuntimeInvisibleAnnotation = - new AnnotationWriter(symbolTable, annotation, lastRuntimeInvisibleAnnotation); + AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation); } } @Override public AnnotationVisitor visitTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { - // Create a ByteVector to hold a 'type_annotation' JVMS structure. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. - ByteVector typeAnnotation = new ByteVector(); - // Write target_type, target_info, and target_path. - TypeReference.putTarget(typeRef, typeAnnotation); - TypePath.put(typePath, typeAnnotation); - // Write type_index and reserve space for num_element_value_pairs. - typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); if (visible) { return lastRuntimeVisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeVisibleTypeAnnotation); + AnnotationWriter.create( + symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation); } else { return lastRuntimeInvisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastRuntimeInvisibleTypeAnnotation); + AnnotationWriter.create( + symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation); } } @@ -700,27 +689,24 @@ public void visitAnnotableParameterCount(final int parameterCount, final boolean @Override public AnnotationVisitor visitParameterAnnotation( final int parameter, final String annotationDescriptor, final boolean visible) { - // Create a ByteVector to hold an 'annotation' JVMS structure. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16. - ByteVector annotation = new ByteVector(); - // Write type_index and reserve space for num_element_value_pairs. - annotation.putShort(symbolTable.addConstantUtf8(annotationDescriptor)).putShort(0); if (visible) { if (lastRuntimeVisibleParameterAnnotations == null) { lastRuntimeVisibleParameterAnnotations = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; } return lastRuntimeVisibleParameterAnnotations[parameter] = - new AnnotationWriter( - symbolTable, annotation, lastRuntimeVisibleParameterAnnotations[parameter]); + AnnotationWriter.create( + symbolTable, annotationDescriptor, lastRuntimeVisibleParameterAnnotations[parameter]); } else { if (lastRuntimeInvisibleParameterAnnotations == null) { lastRuntimeInvisibleParameterAnnotations = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; } return lastRuntimeInvisibleParameterAnnotations[parameter] = - new AnnotationWriter( - symbolTable, annotation, lastRuntimeInvisibleParameterAnnotations[parameter]); + AnnotationWriter.create( + symbolTable, + annotationDescriptor, + lastRuntimeInvisibleParameterAnnotations[parameter]); } } @@ -1415,20 +1401,22 @@ public void visitMultiANewArrayInsn(final String descriptor, final int numDimens @Override public AnnotationVisitor visitInsnAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { - // Create a ByteVector to hold a 'type_annotation' JVMS structure. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. - ByteVector typeAnnotation = new ByteVector(); - // Write target_type, target_info, and target_path. - TypeReference.putTarget((typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), typeAnnotation); - TypePath.put(typePath, typeAnnotation); - // Write type_index and reserve space for num_element_value_pairs. - typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); if (visible) { return lastCodeRuntimeVisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation); + AnnotationWriter.create( + symbolTable, + (typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), + typePath, + descriptor, + lastCodeRuntimeVisibleTypeAnnotation); } else { return lastCodeRuntimeInvisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation); + AnnotationWriter.create( + symbolTable, + (typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), + typePath, + descriptor, + lastCodeRuntimeInvisibleTypeAnnotation); } } @@ -1449,20 +1437,14 @@ public void visitTryCatchBlock( @Override public AnnotationVisitor visitTryCatchAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { - // Create a ByteVector to hold a 'type_annotation' JVMS structure. - // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.20. - ByteVector typeAnnotation = new ByteVector(); - // Write target_type, target_info, and target_path. - TypeReference.putTarget(typeRef, typeAnnotation); - TypePath.put(typePath, typeAnnotation); - // Write type_index and reserve space for num_element_value_pairs. - typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); if (visible) { return lastCodeRuntimeVisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation); + AnnotationWriter.create( + symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeVisibleTypeAnnotation); } else { return lastCodeRuntimeInvisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation); + AnnotationWriter.create( + symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeInvisibleTypeAnnotation); } } @@ -1530,10 +1512,18 @@ public AnnotationVisitor visitLocalVariableAnnotation( typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); if (visible) { return lastCodeRuntimeVisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeVisibleTypeAnnotation); + new AnnotationWriter( + symbolTable, + /* useNamedValues = */ true, + typeAnnotation, + lastCodeRuntimeVisibleTypeAnnotation); } else { return lastCodeRuntimeInvisibleTypeAnnotation = - new AnnotationWriter(symbolTable, typeAnnotation, lastCodeRuntimeInvisibleTypeAnnotation); + new AnnotationWriter( + symbolTable, + /* useNamedValues = */ true, + typeAnnotation, + lastCodeRuntimeInvisibleTypeAnnotation); } } @@ -2004,10 +1994,6 @@ private void putFrameType(final Object type) { * attribute) are the same as the corresponding attributes in the given method. * * @param source the source ClassReader from which the attributes of this method might be copied. - * @param methodInfoOffset the offset in 'source.b' of the method_info JVMS structure from which - * the attributes of this method might be copied. - * @param methodInfoLength the length in 'source.b' of the method_info JVMS structure from which - * the attributes of this method might be copied. * @param hasSyntheticAttribute whether the method_info JVMS structure from which the attributes * of this method might be copied contains a Synthetic attribute. * @param hasDeprecatedAttribute whether the method_info JVMS structure from which the attributes @@ -2024,8 +2010,6 @@ private void putFrameType(final Object type) { */ boolean canCopyMethodAttributes( final ClassReader source, - final int methodInfoOffset, - final int methodInfoLength, final boolean hasSyntheticAttribute, final boolean hasDeprecatedAttribute, final int descriptorIndex, @@ -2060,12 +2044,23 @@ boolean canCopyMethodAttributes( currentExceptionOffset += 2; } } + return true; + } + + /** + * Sets the source from which the attributes of this method will be copied. + * + * @param methodInfoOffset the offset in 'symbolTable.getSource()' of the method_info JVMS + * structure from which the attributes of this method will be copied. + * @param methodInfoLength the length in 'symbolTable.getSource()' of the method_info JVMS + * structure from which the attributes of this method will be copied. + */ + void setMethodAttributesSource(final int methodInfoOffset, final int methodInfoLength) { // Don't copy the attributes yet, instead store their location in the source class reader so // they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes // of the method_info JVMS structure. this.sourceOffset = methodInfoOffset + 6; this.sourceLength = methodInfoLength - 6; - return true; } /** @@ -2133,29 +2128,13 @@ int computeMethodInfoSize() { symbolTable.addConstantUtf8(Constants.EXCEPTIONS); size += 8 + 2 * numberOfExceptions; } - boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { - symbolTable.addConstantUtf8(Constants.SYNTHETIC); - size += 6; - } - if (signatureIndex != 0) { - symbolTable.addConstantUtf8(Constants.SIGNATURE); - size += 8; - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - symbolTable.addConstantUtf8(Constants.DEPRECATED); - size += 6; - } - if (lastRuntimeVisibleAnnotation != null) { - size += - lastRuntimeVisibleAnnotation.computeAnnotationsSize( - Constants.RUNTIME_VISIBLE_ANNOTATIONS); - } - if (lastRuntimeInvisibleAnnotation != null) { - size += - lastRuntimeInvisibleAnnotation.computeAnnotationsSize( - Constants.RUNTIME_INVISIBLE_ANNOTATIONS); - } + size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex); + size += + AnnotationWriter.computeAnnotationsSize( + lastRuntimeVisibleAnnotation, + lastRuntimeInvisibleAnnotation, + lastRuntimeVisibleTypeAnnotation, + lastRuntimeInvisibleTypeAnnotation); if (lastRuntimeVisibleParameterAnnotations != null) { size += AnnotationWriter.computeParameterAnnotationsSize( @@ -2174,16 +2153,6 @@ int computeMethodInfoSize() { ? lastRuntimeInvisibleParameterAnnotations.length : invisibleAnnotableParameterCount); } - if (lastRuntimeVisibleTypeAnnotation != null) { - size += - lastRuntimeVisibleTypeAnnotation.computeAnnotationsSize( - Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); - } - if (lastRuntimeInvisibleTypeAnnotation != null) { - size += - lastRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( - Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); - } if (defaultValue != null) { symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT); size += 6 + defaultValue.length; @@ -2211,7 +2180,7 @@ void putMethodInfo(final ByteVector output) { output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex); // If this method_info must be copied from an existing one, copy it now and return early. if (sourceOffset != 0) { - output.putByteArray(symbolTable.getSource().b, sourceOffset, sourceLength); + output.putByteArray(symbolTable.getSource().classFileBuffer, sourceOffset, sourceLength); return; } // For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS. @@ -2365,26 +2334,14 @@ void putMethodInfo(final ByteVector output) { output.putShort(exceptionIndex); } } - if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { - output.putShort(symbolTable.addConstantUtf8(Constants.SYNTHETIC)).putInt(0); - } - if (signatureIndex != 0) { - output - .putShort(symbolTable.addConstantUtf8(Constants.SIGNATURE)) - .putInt(2) - .putShort(signatureIndex); - } - if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { - output.putShort(symbolTable.addConstantUtf8(Constants.DEPRECATED)).putInt(0); - } - if (lastRuntimeVisibleAnnotation != null) { - lastRuntimeVisibleAnnotation.putAnnotations( - symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_ANNOTATIONS), output); - } - if (lastRuntimeInvisibleAnnotation != null) { - lastRuntimeInvisibleAnnotation.putAnnotations( - symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_ANNOTATIONS), output); - } + Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output); + AnnotationWriter.putAnnotations( + symbolTable, + lastRuntimeVisibleAnnotation, + lastRuntimeInvisibleAnnotation, + lastRuntimeVisibleTypeAnnotation, + lastRuntimeInvisibleTypeAnnotation, + output); if (lastRuntimeVisibleParameterAnnotations != null) { AnnotationWriter.putParameterAnnotations( symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS), @@ -2403,14 +2360,6 @@ void putMethodInfo(final ByteVector output) { : invisibleAnnotableParameterCount, output); } - if (lastRuntimeVisibleTypeAnnotation != null) { - lastRuntimeVisibleTypeAnnotation.putAnnotations( - symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output); - } - if (lastRuntimeInvisibleTypeAnnotation != null) { - lastRuntimeInvisibleTypeAnnotation.putAnnotations( - symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output); - } if (defaultValue != null) { output .putShort(symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT)) diff --git a/spring-core/src/main/java/org/springframework/asm/ModuleVisitor.java b/spring-core/src/main/java/org/springframework/asm/ModuleVisitor.java index 647f7a98cb96..e8c2b0628659 100644 --- a/spring-core/src/main/java/org/springframework/asm/ModuleVisitor.java +++ b/spring-core/src/main/java/org/springframework/asm/ModuleVisitor.java @@ -42,7 +42,9 @@ public abstract class ModuleVisitor { */ protected final int api; - /** The module visitor to which this visitor must delegate method calls. May be null. */ + /** + * The module visitor to which this visitor must delegate method calls. May be {@literal null}. + */ protected ModuleVisitor mv; /** @@ -64,8 +66,8 @@ public ModuleVisitor(final int api) { * be null. */ public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) { - if (api != Opcodes.ASM6 && api != Opcodes.ASM7) { - throw new IllegalArgumentException(); + if (api != Opcodes.ASM7 && api != Opcodes.ASM6) { + throw new IllegalArgumentException("Unsupported api " + api); } this.api = api; this.mv = moduleVisitor; diff --git a/spring-core/src/main/java/org/springframework/asm/Opcodes.java b/spring-core/src/main/java/org/springframework/asm/Opcodes.java index ee4b79e546f4..1cbfe9d8df78 100644 --- a/spring-core/src/main/java/org/springframework/asm/Opcodes.java +++ b/spring-core/src/main/java/org/springframework/asm/Opcodes.java @@ -48,8 +48,212 @@ public interface Opcodes { int ASM6 = 6 << 16 | 0 << 8; int ASM7 = 7 << 16 | 0 << 8; - // Java ClassFile versions (the minor version is stored in the 16 most - // significant bits, and the + /* + * Internal flags used to redirect calls to deprecated methods. For instance, if a visitOldStuff + * method in API_OLD is deprecated and replaced with visitNewStuff in API_NEW, then the + * redirection should be done as follows: + * + *
+ * public class StuffVisitor { + * ... + * + * @Deprecated public void visitOldStuff(int arg, ...) { + * // SOURCE_DEPRECATED means "a call from a deprecated method using the old 'api' value". + * visitNewStuf(arg | (api < API_NEW ? SOURCE_DEPRECATED : 0), ...); + * } + * + * public void visitNewStuff(int argAndSource, ...) { + * if (api < API_NEW && (argAndSource & SOURCE_DEPRECATED) == 0) { + * visitOldStuff(argAndSource, ...); + * } else { + * int arg = argAndSource & ~SOURCE_MASK; + * [ do stuff ] + * } + * } + * } + *+ * + *
If 'api' is equal to API_NEW, there are two cases: + * + *
If 'api' is equal to API_OLD, there are two cases: + * + *
If a user subclass overrides one of these methods, there are only two cases: either 'api' is + * API_OLD and visitOldStuff is overridden (and visitNewStuff is not), or 'api' is API_NEW or + * more, and visitNewStuff is overridden (and visitOldStuff is not). Any other case is a user + * programming error. + * + *
If 'api' is equal to API_NEW, the class hierarchy is equivalent to + * + *
+ * public class StuffVisitor { + * @Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); } + * public void visitNewStuff(int arg, ...) { [ do stuff ] } + * } + * class UserStuffVisitor extends StuffVisitor { + * @Override public void visitNewStuff(int arg, ...) { + * super.visitNewStuff(int arg, ...); // optional + * [ do user stuff ] + * } + * } + *+ * + *
It is then obvious that whether visitNewStuff or visitOldStuff is called, 'do stuff' and 'do + * user stuff' will be executed, in this order. + * + *
If 'api' is equal to API_OLD, the class hierarchy is equivalent to + * + *
+ * public class StuffVisitor { + * @Deprecated public void visitOldStuff(int arg, ...) { + * visitNewStuf(arg | SOURCE_DEPRECATED, ...); + * } + * public void visitNewStuff(int argAndSource...) { + * if ((argAndSource & SOURCE_DEPRECATED) == 0) { + * visitOldStuff(argAndSource, ...); + * } else { + * int arg = argAndSource & ~SOURCE_MASK; + * [ do stuff ] + * } + * } + * } + * class UserStuffVisitor extends StuffVisitor { + * @Override public void visitOldStuff(int arg, ...) { + * super.visitOldStuff(int arg, ...); // optional + * [ do user stuff ] + * } + * } + *+ * + *
and there are two cases: + * + *
In ASM packages, subclasses of StuffVisitor can typically be sub classed again by the user, + * and can be used with API_OLD or API_NEW. Because of this, if such a subclass must override + * visitNewStuff, it must do so in the following way (and must not override visitOldStuff): + * + *
+ * public class AsmStuffVisitor extends StuffVisitor { + * @Override public void visitNewStuff(int argAndSource, ...) { + * if (api < API_NEW && (argAndSource & SOURCE_DEPRECATED) == 0) { + * super.visitNewStuff(argAndSource, ...); + * return; + * } + * super.visitNewStuff(argAndSource, ...); // optional + * int arg = argAndSource & ~SOURCE_MASK; + * [ do other stuff ] + * } + * } + *+ * + *
If a user class extends this with 'api' equal to API_NEW, the class hierarchy is equivalent + * to + * + *
+ * public class StuffVisitor { + * @Deprecated public void visitOldStuff(int arg, ...) { visitNewStuf(arg, ...); } + * public void visitNewStuff(int arg, ...) { [ do stuff ] } + * } + * public class AsmStuffVisitor extends StuffVisitor { + * @Override public void visitNewStuff(int arg, ...) { + * super.visitNewStuff(arg, ...); + * [ do other stuff ] + * } + * } + * class UserStuffVisitor extends StuffVisitor { + * @Override public void visitNewStuff(int arg, ...) { + * super.visitNewStuff(int arg, ...); + * [ do user stuff ] + * } + * } + *+ * + *
It is then obvious that whether visitNewStuff or visitOldStuff is called, 'do stuff', 'do + * other stuff' and 'do user stuff' will be executed, in this order. If, on the other hand, a user + * class extends AsmStuffVisitor with 'api' equal to API_OLD, the class hierarchy is equivalent to + * + *
+ * public class StuffVisitor { + * @Deprecated public void visitOldStuff(int arg, ...) { + * visitNewStuf(arg | SOURCE_DEPRECATED, ...); + * } + * public void visitNewStuff(int argAndSource, ...) { + * if ((argAndSource & SOURCE_DEPRECATED) == 0) { + * visitOldStuff(argAndSource, ...); + * } else { + * int arg = argAndSource & ~SOURCE_MASK; + * [ do stuff ] + * } + * } + * } + * public class AsmStuffVisitor extends StuffVisitor { + * @Override public void visitNewStuff(int argAndSource, ...) { + * if ((argAndSource & SOURCE_DEPRECATED) == 0) { + * super.visitNewStuff(argAndSource, ...); + * return; + * } + * super.visitNewStuff(argAndSource, ...); // optional + * int arg = argAndSource & ~SOURCE_MASK; + * [ do other stuff ] + * } + * } + * class UserStuffVisitor extends StuffVisitor { + * @Override public void visitOldStuff(int arg, ...) { + * super.visitOldStuff(arg, ...); + * [ do user stuff ] + * } + * } + *+ * + *
and, here again, whether visitNewStuff or visitOldStuff is called, 'do stuff', 'do other + * stuff' and 'do user stuff' will be executed, in this order (exercise left to the reader). + * + *