parent
c1218615df
commit
75abd9fc7e
|
@ -67,10 +67,18 @@ public abstract class AnnotationVisitor {
|
|||
* @param annotationVisitor the annotation visitor to which this visitor must delegate method
|
||||
* calls. May be {@literal null}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public AnnotationVisitor(final int api, final AnnotationVisitor annotationVisitor) {
|
||||
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
|
||||
if (api != Opcodes.ASM7
|
||||
&& api != Opcodes.ASM6
|
||||
&& api != Opcodes.ASM5
|
||||
&& api != Opcodes.ASM4
|
||||
&& api != Opcodes.ASM8_EXPERIMENTAL) {
|
||||
throw new IllegalArgumentException("Unsupported api " + api);
|
||||
}
|
||||
if (api == Opcodes.ASM8_EXPERIMENTAL) {
|
||||
Constants.checkAsm8Experimental(this);
|
||||
}
|
||||
this.api = api;
|
||||
this.av = annotationVisitor;
|
||||
}
|
||||
|
|
|
@ -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.ASM7);
|
||||
this.symbolTable = symbolTable;
|
||||
this.useNamedValues = useNamedValues;
|
||||
this.annotation = annotation;
|
||||
|
|
|
@ -50,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;
|
||||
|
||||
|
@ -190,7 +191,7 @@ public class 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.V14) {
|
||||
if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V15) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unsupported class file major version " + readShort(classFileOffset + 6));
|
||||
}
|
||||
|
@ -414,6 +415,7 @@ public class ClassReader {
|
|||
* @param parsingOptions the options to use to parse this class. One or more of {@link
|
||||
* #SKIP_CODE}, {@link #SKIP_DEBUG}, {@link #SKIP_FRAMES} or {@link #EXPAND_FRAMES}.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void accept(
|
||||
final ClassVisitor classVisitor,
|
||||
final Attribute[] attributePrototypes,
|
||||
|
@ -466,6 +468,10 @@ public class ClassReader {
|
|||
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 <i>reverse order</i> or their order in the ClassFile structure.
|
||||
Attribute attributes = null;
|
||||
|
@ -488,6 +494,8 @@ public class ClassReader {
|
|||
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)) {
|
||||
|
@ -505,6 +513,8 @@ public class ClassReader {
|
|||
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)) {
|
||||
|
@ -662,6 +672,17 @@ public class ClassReader {
|
|||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
@ -676,6 +697,15 @@ public class ClassReader {
|
|||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
@ -823,6 +853,186 @@ public class ClassReader {
|
|||
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.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
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).
|
||||
|
||||
int accessFlags = 0;
|
||||
// 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 <i>reverse order</i> 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.DEPRECATED.equals(attributeName)) {
|
||||
accessFlags |= Opcodes.ACC_DEPRECATED;
|
||||
} 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.visitRecordComponentExperimental(accessFlags, 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.visitAnnotationExperimental(
|
||||
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.visitAnnotationExperimental(
|
||||
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.visitTypeAnnotationExperimental(
|
||||
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.visitTypeAnnotationExperimental(
|
||||
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.visitAttributeExperimental(attributes);
|
||||
attributes = nextAttribute;
|
||||
}
|
||||
|
||||
// Visit the end of the field.
|
||||
recordComponentVisitor.visitEndExperimental();
|
||||
return currentOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a JVMS field_info structure and makes the given visitor visit it.
|
||||
*
|
||||
|
@ -1149,7 +1359,7 @@ public class ClassReader {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
|
|
|
@ -30,9 +30,9 @@ package org.springframework.asm;
|
|||
/**
|
||||
* 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
|
||||
*/
|
||||
|
@ -65,10 +65,18 @@ public abstract class ClassVisitor {
|
|||
* @param classVisitor the class visitor to which this visitor must delegate method calls. May be
|
||||
* null.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public ClassVisitor(final int api, final ClassVisitor classVisitor) {
|
||||
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
|
||||
if (api != Opcodes.ASM7
|
||||
&& api != Opcodes.ASM6
|
||||
&& api != Opcodes.ASM5
|
||||
&& api != Opcodes.ASM4
|
||||
&& api != Opcodes.ASM8_EXPERIMENTAL) {
|
||||
throw new IllegalArgumentException("Unsupported api " + api);
|
||||
}
|
||||
if (api == Opcodes.ASM8_EXPERIMENTAL) {
|
||||
Constants.checkAsm8Experimental(this);
|
||||
}
|
||||
this.api = api;
|
||||
this.cv = classVisitor;
|
||||
}
|
||||
|
@ -240,6 +248,24 @@ public abstract class ClassVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
|
||||
* will break existing code using it</b>. 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.ASM8_EXPERIMENTAL) {
|
||||
throw new UnsupportedOperationException("This feature requires ASM8_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 +285,31 @@ public abstract class ClassVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a record component of the class.
|
||||
*
|
||||
* @param access the record component access flags, the only possible value is {@link
|
||||
* Opcodes#ACC_DEPRECATED}.
|
||||
* @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.
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public RecordComponentVisitor visitRecordComponentExperimental(
|
||||
final int access, final String name, final String descriptor, final String signature) {
|
||||
if (api < Opcodes.ASM8_EXPERIMENTAL) {
|
||||
throw new UnsupportedOperationException("This feature requires ASM8_EXPERIMENTAL");
|
||||
}
|
||||
if (cv != null) {
|
||||
return cv.visitRecordComponentExperimental(access, name, descriptor, signature);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a field of the class.
|
||||
*
|
||||
|
|
|
@ -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 class ClassWriter extends ClassVisitor {
|
|||
* maximum stack size nor the stack frames will be computed for these methods</i>.
|
||||
*/
|
||||
public ClassWriter(final ClassReader classReader, final int flags) {
|
||||
super(Opcodes.ASM7);
|
||||
super(/* latest api = */ Opcodes.ASM7);
|
||||
symbolTable = classReader == null ? new SymbolTable(this) : new SymbolTable(this, classReader);
|
||||
if ((flags & COMPUTE_FRAMES) != 0) {
|
||||
this.compute = MethodWriter.COMPUTE_ALL_FRAMES;
|
||||
|
@ -352,6 +372,16 @@ public class ClassWriter extends ClassVisitor {
|
|||
nestMemberClasses.putShort(symbolTable.addConstantClass(nestMember).index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
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 +407,20 @@ public class ClassWriter extends ClassVisitor {
|
|||
// and throw an exception if there is a difference?
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public final RecordComponentVisitor visitRecordComponentExperimental(
|
||||
final int access, final String name, final String descriptor, final String signature) {
|
||||
RecordComponentWriter recordComponentWriter =
|
||||
new RecordComponentWriter(symbolTable, access, 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 +491,7 @@ public class ClassWriter extends ClassVisitor {
|
|||
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 +571,24 @@ public class ClassWriter extends ClassVisitor {
|
|||
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 +693,24 @@ public class ClassWriter extends ClassVisitor {
|
|||
.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 +747,10 @@ public class ClassWriter extends ClassVisitor {
|
|||
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 +779,11 @@ public class ClassWriter extends ClassVisitor {
|
|||
methodWriter.collectAttributePrototypes(attributePrototypes);
|
||||
methodWriter = (MethodWriter) methodWriter.mv;
|
||||
}
|
||||
RecordComponentWriter recordComponentWriter = firstRecordComponent;
|
||||
while (recordComponentWriter != null) {
|
||||
recordComponentWriter.collectAttributePrototypes(attributePrototypes);
|
||||
recordComponentWriter = (RecordComponentWriter) recordComponentWriter.delegate;
|
||||
}
|
||||
return attributePrototypes.toArray();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
// THE POSSIBILITY OF SUCH DAMAGE.
|
||||
package org.springframework.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
|
||||
* API.
|
||||
|
@ -56,7 +61,8 @@ final class Constants implements Opcodes {
|
|||
static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
|
||||
static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
|
||||
static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations";
|
||||
static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations";
|
||||
static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS =
|
||||
"RuntimeInvisibleParameterAnnotations";
|
||||
static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
|
||||
static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations";
|
||||
static final String ANNOTATION_DEFAULT = "AnnotationDefault";
|
||||
|
@ -67,6 +73,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
|
||||
|
@ -173,4 +181,41 @@ final class Constants implements Opcodes {
|
|||
static final int ASM_GOTO_W = 220;
|
||||
|
||||
private Constants() {}
|
||||
|
||||
static void checkAsm8Experimental(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(
|
||||
"ASM8_EXPERIMENTAL can only be used by classes compiled with --enable-preview");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,10 +63,18 @@ public abstract class FieldVisitor {
|
|||
* @param fieldVisitor the field visitor to which this visitor must delegate method calls. May be
|
||||
* null.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public FieldVisitor(final int api, final FieldVisitor fieldVisitor) {
|
||||
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
|
||||
if (api != Opcodes.ASM7
|
||||
&& api != Opcodes.ASM6
|
||||
&& api != Opcodes.ASM5
|
||||
&& api != Opcodes.ASM4
|
||||
&& api != Opcodes.ASM8_EXPERIMENTAL) {
|
||||
throw new IllegalArgumentException("Unsupported api " + api);
|
||||
}
|
||||
if (api == Opcodes.ASM8_EXPERIMENTAL) {
|
||||
Constants.checkAsm8Experimental(this);
|
||||
}
|
||||
this.api = api;
|
||||
this.fv = fieldVisitor;
|
||||
}
|
||||
|
|
|
@ -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.ASM7);
|
||||
this.symbolTable = symbolTable;
|
||||
this.accessFlags = access;
|
||||
this.nameIndex = symbolTable.addConstantUtf8(name);
|
||||
|
|
|
@ -79,10 +79,18 @@ public abstract class MethodVisitor {
|
|||
* @param methodVisitor the method visitor to which this visitor must delegate method calls. May
|
||||
* be null.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public MethodVisitor(final int api, final MethodVisitor methodVisitor) {
|
||||
if (api != Opcodes.ASM7 && api != Opcodes.ASM6 && api != Opcodes.ASM5 && api != Opcodes.ASM4) {
|
||||
if (api != Opcodes.ASM7
|
||||
&& api != Opcodes.ASM6
|
||||
&& api != Opcodes.ASM5
|
||||
&& api != Opcodes.ASM4
|
||||
&& api != Opcodes.ASM8_EXPERIMENTAL) {
|
||||
throw new IllegalArgumentException("Unsupported api " + api);
|
||||
}
|
||||
if (api == Opcodes.ASM8_EXPERIMENTAL) {
|
||||
Constants.checkAsm8Experimental(this);
|
||||
}
|
||||
this.api = api;
|
||||
this.mv = methodVisitor;
|
||||
}
|
||||
|
@ -534,7 +542,7 @@ public abstract class MethodVisitor {
|
|||
|| (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) {
|
||||
|
|
|
@ -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.ASM7);
|
||||
this.symbolTable = symbolTable;
|
||||
this.accessFlags = "<init>".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access;
|
||||
this.nameIndex = symbolTable.addConstantUtf8(name);
|
||||
|
|
|
@ -65,10 +65,18 @@ public abstract class ModuleVisitor {
|
|||
* @param moduleVisitor the module visitor to which this visitor must delegate method calls. May
|
||||
* be null.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public ModuleVisitor(final int api, final ModuleVisitor moduleVisitor) {
|
||||
if (api != Opcodes.ASM7 && api != Opcodes.ASM6) {
|
||||
if (api != Opcodes.ASM7
|
||||
&& api != Opcodes.ASM6
|
||||
&& api != Opcodes.ASM5
|
||||
&& api != Opcodes.ASM4
|
||||
&& api != Opcodes.ASM8_EXPERIMENTAL) {
|
||||
throw new IllegalArgumentException("Unsupported api " + api);
|
||||
}
|
||||
if (api == Opcodes.ASM8_EXPERIMENTAL) {
|
||||
Constants.checkAsm8Experimental(this);
|
||||
}
|
||||
this.api = api;
|
||||
this.mv = moduleVisitor;
|
||||
}
|
||||
|
|
|
@ -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.ASM7);
|
||||
this.symbolTable = symbolTable;
|
||||
this.moduleNameIndex = name;
|
||||
this.moduleFlags = access;
|
||||
|
|
|
@ -48,6 +48,14 @@ public interface Opcodes {
|
|||
int ASM6 = 6 << 16 | 0 << 8;
|
||||
int ASM7 = 7 << 16 | 0 << 8;
|
||||
|
||||
/**
|
||||
* <i>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.</i>
|
||||
*
|
||||
* @deprecated This API is experimental.
|
||||
*/
|
||||
@Deprecated int ASM8_EXPERIMENTAL = 1 << 24 | 8 << 16 | 0 << 8;
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
@ -270,6 +278,7 @@ public interface Opcodes {
|
|||
int V12 = 0 << 16 | 56;
|
||||
int V13 = 0 << 16 | 57;
|
||||
int V14 = 0 << 16 | 58;
|
||||
int V15 = 0 << 16 | 59;
|
||||
|
||||
/**
|
||||
* Version flag indicating that the class is using 'preview' features.
|
||||
|
@ -306,7 +315,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.
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
// 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 org.springframework.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
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class RecordComponentVisitor {
|
||||
/**
|
||||
* The ASM API version implemented by this visitor. The value of this field must be {@link
|
||||
* Opcodes#ASM8_EXPERIMENTAL}.
|
||||
*/
|
||||
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_EXPERIMENTAL}.
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
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_EXPERIMENTAL}.
|
||||
* @param recordComponentVisitor the record component visitor to which this visitor must delegate
|
||||
* method calls. May be null.
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public RecordComponentVisitor(
|
||||
final int api, final RecordComponentVisitor recordComponentVisitor) {
|
||||
if (api != Opcodes.ASM7
|
||||
&& api != Opcodes.ASM6
|
||||
&& api != Opcodes.ASM5
|
||||
&& api != Opcodes.ASM4
|
||||
&& api != Opcodes.ASM8_EXPERIMENTAL) {
|
||||
throw new IllegalArgumentException("Unsupported api " + api);
|
||||
}
|
||||
if (api == Opcodes.ASM8_EXPERIMENTAL) {
|
||||
Constants.checkAsm8Experimental(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}.
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public RecordComponentVisitor getDelegateExperimental() {
|
||||
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.
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public AnnotationVisitor visitAnnotationExperimental(
|
||||
final String descriptor, final boolean visible) {
|
||||
if (delegate != null) {
|
||||
return delegate.visitAnnotationExperimental(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.
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public AnnotationVisitor visitTypeAnnotationExperimental(
|
||||
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
|
||||
if (delegate != null) {
|
||||
return delegate.visitTypeAnnotationExperimental(typeRef, typePath, descriptor, visible);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a non standard attribute of the record component.
|
||||
*
|
||||
* @param attribute an attribute.
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public void visitAttributeExperimental(final Attribute attribute) {
|
||||
if (delegate != null) {
|
||||
delegate.visitAttributeExperimental(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.
|
||||
*
|
||||
* @deprecated this API is experimental.
|
||||
*/
|
||||
@Deprecated
|
||||
public void visitEndExperimental() {
|
||||
if (delegate != null) {
|
||||
delegate.visitEndExperimental();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
// 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 org.springframework.asm;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
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 component_info structure, and those related to attributes
|
||||
// are ordered as in Section TODO of the JVMS.
|
||||
// The field accessFlag doesn't exist in the component_info structure but is used to carry
|
||||
// ACC_DEPRECATED which is represented by an attribute in the structure and as an access flag by
|
||||
// ASM.
|
||||
|
||||
/** The access_flags field can only be {@link Opcodes#ACC_DEPRECATED}. */
|
||||
private final int accessFlags;
|
||||
|
||||
/** 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}.
|
||||
*
|
||||
* <p><b>WARNING</b>: this list stores the attributes in the <i>reverse</i> order of their visit.
|
||||
* firstAttribute is actually the last attribute visited in {@link
|
||||
* #visitAttributeExperimental(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 accessFlags the record component access flags, only synthetic and/or deprecated.
|
||||
* @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 int accessFlags,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature) {
|
||||
super(/* latest api = */ Opcodes.ASM7);
|
||||
this.symbolTable = symbolTable;
|
||||
this.accessFlags = accessFlags;
|
||||
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 visitAnnotationExperimental(
|
||||
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 visitTypeAnnotationExperimental(
|
||||
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 visitAttributeExperimental(final Attribute attribute) {
|
||||
// Store the attributes in the <i>reverse</i> order of their visit by this method.
|
||||
attribute.nextAttribute = firstAttribute;
|
||||
firstAttribute = attribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEndExperimental() {
|
||||
// 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, accessFlags & Opcodes.ACC_DEPRECATED, 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 ((accessFlags & Opcodes.ACC_DEPRECATED) != 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, accessFlags, 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue