From ad09f1b5ecb2b7d5361d7caf194280ed66bc896c Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 20 Mar 2014 01:06:22 +0100 Subject: [PATCH] Upgraded embedded ASM version to 5.0 Issue: SPR-11239 --- build.gradle | 4 +- .../asm/AnnotationVisitor.java | 8 +- .../springframework/asm/AnnotationWriter.java | 55 ++- .../org/springframework/asm/ByteVector.java | 97 ++-- .../org/springframework/asm/ClassReader.java | 368 ++++++++++++-- .../org/springframework/asm/ClassVisitor.java | 48 +- .../org/springframework/asm/ClassWriter.java | 64 ++- .../java/org/springframework/asm/Context.java | 35 ++ .../org/springframework/asm/FieldVisitor.java | 41 +- .../org/springframework/asm/FieldWriter.java | 58 ++- .../java/org/springframework/asm/Frame.java | 17 +- .../springframework/asm/MethodVisitor.java | 256 +++++++++- .../org/springframework/asm/MethodWriter.java | 235 ++++++++- .../java/org/springframework/asm/Opcodes.java | 7 +- .../springframework/asm/SpringAsmInfo.java | 12 +- .../org/springframework/asm/TypePath.java | 193 ++++++++ .../springframework/asm/TypeReference.java | 452 ++++++++++++++++++ .../org/springframework/asm/package-info.java | 30 +- .../asm/util/TraceClassVisitor.java | 37 -- .../asm/util/package-info.java | 22 - .../cglib/SpringCglibInfo.java | 8 +- .../springframework/cglib/package-info.java | 30 +- 22 files changed, 1835 insertions(+), 242 deletions(-) create mode 100644 spring-core/src/main/java/org/springframework/asm/TypePath.java create mode 100644 spring-core/src/main/java/org/springframework/asm/TypeReference.java delete mode 100644 spring-core/src/main/java/org/springframework/asm/util/TraceClassVisitor.java delete mode 100644 spring-core/src/main/java/org/springframework/asm/util/package-info.java diff --git a/build.gradle b/build.gradle index a29d52ed1d..2867a800ee 100644 --- a/build.gradle +++ b/build.gradle @@ -175,8 +175,8 @@ project("spring-build-src") { project("spring-core") { description = "Spring Core" - // As of Spring 4.0, spring-core includes asm 4.2 and repackages cglib 3.1, inlining - // both into the spring-core jar. cglib 3.1 itself depends on asm 4, and is therefore + // As of Spring 4.0.3, spring-core includes asm 5.0 and repackages cglib 3.1, inlining + // both into the spring-core jar. cglib 3.1 itself depends on asm 4+, and is therefore // further transformed by the JarJar task to depend on org.springframework.asm; this // avoids including two different copies of asm unnecessarily. def cglibVersion = "3.1" diff --git a/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java b/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java index 88e40d0980..5f48e99324 100644 --- a/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java +++ b/spring-core/src/main/java/org/springframework/asm/AnnotationVisitor.java @@ -41,7 +41,7 @@ public abstract class AnnotationVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -56,7 +56,7 @@ public abstract class AnnotationVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public AnnotationVisitor(final int api) { this(api, null); @@ -67,13 +67,13 @@ public abstract class AnnotationVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param av * the annotation visitor to which this visitor must delegate * method calls. May be null. */ public AnnotationVisitor(final int api, final AnnotationVisitor av) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; diff --git a/spring-core/src/main/java/org/springframework/asm/AnnotationWriter.java b/spring-core/src/main/java/org/springframework/asm/AnnotationWriter.java index 29888b0d57..dcd8835714 100644 --- a/spring-core/src/main/java/org/springframework/asm/AnnotationWriter.java +++ b/spring-core/src/main/java/org/springframework/asm/AnnotationWriter.java @@ -104,7 +104,7 @@ final class AnnotationWriter extends AnnotationVisitor { */ AnnotationWriter(final ClassWriter cw, final boolean named, final ByteVector bv, final ByteVector parent, final int offset) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.cw = cw; this.named = named; this.bv = bv; @@ -315,4 +315,57 @@ final class AnnotationWriter extends AnnotationVisitor { } } } + + /** + * Puts the given type reference and type path into the given bytevector. + * LOCAL_VARIABLE and RESOURCE_VARIABLE target types are not supported. + * + * @param typeRef + * a reference to the annotated type. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param out + * where the type reference and type path must be put. + */ + static void putTarget(int typeRef, TypePath typePath, ByteVector out) { + switch (typeRef >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + out.putShort(typeRef >>> 16); + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + out.putByte(typeRef >>> 24); + break; + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + out.putInt(typeRef); + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + out.put12(typeRef >>> 24, (typeRef & 0xFFFF00) >> 8); + break; + } + if (typePath == null) { + out.putByte(0); + } else { + int length = typePath.b[typePath.offset] * 2 + 1; + out.putByteArray(typePath.b, typePath.offset, length); + } + } } diff --git a/spring-core/src/main/java/org/springframework/asm/ByteVector.java b/spring-core/src/main/java/org/springframework/asm/ByteVector.java index e89184ae72..4992f1c80e 100644 --- a/spring-core/src/main/java/org/springframework/asm/ByteVector.java +++ b/spring-core/src/main/java/org/springframework/asm/ByteVector.java @@ -230,41 +230,68 @@ public class ByteVector { if (c >= '\001' && c <= '\177') { data[len++] = (byte) c; } else { - int byteLength = i; - for (int j = i; j < charLength; ++j) { - c = s.charAt(j); - if (c >= '\001' && c <= '\177') { - byteLength++; - } else if (c > '\u07FF') { - byteLength += 3; - } else { - byteLength += 2; - } - } - if (byteLength > 65535) { - throw new IllegalArgumentException(); - } - data[length] = (byte) (byteLength >>> 8); - data[length + 1] = (byte) byteLength; - if (length + 2 + byteLength > data.length) { - length = len; - enlarge(2 + byteLength); - data = this.data; - } - for (int j = i; j < charLength; ++j) { - c = s.charAt(j); - if (c >= '\001' && c <= '\177') { - data[len++] = (byte) c; - } else if (c > '\u07FF') { - data[len++] = (byte) (0xE0 | c >> 12 & 0xF); - data[len++] = (byte) (0x80 | c >> 6 & 0x3F); - data[len++] = (byte) (0x80 | c & 0x3F); - } else { - data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); - data[len++] = (byte) (0x80 | c & 0x3F); - } - } - break; + length = len; + return encodeUTF8(s, i, 65535); + } + } + length = len; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. The string length is encoded in two + * bytes before the encoded characters, if there is space for that (i.e. if + * this.length - i - 2 >= 0). + * + * @param s + * the String to encode. + * @param i + * the index of the first character to encode. The previous + * characters are supposed to have already been encoded, using + * only one byte per character. + * @param maxByteLength + * the maximum byte length of the encoded string, including the + * already encoded characters. + * @return this byte vector. + */ + ByteVector encodeUTF8(final String s, int i, int maxByteLength) { + int charLength = s.length(); + int byteLength = i; + char c; + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + byteLength++; + } else if (c > '\u07FF') { + byteLength += 3; + } else { + byteLength += 2; + } + } + if (byteLength > maxByteLength) { + throw new IllegalArgumentException(); + } + int start = length - i - 2; + if (start >= 0) { + data[start] = (byte) (byteLength >>> 8); + data[start + 1] = (byte) byteLength; + } + if (length + byteLength - i > data.length) { + enlarge(byteLength - i); + } + int len = length; + for (int j = i; j < charLength; ++j) { + c = s.charAt(j); + if (c >= '\001' && c <= '\177') { + data[len++] = (byte) c; + } else if (c > '\u07FF') { + data[len++] = (byte) (0xE0 | c >> 12 & 0xF); + data[len++] = (byte) (0x80 | c >> 6 & 0x3F); + data[len++] = (byte) (0x80 | c & 0x3F); + } else { + data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); + data[len++] = (byte) (0x80 | c & 0x3F); } } length = len; diff --git a/spring-core/src/main/java/org/springframework/asm/ClassReader.java b/spring-core/src/main/java/org/springframework/asm/ClassReader.java index 9db0233338..e303169f0b 100644 --- a/spring-core/src/main/java/org/springframework/asm/ClassReader.java +++ b/spring-core/src/main/java/org/springframework/asm/ClassReader.java @@ -166,8 +166,8 @@ public class ClassReader { public ClassReader(final byte[] b, final int off, final int len) { this.b = b; // checks the class version - /* SPRING PATCH: REMOVED FOR JDK 1.8 BYTECODE COMPATIBILITY - if (readShort(off + 6) > Opcodes.V1_7) { + /* SPRING PATCH: REMOVED FOR FORWARD COMPATIBILITY WITH JDK 9 + if (readShort(off + 6) > Opcodes.V1_8) { throw new IllegalArgumentException(); } */ @@ -559,6 +559,8 @@ public class ClassReader { String enclosingDesc = null; int anns = 0; int ianns = 0; + int tanns = 0; + int itanns = 0; int innerClasses = 0; Attribute attributes = null; @@ -583,6 +585,9 @@ public class ClassReader { } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; } else if ("Deprecated".equals(attrName)) { access |= Opcodes.ACC_DEPRECATED; } else if ("Synthetic".equals(attrName)) { @@ -594,6 +599,9 @@ public class ClassReader { } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; } else if ("BootstrapMethods".equals(attrName)) { int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { @@ -628,7 +636,7 @@ public class ClassReader { enclosingDesc); } - // visits the class annotations + // visits the class annotations and type annotations if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, @@ -641,6 +649,22 @@ public class ClassReader { classVisitor.visitAnnotation(readUTF8(v, c), false)); } } + if (ANNOTATIONS && tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (ANNOTATIONS && itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + classVisitor.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } // visits the attributes while (attributes != null) { @@ -699,6 +723,8 @@ public class ClassReader { String signature = null; int anns = 0; int ianns = 0; + int tanns = 0; + int itanns = 0; Object value = null; Attribute attributes = null; @@ -719,9 +745,15 @@ public class ClassReader { } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; } else { Attribute attr = readAttribute(context.attrs, attrName, u + 8, readInt(u + 4), c, -1, null); @@ -741,7 +773,7 @@ public class ClassReader { return u; } - // visits the field annotations + // visits the field annotations and type annotations if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, @@ -754,6 +786,22 @@ public class ClassReader { fv.visitAnnotation(readUTF8(v, c), false)); } } + if (ANNOTATIONS && tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + fv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (ANNOTATIONS && itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + fv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } // visits the field attributes while (attributes != null) { @@ -784,9 +832,9 @@ public class ClassReader { final Context context, int u) { // reads the method declaration char[] c = context.buffer; - int access = readUnsignedShort(u); - String name = readUTF8(u + 2, c); - String desc = readUTF8(u + 4, c); + context.access = readUnsignedShort(u); + context.name = readUTF8(u + 2, c); + context.desc = readUTF8(u + 4, c); u += 6; // reads the method attributes @@ -794,8 +842,11 @@ public class ClassReader { int exception = 0; String[] exceptions = null; String signature = null; + int methodParameters = 0; int anns = 0; int ianns = 0; + int tanns = 0; + int itanns = 0; int dann = 0; int mpanns = 0; int impanns = 0; @@ -820,24 +871,32 @@ public class ClassReader { } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if ("Deprecated".equals(attrName)) { - access |= Opcodes.ACC_DEPRECATED; + context.access |= Opcodes.ACC_DEPRECATED; } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = u + 8; } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { dann = u + 8; } else if ("Synthetic".equals(attrName)) { - access |= Opcodes.ACC_SYNTHETIC + context.access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleParameterAnnotations".equals(attrName)) { mpanns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleParameterAnnotations".equals(attrName)) { impanns = u + 8; + } else if ("MethodParameters".equals(attrName)) { + methodParameters = u + 8; } else { Attribute attr = readAttribute(context.attrs, attrName, u + 8, readInt(u + 4), c, -1, null); @@ -851,8 +910,8 @@ public class ClassReader { u += 2; // visits the method declaration - MethodVisitor mv = classVisitor.visitMethod(access, name, desc, - signature, exceptions); + MethodVisitor mv = classVisitor.visitMethod(context.access, + context.name, context.desc, signature, exceptions); if (mv == null) { return u; } @@ -896,6 +955,13 @@ public class ClassReader { } } + // visit the method parameters + if (methodParameters != 0) { + for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { + mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); + } + } + // visits the method annotations if (ANNOTATIONS && dann != 0) { AnnotationVisitor dv = mv.visitAnnotationDefault(); @@ -916,11 +982,27 @@ public class ClassReader { mv.visitAnnotation(readUTF8(v, c), false)); } } + if (ANNOTATIONS && tanns != 0) { + for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + mv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + } + if (ANNOTATIONS && itanns != 0) { + for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { + v = readAnnotationTarget(context, v); + v = readAnnotationValues(v + 2, c, true, + mv.visitTypeAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + } if (ANNOTATIONS && mpanns != 0) { - readParameterAnnotations(mpanns, desc, c, true, mv); + readParameterAnnotations(mv, context, mpanns, true); } if (ANNOTATIONS && impanns != 0) { - readParameterAnnotations(impanns, desc, c, false, mv); + readParameterAnnotations(mv, context, impanns, false); } // visits the method attributes @@ -933,9 +1015,6 @@ public class ClassReader { // visits the method code if (code != 0) { - context.access = access; - context.name = name; - context.desc = desc; mv.visitCode(); readCode(mv, context, code); } @@ -968,7 +1047,7 @@ public class ClassReader { // reads the bytecode to find the labels int codeStart = u; int codeEnd = u + codeLength; - Label[] labels = new Label[codeLength + 2]; + Label[] labels = context.labels = new Label[codeLength + 2]; readLabel(codeLength + 1, labels); while (u < codeEnd) { int offset = u - codeStart; @@ -1051,6 +1130,12 @@ public class ClassReader { u += 2; // reads the code attributes + int[] tanns = null; // start index of each visible type annotation + int[] itanns = null; // start index of each invisible type annotation + int tann = 0; // current index in tanns array + int itann = 0; // current index in itanns array + int ntoff = -1; // next visible type annotation code offset + int nitoff = -1; // next invisible type annotation code offset int varTable = 0; int varTypeTable = 0; boolean zip = true; @@ -1091,6 +1176,16 @@ public class ClassReader { v += 4; } } + } else if (ANNOTATIONS + && "RuntimeVisibleTypeAnnotations".equals(attrName)) { + tanns = readTypeAnnotations(mv, context, u + 8, true); + ntoff = tanns.length == 0 || readByte(tanns[0]) < 0x43 ? -1 + : readUnsignedShort(tanns[0] + 1); + } else if (ANNOTATIONS + && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { + itanns = readTypeAnnotations(mv, context, u + 8, false); + nitoff = itanns.length == 0 || readByte(itanns[0]) < 0x43 ? -1 + : readUnsignedShort(itanns[0] + 1); } else if (FRAMES && "StackMapTable".equals(attrName)) { if ((context.flags & SKIP_FRAMES) == 0) { stackMap = u + 10; @@ -1213,7 +1308,7 @@ public class ClassReader { } } if (frameCount > 0) { - stackMap = readFrame(stackMap, zip, unzip, labels, frame); + stackMap = readFrame(stackMap, zip, unzip, frame); --frameCount; } else { frame = null; @@ -1312,6 +1407,7 @@ public class ClassReader { case ClassWriter.FIELDORMETH_INSN: case ClassWriter.ITFMETH_INSN: { int cpIndex = items[readUnsignedShort(u + 1)]; + boolean itf = b[cpIndex - 1] == ClassWriter.IMETH; String iowner = readClass(cpIndex, c); cpIndex = items[readUnsignedShort(cpIndex + 2)]; String iname = readUTF8(cpIndex, c); @@ -1319,7 +1415,7 @@ public class ClassReader { if (opcode < Opcodes.INVOKEVIRTUAL) { mv.visitFieldInsn(opcode, iowner, iname, idesc); } else { - mv.visitMethodInsn(opcode, iowner, iname, idesc); + mv.visitMethodInsn(opcode, iowner, iname, idesc, itf); } if (opcode == Opcodes.INVOKEINTERFACE) { u += 5; @@ -1360,6 +1456,29 @@ public class ClassReader { u += 4; break; } + + // visit the instruction annotations, if any + while (tanns != null && tann < tanns.length && ntoff <= offset) { + if (ntoff == offset) { + int v = readAnnotationTarget(context, tanns[tann]); + readAnnotationValues(v + 2, c, true, + mv.visitInsnAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), true)); + } + ntoff = ++tann >= tanns.length || readByte(tanns[tann]) < 0x43 ? -1 + : readUnsignedShort(tanns[tann] + 1); + } + while (itanns != null && itann < itanns.length && nitoff <= offset) { + if (nitoff == offset) { + int v = readAnnotationTarget(context, itanns[itann]); + readAnnotationValues(v + 2, c, true, + mv.visitInsnAnnotation(context.typeRef, + context.typePath, readUTF8(v, c), false)); + } + nitoff = ++itann >= itanns.length + || readByte(itanns[itann]) < 0x43 ? -1 + : readUnsignedShort(itanns[itann] + 1); + } } if (labels[codeLength] != null) { mv.visitLabel(labels[codeLength]); @@ -1399,6 +1518,32 @@ public class ClassReader { } } + // visits the local variables type annotations + if (tanns != null) { + for (int i = 0; i < tanns.length; ++i) { + if ((readByte(tanns[i]) >> 1) == (0x40 >> 1)) { + int v = readAnnotationTarget(context, tanns[i]); + v = readAnnotationValues(v + 2, c, true, + mv.visitLocalVariableAnnotation(context.typeRef, + context.typePath, context.start, + context.end, context.index, readUTF8(v, c), + true)); + } + } + } + if (itanns != null) { + for (int i = 0; i < itanns.length; ++i) { + if ((readByte(itanns[i]) >> 1) == (0x40 >> 1)) { + int v = readAnnotationTarget(context, itanns[i]); + v = readAnnotationValues(v + 2, c, true, + mv.visitLocalVariableAnnotation(context.typeRef, + context.typePath, context.start, + context.end, context.index, readUTF8(v, c), + false)); + } + } + } + // visits the code attributes while (attributes != null) { Attribute attr = attributes.next; @@ -1411,25 +1556,176 @@ public class ClassReader { mv.visitMaxs(maxStack, maxLocals); } + /** + * Parses a type annotation table to find the labels, and to visit the try + * catch block annotations. + * + * @param u + * the start offset of a type annotation table. + * @param mv + * the method visitor to be used to visit the try catch block + * annotations. + * @param context + * information about the class being parsed. + * @param visible + * if the type annotation table to parse contains runtime visible + * annotations. + * @return the start offset of each type annotation in the parsed table. + */ + private int[] readTypeAnnotations(final MethodVisitor mv, + final Context context, int u, boolean visible) { + char[] c = context.buffer; + int[] offsets = new int[readUnsignedShort(u)]; + u += 2; + for (int i = 0; i < offsets.length; ++i) { + offsets[i] = u; + int target = readInt(u); + switch (target >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + u += 2; + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + u += 1; + break; + case 0x40: // LOCAL_VARIABLE + case 0x41: // RESOURCE_VARIABLE + for (int j = readUnsignedShort(u + 1); j > 0; --j) { + int start = readUnsignedShort(u + 3); + int length = readUnsignedShort(u + 5); + readLabel(start, context.labels); + readLabel(start + length, context.labels); + u += 6; + } + u += 3; + break; + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + u += 4; + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + u += 3; + break; + } + int pathLength = readByte(u); + if ((target >>> 24) == 0x42) { + TypePath path = pathLength == 0 ? null : new TypePath(b, u); + u += 1 + 2 * pathLength; + u = readAnnotationValues(u + 2, c, true, + mv.visitTryCatchAnnotation(target, path, + readUTF8(u, c), visible)); + } else { + u = readAnnotationValues(u + 3 + 2 * pathLength, c, true, null); + } + } + return offsets; + } + + /** + * Parses the header of a type annotation to extract its target_type and + * target_path (the result is stored in the given context), and returns the + * start offset of the rest of the type_annotation structure (i.e. the + * offset to the type_index field, which is followed by + * num_element_value_pairs and then the name,value pairs). + * + * @param context + * information about the class being parsed. This is where the + * extracted target_type and target_path must be stored. + * @param u + * the start offset of a type_annotation structure. + * @return the start offset of the rest of the type_annotation structure. + */ + private int readAnnotationTarget(final Context context, int u) { + int target = readInt(u); + switch (target >>> 24) { + case 0x00: // CLASS_TYPE_PARAMETER + case 0x01: // METHOD_TYPE_PARAMETER + case 0x16: // METHOD_FORMAL_PARAMETER + target &= 0xFFFF0000; + u += 2; + break; + case 0x13: // FIELD + case 0x14: // METHOD_RETURN + case 0x15: // METHOD_RECEIVER + target &= 0xFF000000; + u += 1; + break; + case 0x40: // LOCAL_VARIABLE + case 0x41: { // RESOURCE_VARIABLE + target &= 0xFF000000; + int n = readUnsignedShort(u + 1); + context.start = new Label[n]; + context.end = new Label[n]; + context.index = new int[n]; + u += 3; + for (int i = 0; i < n; ++i) { + int start = readUnsignedShort(u); + int length = readUnsignedShort(u + 2); + context.start[i] = readLabel(start, context.labels); + context.end[i] = readLabel(start + length, context.labels); + context.index[i] = readUnsignedShort(u + 4); + u += 6; + } + break; + } + case 0x47: // CAST + case 0x48: // CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + case 0x49: // METHOD_INVOCATION_TYPE_ARGUMENT + case 0x4A: // CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + case 0x4B: // METHOD_REFERENCE_TYPE_ARGUMENT + target &= 0xFF0000FF; + u += 4; + break; + // case 0x10: // CLASS_EXTENDS + // case 0x11: // CLASS_TYPE_PARAMETER_BOUND + // case 0x12: // METHOD_TYPE_PARAMETER_BOUND + // case 0x17: // THROWS + // case 0x42: // EXCEPTION_PARAMETER + // case 0x43: // INSTANCEOF + // case 0x44: // NEW + // case 0x45: // CONSTRUCTOR_REFERENCE + // case 0x46: // METHOD_REFERENCE + default: + target &= (target >>> 24) < 0x43 ? 0xFFFFFF00 : 0xFF000000; + u += 3; + break; + } + int pathLength = readByte(u); + context.typeRef = target; + context.typePath = pathLength == 0 ? null : new TypePath(b, u); + return u + 1 + 2 * pathLength; + } + /** * Reads parameter annotations and makes the given visitor visit them. * + * @param mv + * the visitor that must visit the annotations. + * @param context + * information about the class being parsed. * @param v * start offset in {@link #b b} of the annotations to be read. - * @param desc - * the method descriptor. - * @param buf - * buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or {@link #readConst - * readConst}. * @param visible * true if the annotations to be read are visible at * runtime. - * @param mv - * the visitor that must visit the annotations. */ - private void readParameterAnnotations(int v, final String desc, - final char[] buf, final boolean visible, final MethodVisitor mv) { + private void readParameterAnnotations(final MethodVisitor mv, + final Context context, int v, final boolean visible) { int i; int n = b[v++] & 0xFF; // workaround for a bug in javac (javac compiler generates a parameter @@ -1438,7 +1734,7 @@ public class ClassReader { // equal to the number of parameters in the method descriptor - which // includes the synthetic parameters added by the compiler). This work- // around supposes that the synthetic parameters are the first ones. - int synthetics = Type.getArgumentTypes(desc).length - n; + int synthetics = Type.getArgumentTypes(context.desc).length - n; AnnotationVisitor av; for (i = 0; i < synthetics; ++i) { // virtual annotation to detect synthetic parameters in MethodWriter @@ -1447,12 +1743,13 @@ public class ClassReader { av.visitEnd(); } } + char[] c = context.buffer; for (; i < n + synthetics; ++i) { int j = readUnsignedShort(v); v += 2; for (; j > 0; --j) { - av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible); - v = readAnnotationValues(v + 2, buf, true, av); + av = mv.visitParameterAnnotation(i, readUTF8(v, c), visible); + v = readAnnotationValues(v + 2, c, true, av); } } } @@ -1731,17 +2028,14 @@ public class ClassReader { * if the stack map frame at stackMap is compressed or not. * @param unzip * if the stack map frame must be uncompressed. - * @param labels - * the labels of the method currently being parsed, indexed by - * their offset. A new label for the parsed stack map frame is - * stored in this array if it does not already exist. * @param frame * where the parsed stack map frame must be stored. * @return the offset of the first byte following the parsed frame. */ private int readFrame(int stackMap, boolean zip, boolean unzip, - Label[] labels, Context frame) { + Context frame) { char[] c = frame.buffer; + Label[] labels = frame.labels; int tag; int delta; if (zip) { diff --git a/spring-core/src/main/java/org/springframework/asm/ClassVisitor.java b/spring-core/src/main/java/org/springframework/asm/ClassVisitor.java index ae02341eb4..4a495862d4 100644 --- a/spring-core/src/main/java/org/springframework/asm/ClassVisitor.java +++ b/spring-core/src/main/java/org/springframework/asm/ClassVisitor.java @@ -33,8 +33,9 @@ package org.springframework.asm; * A visitor to visit a Java class. The methods of this class must be called in * the following order: visit [ visitSource ] [ * visitOuterClass ] ( visitAnnotation | - * visitAttribute )* ( visitInnerClass | visitField | - * visitMethod )* visitEnd. + * visitTypeAnnotation | visitAttribute )* ( + * visitInnerClass | visitField | visitMethod )* + * visitEnd. * * @author Eric Bruneton */ @@ -42,7 +43,7 @@ public abstract class ClassVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -57,7 +58,7 @@ public abstract class ClassVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public ClassVisitor(final int api) { this(api, null); @@ -68,17 +69,15 @@ public abstract class ClassVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param cv * the class visitor to which this visitor must delegate method * calls. May be null. */ public ClassVisitor(final int api, final ClassVisitor cv) { - /* SPRING PATCH: REMOVED FOR CGLIB 3.0 COMPATIBILITY - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } - */ this.api = api; this.cv = cv; } @@ -170,6 +169,39 @@ public abstract class ClassVisitor { return null; } + /** + * Visits an annotation on a type in the class signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#CLASS_TYPE_PARAMETER + * CLASS_TYPE_PARAMETER}, + * {@link TypeReference#CLASS_TYPE_PARAMETER_BOUND + * CLASS_TYPE_PARAMETER_BOUND} or + * {@link TypeReference#CLASS_EXTENDS 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (cv != null) { + return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + /** * Visits a non standard attribute of the class. * diff --git a/spring-core/src/main/java/org/springframework/asm/ClassWriter.java b/spring-core/src/main/java/org/springframework/asm/ClassWriter.java index d7fbf52f4c..9fa132221f 100644 --- a/spring-core/src/main/java/org/springframework/asm/ClassWriter.java +++ b/spring-core/src/main/java/org/springframework/asm/ClassWriter.java @@ -416,6 +416,16 @@ public class ClassWriter extends ClassVisitor { */ private AnnotationWriter ianns; + /** + * The runtime visible type annotations of this class. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this class. + */ + private AnnotationWriter itanns; + /** * The non standard attributes of this class. */ @@ -595,7 +605,7 @@ public class ClassWriter extends ClassVisitor { * {@link #COMPUTE_FRAMES}. */ public ClassWriter(final int flags) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); index = 1; pool = new ByteVector(); items = new Item[256]; @@ -677,7 +687,8 @@ public class ClassWriter extends ClassVisitor { sourceFile = newUTF8(file); } if (debug != null) { - sourceDebug = new ByteVector().putUTF8(debug); + sourceDebug = new ByteVector().encodeUTF8(debug, 0, + Integer.MAX_VALUE); } } @@ -710,6 +721,29 @@ public class ClassWriter extends ClassVisitor { return aw; } + @Override + public final AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + @Override public final void visitAttribute(final Attribute attr) { attr.next = attrs; @@ -795,7 +829,7 @@ public class ClassWriter extends ClassVisitor { } if (sourceDebug != null) { ++attributeCount; - size += sourceDebug.length + 4; + size += sourceDebug.length + 6; newUTF8("SourceDebugExtension"); } if (enclosingMethodOwner != 0) { @@ -831,6 +865,16 @@ public class ClassWriter extends ClassVisitor { size += 8 + ianns.getSize(); newUTF8("RuntimeInvisibleAnnotations"); } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + size += 8 + tanns.getSize(); + newUTF8("RuntimeVisibleTypeAnnotations"); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + size += 8 + itanns.getSize(); + newUTF8("RuntimeInvisibleTypeAnnotations"); + } if (attrs != null) { attributeCount += attrs.getCount(); size += attrs.getSize(this, null, 0, -1, -1); @@ -874,9 +918,9 @@ public class ClassWriter extends ClassVisitor { out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); } if (sourceDebug != null) { - int len = sourceDebug.length - 2; + int len = sourceDebug.length; out.putShort(newUTF8("SourceDebugExtension")).putInt(len); - out.putByteArray(sourceDebug.data, 2, len); + out.putByteArray(sourceDebug.data, 0, len); } if (enclosingMethodOwner != 0) { out.putShort(newUTF8("EnclosingMethod")).putInt(4); @@ -904,6 +948,14 @@ public class ClassWriter extends ClassVisitor { out.putShort(newUTF8("RuntimeInvisibleAnnotations")); ianns.put(out); } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } if (attrs != null) { attrs.put(this, null, 0, -1, -1, out); } @@ -1590,7 +1642,7 @@ public class ClassWriter extends ClassVisitor { /** * Returns the common super type of the two given types. The default - * implementation of this method loads the two given classes and uses + * implementation of this method loads the two given classes and uses * the java.lang.Class methods to find the common super class. It can be * overridden to compute this common super type in other ways, in particular * without actually loading any class, or to take into account the class diff --git a/spring-core/src/main/java/org/springframework/asm/Context.java b/spring-core/src/main/java/org/springframework/asm/Context.java index e945a927a3..cab4e23f69 100644 --- a/spring-core/src/main/java/org/springframework/asm/Context.java +++ b/spring-core/src/main/java/org/springframework/asm/Context.java @@ -72,11 +72,46 @@ class Context { */ String desc; + /** + * The label objects, indexed by bytecode offset, of the method currently + * being parsed (only bytecode offsets for which a label is needed have a + * non null associated Label object). + */ + Label[] labels; + + /** + * The target of the type annotation currently being parsed. + */ + int typeRef; + + /** + * The path of the type annotation currently being parsed. + */ + TypePath typePath; + /** * The offset of the latest stack map frame that has been parsed. */ int offset; + /** + * The labels corresponding to the start of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] start; + + /** + * The labels corresponding to the end of the local variable ranges in the + * local variable type annotation currently being parsed. + */ + Label[] end; + + /** + * The local variable indices for each local variable range in the local + * variable type annotation currently being parsed. + */ + int[] index; + /** * The encoding of the latest stack map frame that has been parsed. */ diff --git a/spring-core/src/main/java/org/springframework/asm/FieldVisitor.java b/spring-core/src/main/java/org/springframework/asm/FieldVisitor.java index 929594fe7c..8863e668e4 100644 --- a/spring-core/src/main/java/org/springframework/asm/FieldVisitor.java +++ b/spring-core/src/main/java/org/springframework/asm/FieldVisitor.java @@ -31,8 +31,8 @@ package org.springframework.asm; /** * A visitor to visit a Java field. The methods of this class must be called in - * the following order: ( visitAnnotation | visitAttribute )* - * visitEnd. + * the following order: ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* visitEnd. * * @author Eric Bruneton */ @@ -40,7 +40,7 @@ public abstract class FieldVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -55,7 +55,7 @@ public abstract class FieldVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public FieldVisitor(final int api) { this(api, null); @@ -66,13 +66,13 @@ public abstract class FieldVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param fv * the field visitor to which this visitor must delegate method * calls. May be null. */ public FieldVisitor(final int api, final FieldVisitor fv) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; @@ -96,6 +96,35 @@ public abstract class FieldVisitor { return null; } + /** + * Visits an annotation on the type of the field. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#FIELD FIELD}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (fv != null) { + return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + /** * Visits a non standard attribute of the field. * diff --git a/spring-core/src/main/java/org/springframework/asm/FieldWriter.java b/spring-core/src/main/java/org/springframework/asm/FieldWriter.java index 3fcda54bba..f5a135e549 100644 --- a/spring-core/src/main/java/org/springframework/asm/FieldWriter.java +++ b/spring-core/src/main/java/org/springframework/asm/FieldWriter.java @@ -80,6 +80,17 @@ final class FieldWriter extends FieldVisitor { */ private AnnotationWriter ianns; + /** + * The runtime visible type annotations of this field. May be null. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this field. May be + * null. + */ + private AnnotationWriter itanns; + /** * The non standard attributes of this field. May be null. */ @@ -107,7 +118,7 @@ final class FieldWriter extends FieldVisitor { */ FieldWriter(final ClassWriter cw, final int access, final String name, final String desc, final String signature, final Object value) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); if (cw.firstField == null) { cw.firstField = this; } else { @@ -150,6 +161,29 @@ final class FieldWriter extends FieldVisitor { return aw; } + @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + if (!ClassReader.ANNOTATIONS) { + return null; + } + ByteVector bv = new ByteVector(); + // write target_type and target_info + AnnotationWriter.putTarget(typeRef, typePath, bv); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, + bv.length - 2); + if (visible) { + aw.next = tanns; + tanns = aw; + } else { + aw.next = itanns; + itanns = aw; + } + return aw; + } + @Override public void visitAttribute(final Attribute attr) { attr.next = attrs; @@ -198,6 +232,14 @@ final class FieldWriter extends FieldVisitor { cw.newUTF8("RuntimeInvisibleAnnotations"); size += 8 + ianns.getSize(); } + if (ClassReader.ANNOTATIONS && tanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + tanns.getSize(); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + itanns.getSize(); + } if (attrs != null) { size += attrs.getSize(cw, null, 0, -1, -1); } @@ -237,6 +279,12 @@ final class FieldWriter extends FieldVisitor { if (ClassReader.ANNOTATIONS && ianns != null) { ++attributeCount; } + if (ClassReader.ANNOTATIONS && tanns != null) { + ++attributeCount; + } + if (ClassReader.ANNOTATIONS && itanns != null) { + ++attributeCount; + } if (attrs != null) { attributeCount += attrs.getCount(); } @@ -266,6 +314,14 @@ final class FieldWriter extends FieldVisitor { out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); ianns.put(out); } + if (ClassReader.ANNOTATIONS && tanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + tanns.put(out); + } + if (ClassReader.ANNOTATIONS && itanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + itanns.put(out); + } if (attrs != null) { attrs.put(cw, null, 0, -1, -1, out); } diff --git a/spring-core/src/main/java/org/springframework/asm/Frame.java b/spring-core/src/main/java/org/springframework/asm/Frame.java index d34f2fada4..fcabe0d9df 100644 --- a/spring-core/src/main/java/org/springframework/asm/Frame.java +++ b/spring-core/src/main/java/org/springframework/asm/Frame.java @@ -70,8 +70,8 @@ final class Frame { * stack types. VALUE depends on KIND. For LOCAL types, it is an index in * the input local variable types. For STACK types, it is a position * relatively to the top of input frame stack. For BASE types, it is either - * one of the constants defined in FrameVisitor, or for OBJECT and - * UNINITIALIZED types, a tag and an index in the type table. + * one of the constants defined below, or for OBJECT and UNINITIALIZED + * types, a tag and an index in the type table. * * Output frames can contain types of any kind and with a positive or * negative dimension (and even unassigned types, represented by 0 - which @@ -585,7 +585,7 @@ final class Frame { } // pushes the type on the output stack outputStack[outputStackTop++] = type; - // updates the maximum height reached by the output stack, if needed + // updates the maximun height reached by the output stack, if needed int top = owner.inputStackTop + outputStackTop; if (top > owner.outputStackMax) { owner.outputStackMax = top; @@ -1417,6 +1417,7 @@ final class Frame { // if t is the NULL type, merge(u,t)=u, so there is no change return false; } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) { + // if t and u have the same dimension and same base kind if ((u & BASE_KIND) == OBJECT) { // if t is also a reference type, and if u and t have the // same dimension merge(u,t) = dim(t) | common parent of the @@ -1429,9 +1430,13 @@ final class Frame { v = OBJECT | cw.addType("java/lang/Object"); } } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) { - // if t is any other reference or array type, - // merge(u,t)=java/lang/Object - v = OBJECT | cw.addType("java/lang/Object"); + // if t is any other reference or array type, the merged type + // is Object, or min(dim(u), dim(t)) | java/lang/Object is u + // and t have different array dimensions + int tdim = t & DIM; + int udim = u & DIM; + v = (udim != tdim ? Math.min(tdim, udim) : 0) | OBJECT + | cw.addType("java/lang/Object"); } else { // if t is any other type, merge(u,t)=TOP v = TOP; 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 4dabda6959..fc64fe23b2 100644 --- a/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java +++ b/spring-core/src/main/java/org/springframework/asm/MethodVisitor.java @@ -31,18 +31,24 @@ package org.springframework.asm; /** * A visitor to visit a Java method. The methods of this class must be called in - * the following order: [ visitAnnotationDefault ] ( - * visitAnnotation | visitParameterAnnotation | - * visitAttribute )* [ visitCode ( visitFrame | - * visitXInsn | visitLabel | - * visitTryCatchBlock | visitLocalVariable | + * the following order: ( visitParameter )* [ + * visitAnnotationDefault ] ( visitAnnotation | + * visitTypeAnnotation | visitAttribute )* [ + * visitCode ( visitFrame | visitXInsn | + * visitLabel | visitInsnAnnotation | + * visitTryCatchBlock | visitTryCatchBlockAnnotation | + * visitLocalVariable | visitLocalVariableAnnotation | * visitLineNumber )* visitMaxs ] visitEnd. In - * addition, the visitXInsn and visitLabel methods - * must be called in the sequential order of the bytecode instructions of the - * visited code, visitTryCatchBlock must be called before the - * labels passed as arguments have been visited, and the - * visitLocalVariable and visitLineNumber methods must be - * called after the labels passed as arguments have been visited. + * addition, the visitXInsn and visitLabel methods must + * be called in the sequential order of the bytecode instructions of the visited + * code, visitInsnAnnotation must be called after the annotated + * instruction, visitTryCatchBlock must be called before the + * labels passed as arguments have been visited, + * visitTryCatchBlockAnnotation must be called after the + * corresponding try catch block has been visited, and the + * visitLocalVariable, visitLocalVariableAnnotation and + * visitLineNumber methods must be called after the labels + * passed as arguments have been visited. * * @author Eric Bruneton */ @@ -50,7 +56,7 @@ public abstract class MethodVisitor { /** * The ASM API version implemented by this visitor. The value of this field - * must be one of {@link Opcodes#ASM4}. + * must be one of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ protected final int api; @@ -65,7 +71,7 @@ public abstract class MethodVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. */ public MethodVisitor(final int api) { this(api, null); @@ -76,13 +82,13 @@ public abstract class MethodVisitor { * * @param api * the ASM API version implemented by this visitor. Must be one - * of {@link Opcodes#ASM4}. + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param mv * the method visitor to which this visitor must delegate method * calls. May be null. */ public MethodVisitor(final int api, final MethodVisitor mv) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; @@ -90,9 +96,28 @@ public abstract class MethodVisitor { } // ------------------------------------------------------------------------- - // Annotations and non standard attributes + // Parameters, annotations and non standard attributes // ------------------------------------------------------------------------- + /** + * Visits a parameter of this method. + * + * @param name + * parameter name or null if none is provided. + * @param access + * the parameter's access flags, only ACC_FINAL, + * ACC_SYNTHETIC or/and ACC_MANDATED are + * allowed (see {@link Opcodes}). + */ + public void visitParameter(String name, int access) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + mv.visitParameter(name, access); + } + } + /** * Visits the default value of this annotation interface method. * @@ -127,6 +152,42 @@ public abstract class MethodVisitor { return null; } + /** + * Visits an annotation on a type in the method signature. + * + * @param typeRef + * a reference to the annotated type. The sort of this type + * reference must be {@link TypeReference#METHOD_TYPE_PARAMETER + * METHOD_TYPE_PARAMETER}, + * {@link TypeReference#METHOD_TYPE_PARAMETER_BOUND + * METHOD_TYPE_PARAMETER_BOUND}, + * {@link TypeReference#METHOD_RETURN METHOD_RETURN}, + * {@link TypeReference#METHOD_RECEIVER METHOD_RECEIVER}, + * {@link TypeReference#METHOD_FORMAL_PARAMETER + * METHOD_FORMAL_PARAMETER} or {@link TypeReference#THROWS + * THROWS}. 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 + * null if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + if (api < Opcodes.ASM5) { + throw new RuntimeException(); + } + if (mv != null) { + return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); + } + return null; + } + /** * Visits an annotation of a parameter this method. * @@ -201,9 +262,11 @@ public abstract class MethodVisitor { *
  • {@link Opcodes#F_CHOP} representing frame with current locals are the * same as the locals in the previous frame, except that the last 1-3 locals * are absent and with the empty stack (nLocals is 1, 2 or 3).
  • - *
  • {@link Opcodes#F_FULL} representing complete frame data.
  • + *
  • {@link Opcodes#F_FULL} representing complete frame data.
  • * - *
    + * + * + *
    * In both cases the first frame, corresponding to the method's parameters * and access flags, is implicit and must not be visited. Also, it is * illegal to visit two or more frames for the same code location (i.e., at @@ -376,13 +439,52 @@ public abstract class MethodVisitor { * @param desc * the method's descriptor (see {@link Type Type}). */ + @Deprecated public void visitMethodInsn(int opcode, String owner, String name, String desc) { + if (api >= Opcodes.ASM5) { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, desc, itf); + return; + } if (mv != null) { mv.visitMethodInsn(opcode, owner, name, desc); } } + /** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode + * the opcode of the type instruction to be visited. This opcode + * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link Type#getInternalName() getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link Type Type}). + * @param itf + * if the method's owner class is an interface. + */ + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { + throw new IllegalArgumentException( + "INVOKESPECIAL/STATIC on interfaces require ASM 5"); + } + visitMethodInsn(opcode, owner, name, desc); + return; + } + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + /** * Visits an invokedynamic instruction. * @@ -449,7 +551,7 @@ public abstract class MethodVisitor { * constant types, implementations of this method should check for * unexpected constant types, like this: * - *
    +     * 
          * if (cst instanceof Integer) {
          *     // ...
          * } else if (cst instanceof Float) {
    @@ -558,6 +660,48 @@ public abstract class MethodVisitor {
             }
         }
     
    +    /**
    +     * Visits an annotation on an instruction. This method must be called just
    +     * after the annotated instruction. It can be called several times
    +     * for the same instruction.
    +     * 
    +     * @param typeRef
    +     *            a reference to the annotated type. The sort of this type
    +     *            reference must be {@link TypeReference#INSTANCEOF INSTANCEOF},
    +     *            {@link TypeReference#NEW NEW},
    +     *            {@link TypeReference#CONSTRUCTOR_REFERENCE
    +     *            CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE
    +     *            METHOD_REFERENCE}, {@link TypeReference#CAST CAST},
    +     *            {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
    +     *            CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
    +     *            {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT
    +     *            METHOD_INVOCATION_TYPE_ARGUMENT},
    +     *            {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
    +     *            CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
    +     *            {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT
    +     *            METHOD_REFERENCE_TYPE_ARGUMENT}. 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
    +     *            null if the annotation targets 'typeRef' as a whole.
    +     * @param desc
    +     *            the class descriptor of the annotation class.
    +     * @param visible
    +     *            true if the annotation is visible at runtime.
    +     * @return a visitor to visit the annotation values, or null if
    +     *         this visitor is not interested in visiting this annotation.
    +     */
    +    public AnnotationVisitor visitInsnAnnotation(int typeRef,
    +            TypePath typePath, String desc, boolean visible) {
    +        if (api < Opcodes.ASM5) {
    +            throw new RuntimeException();
    +        }
    +        if (mv != null) {
    +            return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
    +        }
    +        return null;
    +    }
    +
         // -------------------------------------------------------------------------
         // Exceptions table entries, debug information, max stack and max locals
         // -------------------------------------------------------------------------
    @@ -586,6 +730,38 @@ public abstract class MethodVisitor {
             }
         }
     
    +    /**
    +     * Visits an annotation on an exception handler type. This method must be
    +     * called after the {@link #visitTryCatchBlock} for the annotated
    +     * exception handler. It can be called several times for the same exception
    +     * handler.
    +     * 
    +     * @param typeRef
    +     *            a reference to the annotated type. The sort of this type
    +     *            reference must be {@link TypeReference#EXCEPTION_PARAMETER
    +     *            EXCEPTION_PARAMETER}. 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
    +     *            null if the annotation targets 'typeRef' as a whole.
    +     * @param desc
    +     *            the class descriptor of the annotation class.
    +     * @param visible
    +     *            true if the annotation is visible at runtime.
    +     * @return a visitor to visit the annotation values, or null if
    +     *         this visitor is not interested in visiting this annotation.
    +     */
    +    public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
    +            TypePath typePath, String desc, boolean visible) {
    +        if (api < Opcodes.ASM5) {
    +            throw new RuntimeException();
    +        }
    +        if (mv != null) {
    +            return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible);
    +        }
    +        return null;
    +    }
    +
         /**
          * Visits a local variable declaration.
          * 
    @@ -616,6 +792,48 @@ public abstract class MethodVisitor {
             }
         }
     
    +    /**
    +     * Visits an annotation on a local variable type.
    +     * 
    +     * @param typeRef
    +     *            a reference to the annotated type. The sort of this type
    +     *            reference must be {@link TypeReference#LOCAL_VARIABLE
    +     *            LOCAL_VARIABLE} or {@link TypeReference#RESOURCE_VARIABLE
    +     *            RESOURCE_VARIABLE}. 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
    +     *            null if the annotation targets 'typeRef' as a whole.
    +     * @param start
    +     *            the fist instructions corresponding to the continuous ranges
    +     *            that make the scope of this local variable (inclusive).
    +     * @param end
    +     *            the last instructions corresponding to the continuous ranges
    +     *            that make the scope of this local variable (exclusive). This
    +     *            array must have the same size as the 'start' array.
    +     * @param index
    +     *            the local variable's index in each range. This array must have
    +     *            the same size as the 'start' array.
    +     * @param desc
    +     *            the class descriptor of the annotation class.
    +     * @param visible
    +     *            true if the annotation is visible at runtime.
    +     * @return a visitor to visit the annotation values, or null if
    +     *         this visitor is not interested in visiting this annotation.
    +     */
    +    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
    +            TypePath typePath, Label[] start, Label[] end, int[] index,
    +            String desc, boolean visible) {
    +        if (api < Opcodes.ASM5) {
    +            throw new RuntimeException();
    +        }
    +        if (mv != null) {
    +            return mv.visitLocalVariableAnnotation(typeRef, typePath, start,
    +                    end, index, desc, visible);
    +        }
    +        return null;
    +    }
    +
         /**
          * Visits a line number declaration.
          * 
    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 0616dc9e78..d3fdc7e2f5 100644
    --- a/spring-core/src/main/java/org/springframework/asm/MethodWriter.java
    +++ b/spring-core/src/main/java/org/springframework/asm/MethodWriter.java
    @@ -191,6 +191,18 @@ class MethodWriter extends MethodVisitor {
          */
         private AnnotationWriter ianns;
     
    +    /**
    +     * The runtime visible type annotations of this method. May be null
    +     * .
    +     */
    +    private AnnotationWriter tanns;
    +
    +    /**
    +     * The runtime invisible type annotations of this method. May be
    +     * null.
    +     */
    +    private AnnotationWriter itanns;
    +
         /**
          * The runtime visible parameter annotations of this method. May be
          * null.
    @@ -282,6 +294,16 @@ class MethodWriter extends MethodVisitor {
          */
         private Handler lastHandler;
     
    +    /**
    +     * Number of entries in the MethodParameters attribute.
    +     */
    +    private int methodParametersCount;
    +
    +    /**
    +     * The MethodParameters attribute.
    +     */
    +    private ByteVector methodParameters;
    +
         /**
          * Number of entries in the LocalVariableTable attribute.
          */
    @@ -312,6 +334,21 @@ class MethodWriter extends MethodVisitor {
          */
         private ByteVector lineNumber;
     
    +    /**
    +     * The start offset of the last visited instruction.
    +     */
    +    private int lastCodeOffset;
    +
    +    /**
    +     * The runtime visible type annotations of the code. May be null.
    +     */
    +    private AnnotationWriter ctanns;
    +
    +    /**
    +     * The runtime invisible type annotations of the code. May be null.
    +     */
    +    private AnnotationWriter ictanns;
    +
         /**
          * The non standard attributes of the method's code.
          */
    @@ -416,7 +453,7 @@ class MethodWriter extends MethodVisitor {
                 final String desc, final String signature,
                 final String[] exceptions, final boolean computeMaxs,
                 final boolean computeFrames) {
    -        super(Opcodes.ASM4);
    +        super(Opcodes.ASM5);
             if (cw.firstMethod == null) {
                 cw.firstMethod = this;
             } else {
    @@ -461,6 +498,16 @@ class MethodWriter extends MethodVisitor {
         // Implementation of the MethodVisitor abstract class
         // ------------------------------------------------------------------------
     
    +    @Override
    +    public void visitParameter(String name, int access) {
    +        if (methodParameters == null) {
    +            methodParameters = new ByteVector();
    +        }
    +        ++methodParametersCount;
    +        methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name))
    +                .putShort(access);
    +    }
    +
         @Override
         public AnnotationVisitor visitAnnotationDefault() {
             if (!ClassReader.ANNOTATIONS) {
    @@ -490,6 +537,29 @@ class MethodWriter extends MethodVisitor {
             return aw;
         }
     
    +    @Override
    +    public AnnotationVisitor visitTypeAnnotation(final int typeRef,
    +            final TypePath typePath, final String desc, final boolean visible) {
    +        if (!ClassReader.ANNOTATIONS) {
    +            return null;
    +        }
    +        ByteVector bv = new ByteVector();
    +        // write target_type and target_info
    +        AnnotationWriter.putTarget(typeRef, typePath, bv);
    +        // write type, and reserve space for values count
    +        bv.putShort(cw.newUTF8(desc)).putShort(0);
    +        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
    +                bv.length - 2);
    +        if (visible) {
    +            aw.next = tanns;
    +            tanns = aw;
    +        } else {
    +            aw.next = itanns;
    +            itanns = aw;
    +        }
    +        return aw;
    +    }
    +
         @Override
         public AnnotationVisitor visitParameterAnnotation(final int parameter,
                 final String desc, final boolean visible) {
    @@ -642,6 +712,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitInsn(final int opcode) {
    +        lastCodeOffset = code.length;
             // adds the instruction to the bytecode of the method
             code.putByte(opcode);
             // update currentBlock
    @@ -667,6 +738,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitIntInsn(final int opcode, final int operand) {
    +        lastCodeOffset = code.length;
             // Label currentBlock = this.currentBlock;
             if (currentBlock != null) {
                 if (compute == FRAMES) {
    @@ -691,6 +763,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitVarInsn(final int opcode, final int var) {
    +        lastCodeOffset = code.length;
             // Label currentBlock = this.currentBlock;
             if (currentBlock != null) {
                 if (compute == FRAMES) {
    @@ -749,6 +822,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitTypeInsn(final int opcode, final String type) {
    +        lastCodeOffset = code.length;
             Item i = cw.newClassItem(type);
             // Label currentBlock = this.currentBlock;
             if (currentBlock != null) {
    @@ -771,6 +845,7 @@ class MethodWriter extends MethodVisitor {
         @Override
         public void visitFieldInsn(final int opcode, final String owner,
                 final String name, final String desc) {
    +        lastCodeOffset = code.length;
             Item i = cw.newFieldItem(owner, name, desc);
             // Label currentBlock = this.currentBlock;
             if (currentBlock != null) {
    @@ -808,8 +883,8 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitMethodInsn(final int opcode, final String owner,
    -            final String name, final String desc) {
    -        boolean itf = opcode == Opcodes.INVOKEINTERFACE;
    +            final String name, final String desc, final boolean itf) {
    +        lastCodeOffset = code.length;
             Item i = cw.newMethodItem(owner, name, desc, itf);
             int argSize = i.intVal;
             // Label currentBlock = this.currentBlock;
    @@ -847,7 +922,7 @@ class MethodWriter extends MethodVisitor {
                 }
             }
             // adds the instruction to the bytecode of the method
    -        if (itf) {
    +        if (opcode == Opcodes.INVOKEINTERFACE) {
                 if (argSize == 0) {
                     argSize = Type.getArgumentsAndReturnSizes(desc);
                     i.intVal = argSize;
    @@ -861,6 +936,7 @@ class MethodWriter extends MethodVisitor {
         @Override
         public void visitInvokeDynamicInsn(final String name, final String desc,
                 final Handle bsm, final Object... bsmArgs) {
    +        lastCodeOffset = code.length;
             Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs);
             int argSize = i.intVal;
             // Label currentBlock = this.currentBlock;
    @@ -900,6 +976,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitJumpInsn(final int opcode, final Label label) {
    +        lastCodeOffset = code.length;
             Label nextInsn = null;
             // Label currentBlock = this.currentBlock;
             if (currentBlock != null) {
    @@ -1045,6 +1122,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitLdcInsn(final Object cst) {
    +        lastCodeOffset = code.length;
             Item i = cw.newConstItem(cst);
             // Label currentBlock = this.currentBlock;
             if (currentBlock != null) {
    @@ -1078,6 +1156,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitIincInsn(final int var, final int increment) {
    +        lastCodeOffset = code.length;
             if (currentBlock != null) {
                 if (compute == FRAMES) {
                     currentBlock.frame.execute(Opcodes.IINC, var, null, null);
    @@ -1102,6 +1181,7 @@ class MethodWriter extends MethodVisitor {
         @Override
         public void visitTableSwitchInsn(final int min, final int max,
                 final Label dflt, final Label... labels) {
    +        lastCodeOffset = code.length;
             // adds the instruction to the bytecode of the method
             int source = code.length;
             code.putByte(Opcodes.TABLESWITCH);
    @@ -1118,6 +1198,7 @@ class MethodWriter extends MethodVisitor {
         @Override
         public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
                 final Label[] labels) {
    +        lastCodeOffset = code.length;
             // adds the instruction to the bytecode of the method
             int source = code.length;
             code.putByte(Opcodes.LOOKUPSWITCH);
    @@ -1160,6 +1241,7 @@ class MethodWriter extends MethodVisitor {
     
         @Override
         public void visitMultiANewArrayInsn(final String desc, final int dims) {
    +        lastCodeOffset = code.length;
             Item i = cw.newClassItem(desc);
             // Label currentBlock = this.currentBlock;
             if (currentBlock != null) {
    @@ -1175,6 +1257,30 @@ class MethodWriter extends MethodVisitor {
             code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
         }
     
    +    @Override
    +    public AnnotationVisitor visitInsnAnnotation(int typeRef,
    +            TypePath typePath, String desc, boolean visible) {
    +        if (!ClassReader.ANNOTATIONS) {
    +            return null;
    +        }
    +        ByteVector bv = new ByteVector();
    +        // write target_type and target_info
    +        typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8);
    +        AnnotationWriter.putTarget(typeRef, typePath, bv);
    +        // write type, and reserve space for values count
    +        bv.putShort(cw.newUTF8(desc)).putShort(0);
    +        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
    +                bv.length - 2);
    +        if (visible) {
    +            aw.next = ctanns;
    +            ctanns = aw;
    +        } else {
    +            aw.next = ictanns;
    +            ictanns = aw;
    +        }
    +        return aw;
    +    }
    +
         @Override
         public void visitTryCatchBlock(final Label start, final Label end,
                 final Label handler, final String type) {
    @@ -1193,6 +1299,29 @@ class MethodWriter extends MethodVisitor {
             lastHandler = h;
         }
     
    +    @Override
    +    public AnnotationVisitor visitTryCatchAnnotation(int typeRef,
    +            TypePath typePath, String desc, boolean visible) {
    +        if (!ClassReader.ANNOTATIONS) {
    +            return null;
    +        }
    +        ByteVector bv = new ByteVector();
    +        // write target_type and target_info
    +        AnnotationWriter.putTarget(typeRef, typePath, bv);
    +        // write type, and reserve space for values count
    +        bv.putShort(cw.newUTF8(desc)).putShort(0);
    +        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
    +                bv.length - 2);
    +        if (visible) {
    +            aw.next = ctanns;
    +            ctanns = aw;
    +        } else {
    +            aw.next = ictanns;
    +            ictanns = aw;
    +        }
    +        return aw;
    +    }
    +
         @Override
         public void visitLocalVariable(final String name, final String desc,
                 final String signature, final Label start, final Label end,
    @@ -1225,6 +1354,41 @@ class MethodWriter extends MethodVisitor {
             }
         }
     
    +    @Override
    +    public AnnotationVisitor visitLocalVariableAnnotation(int typeRef,
    +            TypePath typePath, Label[] start, Label[] end, int[] index,
    +            String desc, boolean visible) {
    +        if (!ClassReader.ANNOTATIONS) {
    +            return null;
    +        }
    +        ByteVector bv = new ByteVector();
    +        // write target_type and target_info
    +        bv.putByte(typeRef >>> 24).putShort(start.length);
    +        for (int i = 0; i < start.length; ++i) {
    +            bv.putShort(start[i].position)
    +                    .putShort(end[i].position - start[i].position)
    +                    .putShort(index[i]);
    +        }
    +        if (typePath == null) {
    +            bv.putByte(0);
    +        } else {
    +            int length = typePath.b[typePath.offset] * 2 + 1;
    +            bv.putByteArray(typePath.b, typePath.offset, length);
    +        }
    +        // write type, and reserve space for values count
    +        bv.putShort(cw.newUTF8(desc)).putShort(0);
    +        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
    +                bv.length - 2);
    +        if (visible) {
    +            aw.next = ctanns;
    +            ctanns = aw;
    +        } else {
    +            aw.next = ictanns;
    +            ictanns = aw;
    +        }
    +        return aw;
    +    }
    +
         @Override
         public void visitLineNumber(final int line, final Label start) {
             if (lineNumber == null) {
    @@ -1890,6 +2054,14 @@ class MethodWriter extends MethodVisitor {
                     cw.newUTF8(zip ? "StackMapTable" : "StackMap");
                     size += 8 + stackMap.length;
                 }
    +            if (ClassReader.ANNOTATIONS && ctanns != null) {
    +                cw.newUTF8("RuntimeVisibleTypeAnnotations");
    +                size += 8 + ctanns.getSize();
    +            }
    +            if (ClassReader.ANNOTATIONS && ictanns != null) {
    +                cw.newUTF8("RuntimeInvisibleTypeAnnotations");
    +                size += 8 + ictanns.getSize();
    +            }
                 if (cattrs != null) {
                     size += cattrs.getSize(cw, code.data, code.length, maxStack,
                             maxLocals);
    @@ -1915,6 +2087,10 @@ class MethodWriter extends MethodVisitor {
                 cw.newUTF8(signature);
                 size += 8;
             }
    +        if (methodParameters != null) {
    +            cw.newUTF8("MethodParameters");
    +            size += 7 + methodParameters.length;
    +        }
             if (ClassReader.ANNOTATIONS && annd != null) {
                 cw.newUTF8("AnnotationDefault");
                 size += 6 + annd.length;
    @@ -1927,6 +2103,14 @@ class MethodWriter extends MethodVisitor {
                 cw.newUTF8("RuntimeInvisibleAnnotations");
                 size += 8 + ianns.getSize();
             }
    +        if (ClassReader.ANNOTATIONS && tanns != null) {
    +            cw.newUTF8("RuntimeVisibleTypeAnnotations");
    +            size += 8 + tanns.getSize();
    +        }
    +        if (ClassReader.ANNOTATIONS && itanns != null) {
    +            cw.newUTF8("RuntimeInvisibleTypeAnnotations");
    +            size += 8 + itanns.getSize();
    +        }
             if (ClassReader.ANNOTATIONS && panns != null) {
                 cw.newUTF8("RuntimeVisibleParameterAnnotations");
                 size += 7 + 2 * (panns.length - synthetics);
    @@ -1983,6 +2167,9 @@ class MethodWriter extends MethodVisitor {
             if (ClassReader.SIGNATURES && signature != null) {
                 ++attributeCount;
             }
    +        if (methodParameters != null) {
    +            ++attributeCount;
    +        }
             if (ClassReader.ANNOTATIONS && annd != null) {
                 ++attributeCount;
             }
    @@ -1992,6 +2179,12 @@ class MethodWriter extends MethodVisitor {
             if (ClassReader.ANNOTATIONS && ianns != null) {
                 ++attributeCount;
             }
    +        if (ClassReader.ANNOTATIONS && tanns != null) {
    +            ++attributeCount;
    +        }
    +        if (ClassReader.ANNOTATIONS && itanns != null) {
    +            ++attributeCount;
    +        }
             if (ClassReader.ANNOTATIONS && panns != null) {
                 ++attributeCount;
             }
    @@ -2016,6 +2209,12 @@ class MethodWriter extends MethodVisitor {
                 if (stackMap != null) {
                     size += 8 + stackMap.length;
                 }
    +            if (ClassReader.ANNOTATIONS && ctanns != null) {
    +                size += 8 + ctanns.getSize();
    +            }
    +            if (ClassReader.ANNOTATIONS && ictanns != null) {
    +                size += 8 + ictanns.getSize();
    +            }
                 if (cattrs != null) {
                     size += cattrs.getSize(cw, code.data, code.length, maxStack,
                             maxLocals);
    @@ -2045,6 +2244,12 @@ class MethodWriter extends MethodVisitor {
                 if (stackMap != null) {
                     ++attributeCount;
                 }
    +            if (ClassReader.ANNOTATIONS && ctanns != null) {
    +                ++attributeCount;
    +            }
    +            if (ClassReader.ANNOTATIONS && ictanns != null) {
    +                ++attributeCount;
    +            }
                 if (cattrs != null) {
                     attributeCount += cattrs.getCount();
                 }
    @@ -2070,6 +2275,14 @@ class MethodWriter extends MethodVisitor {
                     out.putInt(stackMap.length + 2).putShort(frameCount);
                     out.putByteArray(stackMap.data, 0, stackMap.length);
                 }
    +            if (ClassReader.ANNOTATIONS && ctanns != null) {
    +                out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
    +                ctanns.put(out);
    +            }
    +            if (ClassReader.ANNOTATIONS && ictanns != null) {
    +                out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
    +                ictanns.put(out);
    +            }
                 if (cattrs != null) {
                     cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
                 }
    @@ -2095,6 +2308,12 @@ class MethodWriter extends MethodVisitor {
                 out.putShort(cw.newUTF8("Signature")).putInt(2)
                         .putShort(cw.newUTF8(signature));
             }
    +        if (methodParameters != null) {
    +            out.putShort(cw.newUTF8("MethodParameters"));
    +            out.putInt(methodParameters.length + 1).putByte(
    +                    methodParametersCount);
    +            out.putByteArray(methodParameters.data, 0, methodParameters.length);
    +        }
             if (ClassReader.ANNOTATIONS && annd != null) {
                 out.putShort(cw.newUTF8("AnnotationDefault"));
                 out.putInt(annd.length);
    @@ -2108,6 +2327,14 @@ class MethodWriter extends MethodVisitor {
                 out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
                 ianns.put(out);
             }
    +        if (ClassReader.ANNOTATIONS && tanns != null) {
    +            out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
    +            tanns.put(out);
    +        }
    +        if (ClassReader.ANNOTATIONS && itanns != null) {
    +            out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
    +            itanns.put(out);
    +        }
             if (ClassReader.ANNOTATIONS && panns != null) {
                 out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
                 AnnotationWriter.put(panns, synthetics, out);
    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 665ac94cf5..1f4f4825e4 100644
    --- a/spring-core/src/main/java/org/springframework/asm/Opcodes.java
    +++ b/spring-core/src/main/java/org/springframework/asm/Opcodes.java
    @@ -46,6 +46,7 @@ public interface Opcodes {
         // ASM API versions
     
         int ASM4 = 4 << 16 | 0 << 8 | 0;
    +    int ASM5 = 5 << 16 | 0 << 8 | 0;
     
         // versions
     
    @@ -56,6 +57,7 @@ public interface Opcodes {
         int V1_5 = 0 << 16 | 49;
         int V1_6 = 0 << 16 | 50;
         int V1_7 = 0 << 16 | 51;
    +    int V1_8 = 0 << 16 | 52;
     
         // access flags
     
    @@ -63,7 +65,7 @@ public interface Opcodes {
         int ACC_PRIVATE = 0x0002; // class, field, method
         int ACC_PROTECTED = 0x0004; // class, field, method
         int ACC_STATIC = 0x0008; // field, method
    -    int ACC_FINAL = 0x0010; // class, field, method
    +    int ACC_FINAL = 0x0010; // class, field, method, parameter
         int ACC_SUPER = 0x0020; // class
         int ACC_SYNCHRONIZED = 0x0020; // method
         int ACC_VOLATILE = 0x0040; // field
    @@ -74,9 +76,10 @@ public interface Opcodes {
         int ACC_INTERFACE = 0x0200; // class
         int ACC_ABSTRACT = 0x0400; // class, method
         int ACC_STRICT = 0x0800; // method
    -    int ACC_SYNTHETIC = 0x1000; // class, field, method
    +    int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter
         int ACC_ANNOTATION = 0x2000; // class
         int ACC_ENUM = 0x4000; // class(?) field inner
    +    int ACC_MANDATED = 0x8000; // parameter
     
         // ASM specific pseudo access flags
     
    diff --git a/spring-core/src/main/java/org/springframework/asm/SpringAsmInfo.java b/spring-core/src/main/java/org/springframework/asm/SpringAsmInfo.java
    index 1413be7292..3a8eb3f120 100644
    --- a/spring-core/src/main/java/org/springframework/asm/SpringAsmInfo.java
    +++ b/spring-core/src/main/java/org/springframework/asm/SpringAsmInfo.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2013 the original author or authors.
    + * Copyright 2002-2014 the original author or authors.
      *
      * Licensed under the Apache License, Version 2.0 (the "License");
      * you may not use this file except in compliance with the License.
    @@ -18,9 +18,9 @@ package org.springframework.asm;
     
     /**
      * Utility class exposing constants related to Spring's internal repackaging
    - * of the ASM bytecode manipulation library.
    + * of the ASM bytecode manipulation library (currently based on version 5.0).
      *
    - * 

    See package-level javadoc for more + *

    See package-level javadocs for more * information on {@code org.springframework.asm}. * * @author Chris Beams @@ -30,9 +30,9 @@ public final class SpringAsmInfo { /** * The ASM compatibility version for Spring's ASM visitor implementations: - * as of Spring 3.2, it's ASM4. To be updated once we upgrade to ASM 5.0. - * @see Opcodes#ASM4 + * as of Spring 4.0.3, it's ASM5. + * @see Opcodes#ASM5 */ - public static final int ASM_VERSION = Opcodes.ASM4; + public static final int ASM_VERSION = Opcodes.ASM5; } diff --git a/spring-core/src/main/java/org/springframework/asm/TypePath.java b/spring-core/src/main/java/org/springframework/asm/TypePath.java new file mode 100644 index 0000000000..af5140b7cc --- /dev/null +++ b/spring-core/src/main/java/org/springframework/asm/TypePath.java @@ -0,0 +1,193 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 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; + +/** + * The path to a type argument, wildcard bound, array element type, or static + * inner type within an enclosing type. + * + * @author Eric Bruneton + */ +public class TypePath { + + /** + * A type path step that steps into the element type of an array type. See + * {@link #getStep getStep}. + */ + public final static int ARRAY_ELEMENT = 0; + + /** + * A type path step that steps into the nested type of a class type. See + * {@link #getStep getStep}. + */ + public final static int INNER_TYPE = 1; + + /** + * A type path step that steps into the bound of a wildcard type. See + * {@link #getStep getStep}. + */ + public final static int WILDCARD_BOUND = 2; + + /** + * A type path step that steps into a type argument of a generic type. See + * {@link #getStep getStep}. + */ + public final static int TYPE_ARGUMENT = 3; + + /** + * The byte array where the path is stored, in Java class file format. + */ + byte[] b; + + /** + * The offset of the first byte of the type path in 'b'. + */ + int offset; + + /** + * Creates a new type path. + * + * @param b + * the byte array containing the type path in Java class file + * format. + * @param offset + * the offset of the first byte of the type path in 'b'. + */ + TypePath(byte[] b, int offset) { + this.b = b; + this.offset = offset; + } + + /** + * Returns the length of this path. + * + * @return the length of this path. + */ + public int getLength() { + return b[offset]; + } + + /** + * Returns the value of the given step of this path. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE + * INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + */ + public int getStep(int index) { + return b[offset + 2 * index + 1]; + } + + /** + * Returns the index of the type argument that the given step is stepping + * into. This method should only be used for steps whose value is + * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}. + * + * @param index + * an index between 0 and {@link #getLength()}, exclusive. + * @return the index of the type argument that the given step is stepping + * into. + */ + public int getStepArgument(int index) { + return b[offset + 2 * index + 2]; + } + + /** + * Converts a type path in string form, in the format used by + * {@link #toString()}, into a TypePath object. + * + * @param typePath + * a type path in string form, in the format used by + * {@link #toString()}. May be null or empty. + * @return the corresponding TypePath object, or null if the path is empty. + */ + public static TypePath fromString(final String typePath) { + if (typePath == null || typePath.length() == 0) { + return null; + } + int n = typePath.length(); + ByteVector out = new ByteVector(n); + out.putByte(0); + for (int i = 0; i < n;) { + char c = typePath.charAt(i++); + if (c == '[') { + out.put11(ARRAY_ELEMENT, 0); + } else if (c == '.') { + out.put11(INNER_TYPE, 0); + } else if (c == '*') { + out.put11(WILDCARD_BOUND, 0); + } else if (c >= '0' && c <= '9') { + int typeArg = c - '0'; + while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') { + typeArg = typeArg * 10 + c - '0'; + i += 1; + } + out.put11(TYPE_ARGUMENT, typeArg); + } + } + out.data[0] = (byte) (out.length / 2); + return new TypePath(out.data, 0); + } + + /** + * Returns a string representation of this type path. {@link #ARRAY_ELEMENT + * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE + * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps + * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type + * argument index in decimal form. + */ + @Override + public String toString() { + int length = getLength(); + StringBuilder result = new StringBuilder(length * 2); + for (int i = 0; i < length; ++i) { + switch (getStep(i)) { + case ARRAY_ELEMENT: + result.append('['); + break; + case INNER_TYPE: + result.append('.'); + break; + case WILDCARD_BOUND: + result.append('*'); + break; + case TYPE_ARGUMENT: + result.append(getStepArgument(i)); + break; + default: + result.append('_'); + } + } + return result.toString(); + } +} diff --git a/spring-core/src/main/java/org/springframework/asm/TypeReference.java b/spring-core/src/main/java/org/springframework/asm/TypeReference.java new file mode 100644 index 0000000000..a71492831b --- /dev/null +++ b/spring-core/src/main/java/org/springframework/asm/TypeReference.java @@ -0,0 +1,452 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2013 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 reference to a type appearing in a class, field or method declaration, or + * on an instruction. Such a reference designates the part of the class where + * the referenced type is appearing (e.g. an 'extends', 'implements' or 'throws' + * clause, a 'new' instruction, a 'catch' clause, a type cast, a local variable + * declaration, etc). + * + * @author Eric Bruneton + */ +public class TypeReference { + + /** + * The sort of type references that target a type parameter of a generic + * class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER = 0x00; + + /** + * The sort of type references that target a type parameter of a generic + * method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER = 0x01; + + /** + * The sort of type references that target the super class of a class or one + * of the interfaces it implements. See {@link #getSort getSort}. + */ + public final static int CLASS_EXTENDS = 0x10; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic class. See {@link #getSort getSort}. + */ + public final static int CLASS_TYPE_PARAMETER_BOUND = 0x11; + + /** + * The sort of type references that target a bound of a type parameter of a + * generic method. See {@link #getSort getSort}. + */ + public final static int METHOD_TYPE_PARAMETER_BOUND = 0x12; + + /** + * The sort of type references that target the type of a field. See + * {@link #getSort getSort}. + */ + public final static int FIELD = 0x13; + + /** + * The sort of type references that target the return type of a method. See + * {@link #getSort getSort}. + */ + public final static int METHOD_RETURN = 0x14; + + /** + * The sort of type references that target the receiver type of a method. + * See {@link #getSort getSort}. + */ + public final static int METHOD_RECEIVER = 0x15; + + /** + * The sort of type references that target the type of a formal parameter of + * a method. See {@link #getSort getSort}. + */ + public final static int METHOD_FORMAL_PARAMETER = 0x16; + + /** + * The sort of type references that target the type of an exception declared + * in the throws clause of a method. See {@link #getSort getSort}. + */ + public final static int THROWS = 0x17; + + /** + * The sort of type references that target the type of a local variable in a + * method. See {@link #getSort getSort}. + */ + public final static int LOCAL_VARIABLE = 0x40; + + /** + * The sort of type references that target the type of a resource variable + * in a method. See {@link #getSort getSort}. + */ + public final static int RESOURCE_VARIABLE = 0x41; + + /** + * The sort of type references that target the type of the exception of a + * 'catch' clause in a method. See {@link #getSort getSort}. + */ + public final static int EXCEPTION_PARAMETER = 0x42; + + /** + * The sort of type references that target the type declared in an + * 'instanceof' instruction. See {@link #getSort getSort}. + */ + public final static int INSTANCEOF = 0x43; + + /** + * The sort of type references that target the type of the object created by + * a 'new' instruction. See {@link #getSort getSort}. + */ + public final static int NEW = 0x44; + + /** + * The sort of type references that target the receiver type of a + * constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE = 0x45; + + /** + * The sort of type references that target the receiver type of a method + * reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE = 0x46; + + /** + * The sort of type references that target the type declared in an explicit + * or implicit cast instruction. See {@link #getSort getSort}. + */ + public final static int CAST = 0x47; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor call. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method call. See {@link #getSort getSort}. + */ + public final static int METHOD_INVOCATION_TYPE_ARGUMENT = 0x49; + + /** + * The sort of type references that target a type parameter of a generic + * constructor in a constructor reference. See {@link #getSort getSort}. + */ + public final static int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT = 0x4A; + + /** + * The sort of type references that target a type parameter of a generic + * method in a method reference. See {@link #getSort getSort}. + */ + public final static int METHOD_REFERENCE_TYPE_ARGUMENT = 0x4B; + + /** + * The type reference value in Java class file format. + */ + private int value; + + /** + * Creates a new TypeReference. + * + * @param typeRef + * the int encoded value of the type reference, as received in a + * visit method related to type annotations, like + * visitTypeAnnotation. + */ + public TypeReference(int typeRef) { + this.value = typeRef; + } + + /** + * Returns a type reference of the given sort. + * + * @param sort + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, or + * {@link #METHOD_REFERENCE METHOD_REFERENCE}. + * @return a type reference of the given sort. + */ + public static TypeReference newTypeReference(int sort) { + return new TypeReference(sort << 24); + } + + /** + * Returns a reference to a type parameter of a generic class or method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @return a reference to the given generic class or method type parameter. + */ + public static TypeReference newTypeParameterReference(int sort, + int paramIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16)); + } + + /** + * Returns a reference to a type parameter bound of a generic class or + * method. + * + * @param sort + * {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER} or + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}. + * @param paramIndex + * the type parameter index. + * @param boundIndex + * the type bound index within the above type parameters. + * @return a reference to the given generic class or method type parameter + * bound. + */ + public static TypeReference newTypeParameterBoundReference(int sort, + int paramIndex, int boundIndex) { + return new TypeReference((sort << 24) | (paramIndex << 16) + | (boundIndex << 8)); + } + + /** + * Returns a reference to the super class or to an interface of the + * 'implements' clause of a class. + * + * @param itfIndex + * the index of an interface in the 'implements' clause of a + * class, or -1 to reference the super class of the class. + * @return a reference to the given super type of a class. + */ + public static TypeReference newSuperTypeReference(int itfIndex) { + itfIndex &= 0xFFFF; + return new TypeReference((CLASS_EXTENDS << 24) | (itfIndex << 8)); + } + + /** + * Returns a reference to the type of a formal parameter of a method. + * + * @param paramIndex + * the formal parameter index. + * + * @return a reference to the type of the given method formal parameter. + */ + public static TypeReference newFormalParameterReference(int paramIndex) { + return new TypeReference((METHOD_FORMAL_PARAMETER << 24) + | (paramIndex << 16)); + } + + /** + * Returns a reference to the type of an exception, in a 'throws' clause of + * a method. + * + * @param exceptionIndex + * the index of an exception in a 'throws' clause of a method. + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newExceptionReference(int exceptionIndex) { + return new TypeReference((THROWS << 24) | (exceptionIndex << 8)); + } + + /** + * Returns a reference to the type of the exception declared in a 'catch' + * clause of a method. + * + * @param tryCatchBlockIndex + * the index of a try catch block (using the order in which they + * are visited with visitTryCatchBlock). + * + * @return a reference to the type of the given exception. + */ + public static TypeReference newTryCatchReference(int tryCatchBlockIndex) { + return new TypeReference((EXCEPTION_PARAMETER << 24) + | (tryCatchBlockIndex << 8)); + } + + /** + * Returns a reference to the type of a type argument in a constructor or + * method call or reference. + * + * @param sort + * {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + * @param argIndex + * the type argument index. + * + * @return a reference to the type of the given type argument. + */ + public static TypeReference newTypeArgumentReference(int sort, int argIndex) { + return new TypeReference((sort << 24) | argIndex); + } + + /** + * Returns the sort of this type reference. + * + * @return {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_EXTENDS CLASS_EXTENDS}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND}, + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}, + * {@link #FIELD FIELD}, {@link #METHOD_RETURN METHOD_RETURN}, + * {@link #METHOD_RECEIVER METHOD_RECEIVER}, + * {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}, + * {@link #THROWS THROWS}, {@link #LOCAL_VARIABLE LOCAL_VARIABLE}, + * {@link #RESOURCE_VARIABLE RESOURCE_VARIABLE}, + * {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER}, + * {@link #INSTANCEOF INSTANCEOF}, {@link #NEW NEW}, + * {@link #CONSTRUCTOR_REFERENCE CONSTRUCTOR_REFERENCE}, + * {@link #METHOD_REFERENCE METHOD_REFERENCE}, {@link #CAST CAST}, + * {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT + * METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT + * METHOD_REFERENCE_TYPE_ARGUMENT}. + */ + public int getSort() { + return value >>> 24; + } + + /** + * Returns the index of the type parameter referenced by this type + * reference. This method must only be used for type references whose sort + * is {@link #CLASS_TYPE_PARAMETER CLASS_TYPE_PARAMETER}, + * {@link #METHOD_TYPE_PARAMETER METHOD_TYPE_PARAMETER}, + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter index. + */ + public int getTypeParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the type parameter bound, within the type parameter + * {@link #getTypeParameterIndex}, referenced by this type reference. This + * method must only be used for type references whose sort is + * {@link #CLASS_TYPE_PARAMETER_BOUND CLASS_TYPE_PARAMETER_BOUND} or + * {@link #METHOD_TYPE_PARAMETER_BOUND METHOD_TYPE_PARAMETER_BOUND}. + * + * @return a type parameter bound index. + */ + public int getTypeParameterBoundIndex() { + return (value & 0x0000FF00) >> 8; + } + + /** + * Returns the index of the "super type" of a class that is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #CLASS_EXTENDS CLASS_EXTENDS}. + * + * @return the index of an interface in the 'implements' clause of a class, + * or -1 if this type reference references the type of the super + * class. + */ + public int getSuperTypeIndex() { + return (short) ((value & 0x00FFFF00) >> 8); + } + + /** + * Returns the index of the formal parameter whose type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #METHOD_FORMAL_PARAMETER METHOD_FORMAL_PARAMETER}. + * + * @return a formal parameter index. + */ + public int getFormalParameterIndex() { + return (value & 0x00FF0000) >> 16; + } + + /** + * Returns the index of the exception, in a 'throws' clause of a method, + * whose type is referenced by this type reference. This method must only be + * used for type references whose sort is {@link #THROWS THROWS}. + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getExceptionIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the try catch block (using the order in which they + * are visited with visitTryCatchBlock), whose 'catch' type is referenced by + * this type reference. This method must only be used for type references + * whose sort is {@link #EXCEPTION_PARAMETER EXCEPTION_PARAMETER} . + * + * @return the index of an exception in the 'throws' clause of a method. + */ + public int getTryCatchBlockIndex() { + return (value & 0x00FFFF00) >> 8; + } + + /** + * Returns the index of the type argument referenced by this type reference. + * This method must only be used for type references whose sort is + * {@link #CAST CAST}, {@link #CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + * CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT}, + * {@link #METHOD_INVOCATION_TYPE_ARGUMENT METHOD_INVOCATION_TYPE_ARGUMENT}, + * {@link #CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + * CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or + * {@link #METHOD_REFERENCE_TYPE_ARGUMENT METHOD_REFERENCE_TYPE_ARGUMENT}. + * + * @return a type parameter index. + */ + public int getTypeArgumentIndex() { + return value & 0xFF; + } + + /** + * Returns the int encoded value of this type reference, suitable for use in + * visit methods related to type annotations, like visitTypeAnnotation. + * + * @return the int encoded value of this type reference. + */ + public int getValue() { + return value; + } +} diff --git a/spring-core/src/main/java/org/springframework/asm/package-info.java b/spring-core/src/main/java/org/springframework/asm/package-info.java index 842143aa51..9ced53eaa3 100644 --- a/spring-core/src/main/java/org/springframework/asm/package-info.java +++ b/spring-core/src/main/java/org/springframework/asm/package-info.java @@ -1,28 +1,16 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ /** - * Spring's repackaging of org.objectweb.asm 4 (for - * internal use only). + * Spring's repackaging of + * org.objectweb.asm 5.0 + * (for internal use only). + * *

    This repackaging technique avoids any potential conflicts with - * dependencies on ASM at the application level or from other third-party + * dependencies on ASM at the application level or from third-party * libraries and frameworks. - *

    As this repackaging happens at the classfile level, sources and Javadoc - * are not available here. See the original ObjectWeb - * ASM 4 Javadoc + * + *

    As this repackaging happens at the class file level, sources + * and javadocs are not available here. See the original ObjectWeb + * ASM 5.0 javadocs * for details when working with these classes. * * @since 3.2 diff --git a/spring-core/src/main/java/org/springframework/asm/util/TraceClassVisitor.java b/spring-core/src/main/java/org/springframework/asm/util/TraceClassVisitor.java deleted file mode 100644 index bf5900fbd1..0000000000 --- a/spring-core/src/main/java/org/springframework/asm/util/TraceClassVisitor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.asm.util; - -import java.io.PrintWriter; - -import org.springframework.asm.ClassVisitor; -import org.springframework.asm.SpringAsmInfo; - -/** - * Dummy implementation of missing TraceClassVisitor from cglib-nodep's internally - * repackaged ASM library, added to avoid NoClassDefFoundErrors. - * - * @author Chris Beams - * @since 3.2 - */ -public class TraceClassVisitor extends ClassVisitor { - - public TraceClassVisitor(Object object, PrintWriter pw) { - super(SpringAsmInfo.ASM_VERSION); - } - -} diff --git a/spring-core/src/main/java/org/springframework/asm/util/package-info.java b/spring-core/src/main/java/org/springframework/asm/util/package-info.java deleted file mode 100644 index f03a2e8d87..0000000000 --- a/spring-core/src/main/java/org/springframework/asm/util/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Dummy implementations of asm-util classes (for internal use only). - * - * @since 3.2 - */ -package org.springframework.asm.util; diff --git a/spring-core/src/main/java/org/springframework/cglib/SpringCglibInfo.java b/spring-core/src/main/java/org/springframework/cglib/SpringCglibInfo.java index 0ed64ca52e..6922898bb6 100644 --- a/spring-core/src/main/java/org/springframework/cglib/SpringCglibInfo.java +++ b/spring-core/src/main/java/org/springframework/cglib/SpringCglibInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,10 @@ package org.springframework.cglib; /** - * Empty class used to ensure that the {@code org.springframework.cglib} package is - * processed during Javadoc generation. + * Empty class used to ensure that the {@code org.springframework.cglib} + * package is processed during javadoc generation. * - *

    See package-level Javadoc for more + *

    See package-level javadocs for more * information on {@code org.springframework.cglib}. * * @author Chris Beams diff --git a/spring-core/src/main/java/org/springframework/cglib/package-info.java b/spring-core/src/main/java/org/springframework/cglib/package-info.java index a7f25ce2a6..6246aae82b 100644 --- a/spring-core/src/main/java/org/springframework/cglib/package-info.java +++ b/spring-core/src/main/java/org/springframework/cglib/package-info.java @@ -1,28 +1,16 @@ -/* - * Copyright 2002-2012 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ /** - * Spring's repackaging of net.sf.cglib 3 (for - * internal use only). + * Spring's repackaging of + * net.sf.cglib 3.1 + * (for internal use only). + * *

    This repackaging technique avoids any potential conflicts with - * dependencies on CGLIB at the application level or from other third-party + * dependencies on CGLIB at the application level or from third-party * libraries and frameworks. - *

    As this repackaging happens at the classfile level, sources and Javadoc - * are not available here. See the original - * CGLIB 3 Javadoc + * + *

    As this repackaging happens at the class file level, sources + * and javadocs are not available here. See the original + * CGLIB 3.1 javadocs * for details when working with these classes. * * @since 3.2