diff options
169 files changed, 5720 insertions, 1557 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 703c5add42..ffacbf0442 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -181,6 +181,10 @@ filter { { matchName="scala.reflect.api.Internals#ReificationSupportApi.SyntacticExistentialType" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.SynchronizedOps.newNestedScope" + problemName=MissingMethodProblem } ] } diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index ca8f7468eb..3cd985aeae 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -230,6 +230,10 @@ filter { { matchName="scala.reflect.runtime.JavaMirrors#JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$followStatic" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.SynchronizedOps.newNestedScope" + problemName=MissingMethodProblem } ] } diff --git a/build-ant-macros.xml b/build-ant-macros.xml index bd0e723b9b..609f106d09 100644 --- a/build-ant-macros.xml +++ b/build-ant-macros.xml @@ -467,6 +467,11 @@ <filter token="SCALA_FULL_VERSION" value="${scala.full.version}"/> <filter token="SCALA_COMPILER_DOC_VERSION" value="${scala-compiler-doc.version.number}"/> <filter token="SCALA_COMPILER_INTERACTIVE_VERSION" value="${scala-compiler-interactive.version.number}"/> + <filter token="XML_VERSION" value="${scala-xml.version.number}" /> + <filter token="PARSER_COMBINATORS_VERSION" value="${scala-parser-combinators.version.number}" /> + <filter token="CONTINUATIONS_PLUGIN_VERSION" value="${scala-continuations-plugin.version.number}" /> + <filter token="CONTINUATIONS_LIBRARY_VERSION" value="${scala-continuations-library.version.number}" /> + <filter token="SCALA_SWING_VERSION" value="${scala-swing.version.number}" /> </filterset> </copy> <bnd classpath="${@{project}.jar}" eclipse="false" failok="false" exceptions="true" files="${build-osgi.dir}/${@{project}.name}.bnd" output="${build-osgi.dir}"/> diff --git a/build.number b/build.number index 63a55339b9..4a0898529e 100644 --- a/build.number +++ b/build.number @@ -1,7 +1,7 @@ #Tue Sep 11 19:21:09 CEST 2007 version.major=2 version.minor=11 -version.patch=1 +version.patch=2 # This is the -N part of a version. if it's 0, it's dropped from maven versions. version.bnum=0 diff --git a/spec/03-types.md b/spec/03-types.md index 66ddee8b7e..4a02c7a8c1 100644 --- a/spec/03-types.md +++ b/spec/03-types.md @@ -352,8 +352,7 @@ An infix type $T_1$ `op` $T_2$ consists of an infix operator `op` which gets applied to two type operands $T_1$ and $T_2$. The type is equivalent to the type application `op`$[T_1, T_2]$. The infix operator `op` may be an -arbitrary identifier, except for `*`, which is reserved as a postfix modifier -denoting a [repeated parameter type](04-basic-declarations-and-definitions.html#repeated-parameters). +arbitrary identifier. All type infix operators have the same precedence; parentheses have to be used for grouping. The [associativity](06-expressions.html#prefix-infix-and-postfix-operations) diff --git a/src/asm/README b/src/asm/README new file mode 100644 index 0000000000..3ceac88098 --- /dev/null +++ b/src/asm/README @@ -0,0 +1,30 @@ +Version 5.0.2, SVN r1741, tags/ASM_5_0_2 + +Git SVN repo: https://github.com/lrytz/asm + - git svn howto: https://github.com/lrytz/asm/issues/1 + +Upgrading ASM +------------- + +Start by deleting all source files in src/asm/ and copy the ones from the latest ASM release. + +Excluded Files (don't copy): + - package.html files + - org/objectweb/asm/commons + - org/objectweb/asm/optimizer + - org/objectweb/asm/xml + +Re-packaging and cosmetic changes: + - convert line endings (there are some CRLF) + find src/asm/scala/tools/asm -name '*.java' | xargs dos2unix + - change package clauses + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/package org\.objectweb\.asm/package scala.tools.asm/' + - update imports + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/import org\.objectweb\.asm/import scala.tools.asm/' + - update @links, @associates + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/@link org\.objectweb\.asm/@link scala.tools.asm/' + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/@associates org\.objectweb\.asm/@associates scala.tools.asm/' + - remove trailing whitespace + find src/asm/scala/tools/asm -name '*.java' | xargs sed -i '' -e 's/[ ]*$//' + +Actual changes: check the git log for [asm-cherry-pick] after the previous upgrade. diff --git a/src/asm/scala/tools/asm/AnnotationVisitor.java b/src/asm/scala/tools/asm/AnnotationVisitor.java index c806ca71e8..abcaf1d6d1 100644 --- a/src/asm/scala/tools/asm/AnnotationVisitor.java +++ b/src/asm/scala/tools/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/src/asm/scala/tools/asm/AnnotationWriter.java b/src/asm/scala/tools/asm/AnnotationWriter.java index 8eb5b2ef48..6de74ce041 100644 --- a/src/asm/scala/tools/asm/AnnotationWriter.java +++ b/src/asm/scala/tools/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 + * <tt>null</tt> 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/src/asm/scala/tools/asm/ByteVector.java b/src/asm/scala/tools/asm/ByteVector.java index 2bc63eb384..3bca7af12a 100644 --- a/src/asm/scala/tools/asm/ByteVector.java +++ b/src/asm/scala/tools/asm/ByteVector.java @@ -204,11 +204,14 @@ public class ByteVector { * automatically enlarged if necessary. * * @param s - * a String. + * a String whose UTF8 encoded length must be less than 65536. * @return this byte vector. */ public ByteVector putUTF8(final String s) { int charLength = s.length(); + if (charLength > 65535) { + throw new IllegalArgumentException(); + } int len = length; if (len + 2 + charLength > data.length) { enlarge(2 + charLength); @@ -227,38 +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; - } - } - 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/src/asm/scala/tools/asm/ClassReader.java b/src/asm/scala/tools/asm/ClassReader.java index cc655c1b62..8b0e12cb04 100644 --- a/src/asm/scala/tools/asm/ClassReader.java +++ b/src/asm/scala/tools/asm/ClassReader.java @@ -166,7 +166,7 @@ public class ClassReader { public ClassReader(final byte[] b, final int off, final int len) { this.b = b; // checks the class version - if (readShort(off + 6) > Opcodes.V1_7) { + if (readShort(off + 6) > Opcodes.V1_8) { throw new IllegalArgumentException(); } // parses the constant pool @@ -557,6 +557,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; @@ -581,6 +583,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)) { @@ -592,6 +597,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++) { @@ -626,7 +634,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, @@ -639,6 +647,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) { @@ -697,6 +721,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; @@ -718,8 +744,14 @@ public class ClassReader { && "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); @@ -739,7 +771,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, @@ -752,6 +784,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) { @@ -782,9 +830,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 @@ -792,8 +840,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; @@ -818,24 +869,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); @@ -849,8 +908,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; } @@ -894,6 +953,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(); @@ -914,11 +980,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 @@ -931,9 +1013,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); } @@ -966,7 +1045,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; @@ -1049,6 +1128,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; @@ -1089,6 +1174,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; @@ -1211,7 +1306,7 @@ public class ClassReader { } } if (frameCount > 0) { - stackMap = readFrame(stackMap, zip, unzip, labels, frame); + stackMap = readFrame(stackMap, zip, unzip, frame); --frameCount; } else { frame = null; @@ -1310,6 +1405,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); @@ -1317,7 +1413,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; @@ -1358,6 +1454,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]); @@ -1397,6 +1516,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; @@ -1410,24 +1555,175 @@ public class ClassReader { } /** + * 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 * <tt>true</tt> 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 @@ -1436,7 +1732,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 @@ -1445,12 +1741,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); } } } @@ -1729,17 +2026,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/src/asm/scala/tools/asm/ClassVisitor.java b/src/asm/scala/tools/asm/ClassVisitor.java index 3fc364d5e5..48dc2ca6ae 100644 --- a/src/asm/scala/tools/asm/ClassVisitor.java +++ b/src/asm/scala/tools/asm/ClassVisitor.java @@ -33,8 +33,9 @@ package scala.tools.asm; * A visitor to visit a Java class. The methods of this class must be called in * the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [ * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> | - * <tt>visitAttribute</tt> )* ( <tt>visitInnerClass</tt> | <tt>visitField</tt> | - * <tt>visitMethod</tt> )* <tt>visitEnd</tt>. + * <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* ( + * <tt>visitInnerClass</tt> | <tt>visitField</tt> | <tt>visitMethod</tt> )* + * <tt>visitEnd</tt>. * * @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,13 +69,13 @@ 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) { - if (api != Opcodes.ASM4) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { throw new IllegalArgumentException(); } this.api = api; @@ -169,6 +170,39 @@ public abstract class ClassVisitor { } /** + * 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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or <tt>null</tt> 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. * * @param attr diff --git a/src/asm/scala/tools/asm/ClassWriter.java b/src/asm/scala/tools/asm/ClassWriter.java index 93ed7313c7..5c2de3f982 100644 --- a/src/asm/scala/tools/asm/ClassWriter.java +++ b/src/asm/scala/tools/asm/ClassWriter.java @@ -417,6 +417,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. */ private Attribute attrs; @@ -477,12 +487,12 @@ public class ClassWriter extends ClassVisitor { * <tt>true</tt> if the maximum stack size and number of local variables * must be automatically computed. */ - private final boolean computeMaxs; + private boolean computeMaxs; /** * <tt>true</tt> if the stack map frames must be recomputed from scratch. */ - private final boolean computeFrames; + private boolean computeFrames; /** * <tt>true</tt> if the stack map tables of this class are invalid. The @@ -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); } } @@ -711,6 +722,29 @@ public class ClassWriter extends ClassVisitor { } @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; attrs = attr; @@ -722,11 +756,29 @@ public class ClassWriter extends ClassVisitor { if (innerClasses == null) { innerClasses = new ByteVector(); } - ++innerClassesCount; - innerClasses.putShort(name == null ? 0 : newClass(name)); - innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); - innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); - innerClasses.putShort(access); + // Sec. 4.7.6 of the JVMS states "Every CONSTANT_Class_info entry in the + // constant_pool table which represents a class or interface C that is + // not a package member must have exactly one corresponding entry in the + // classes array". To avoid duplicates we keep track in the intVal field + // of the Item of each CONSTANT_Class_info entry C whether an inner + // class entry has already been added for C (this field is unused for + // class entries, and changing its value does not change the hashcode + // and equality tests). If so we store the index of this inner class + // entry (plus one) in intVal. This hack allows duplicate detection in + // O(1) time. + Item nameItem = newClassItem(name); + if (nameItem.intVal == 0) { + ++innerClassesCount; + innerClasses.putShort(nameItem.index); + innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); + innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); + innerClasses.putShort(access); + nameItem.intVal = innerClassesCount; + } else { + // Compare the inner classes entry nameItem.intVal - 1 with the + // arguments of this method and throw an exception if there is a + // difference? + } } @Override @@ -795,7 +847,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 +883,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 +936,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,13 +966,34 @@ 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); } if (invalidFrames) { - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES); - return cw.toByteArray(); + anns = null; + ianns = null; + attrs = null; + innerClassesCount = 0; + innerClasses = null; + bootstrapMethodsCount = 0; + bootstrapMethods = null; + firstField = null; + lastField = null; + firstMethod = null; + lastMethod = null; + computeMaxs = false; + computeFrames = true; + invalidFrames = false; + new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES); + return toByteArray(); } return out.data; } @@ -1577,7 +1660,7 @@ public class ClassWriter extends ClassVisitor { /** * Returns the common super type of the two given types. The default - * implementation of this method <i>loads<i> the two given classes and uses + * implementation of this method <i>loads</i> 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 @@ -1664,6 +1747,15 @@ public class ClassWriter extends ClassVisitor { } /** + * Find item that whose index is `index`. + */ + public Item findItemByIndex(int index) { + int i = 0; + while (i < items.length && (items[i] == null || items[i].index != index)) i++; + return items[i]; + } + + /** * Puts one byte and two shorts into the constant pool. * * @param b diff --git a/src/asm/scala/tools/asm/Context.java b/src/asm/scala/tools/asm/Context.java index 7b3a2ad9dd..24546969e3 100644 --- a/src/asm/scala/tools/asm/Context.java +++ b/src/asm/scala/tools/asm/Context.java @@ -73,11 +73,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. */ int mode; diff --git a/src/asm/scala/tools/asm/CustomAttr.java b/src/asm/scala/tools/asm/CustomAttr.java index 22b5d287b7..5ecfd283d0 100644 --- a/src/asm/scala/tools/asm/CustomAttr.java +++ b/src/asm/scala/tools/asm/CustomAttr.java @@ -1,5 +1,5 @@ /* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL + * Copyright 2005-2012 LAMP/EPFL */ package scala.tools.asm; diff --git a/src/asm/scala/tools/asm/FieldVisitor.java b/src/asm/scala/tools/asm/FieldVisitor.java index 9171f331e5..708c1d322e 100644 --- a/src/asm/scala/tools/asm/FieldVisitor.java +++ b/src/asm/scala/tools/asm/FieldVisitor.java @@ -31,8 +31,8 @@ package scala.tools.asm; /** * A visitor to visit a Java field. The methods of this class must be called in - * the following order: ( <tt>visitAnnotation</tt> | <tt>visitAttribute</tt> )* - * <tt>visitEnd</tt>. + * the following order: ( <tt>visitAnnotation</tt> | + * <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* <tt>visitEnd</tt>. * * @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; @@ -97,6 +97,35 @@ public abstract class FieldVisitor { } /** + * 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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or <tt>null</tt> 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. * * @param attr diff --git a/src/asm/scala/tools/asm/FieldWriter.java b/src/asm/scala/tools/asm/FieldWriter.java index 02c6059b91..e640a8d406 100644 --- a/src/asm/scala/tools/asm/FieldWriter.java +++ b/src/asm/scala/tools/asm/FieldWriter.java @@ -81,6 +81,17 @@ final class FieldWriter extends FieldVisitor { private AnnotationWriter ianns; /** + * The runtime visible type annotations of this field. May be <tt>null</tt>. + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this field. May be + * <tt>null</tt>. + */ + private AnnotationWriter itanns; + + /** * The non standard attributes of this field. May be <tt>null</tt>. */ private Attribute attrs; @@ -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 { @@ -151,6 +162,29 @@ final class FieldWriter extends FieldVisitor { } @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; attrs = attr; @@ -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/src/asm/scala/tools/asm/Frame.java b/src/asm/scala/tools/asm/Frame.java index bcc3e8450b..85ad3269ab 100644 --- a/src/asm/scala/tools/asm/Frame.java +++ b/src/asm/scala/tools/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 @@ -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 @@ -1425,13 +1426,21 @@ final class Frame { | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); } else { // if u and t are array types, but not with the same element - // type, merge(u,t)=java/lang/Object - v = OBJECT | cw.addType("java/lang/Object"); + // type, merge(u,t) = dim(u) - 1 | java/lang/Object + int vdim = ELEMENT_OF + (u & DIM); + v = vdim | 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 min(udim, tdim) | java/lang/Object, where udim is the + // array dimension of u, minus 1 if u is an array type with a + // primitive element type (and similarly for tdim). + int tdim = (((t & DIM) == 0 || (t & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (t & DIM); + int udim = (((u & DIM) == 0 || (u & BASE_KIND) == OBJECT) ? 0 + : ELEMENT_OF) + (u & DIM); + v = Math.min(tdim, udim) | OBJECT + | cw.addType("java/lang/Object"); } else { // if t is any other type, merge(u,t)=TOP v = TOP; diff --git a/src/asm/scala/tools/asm/Handle.java b/src/asm/scala/tools/asm/Handle.java index 5dd06a54b9..cf12bb7613 100644 --- a/src/asm/scala/tools/asm/Handle.java +++ b/src/asm/scala/tools/asm/Handle.java @@ -49,7 +49,8 @@ public final class Handle { final int tag; /** - * The internal name of the field or method designed by this handle. + * The internal name of the class that owns the field or method designated + * by this handle. */ final String owner; @@ -76,8 +77,8 @@ public final class Handle { * {@link Opcodes#H_NEWINVOKESPECIAL} or * {@link Opcodes#H_INVOKEINTERFACE}. * @param owner - * the internal name of the field or method designed by this - * handle. + * the internal name of the class that owns the field or method + * designated by this handle. * @param name * the name of the field or method designated by this handle. * @param desc @@ -106,9 +107,11 @@ public final class Handle { } /** - * Returns the internal name of the field or method designed by this handle. + * Returns the internal name of the class that owns the field or method + * designated by this handle. * - * @return the internal name of the field or method designed by this handle. + * @return the internal name of the class that owns the field or method + * designated by this handle. */ public String getOwner() { return owner; diff --git a/src/asm/scala/tools/asm/Item.java b/src/asm/scala/tools/asm/Item.java index 94195a1082..4693f5ae99 100644 --- a/src/asm/scala/tools/asm/Item.java +++ b/src/asm/scala/tools/asm/Item.java @@ -208,9 +208,10 @@ final class Item { this.strVal2 = strVal2; this.strVal3 = strVal3; switch (type) { + case ClassWriter.CLASS: + this.intVal = 0; // intVal of a class must be zero, see visitInnerClass case ClassWriter.UTF8: case ClassWriter.STR: - case ClassWriter.CLASS: case ClassWriter.MTYPE: case ClassWriter.TYPE_NORMAL: hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); diff --git a/src/asm/scala/tools/asm/MethodVisitor.java b/src/asm/scala/tools/asm/MethodVisitor.java index e43ca97823..bddc325020 100644 --- a/src/asm/scala/tools/asm/MethodVisitor.java +++ b/src/asm/scala/tools/asm/MethodVisitor.java @@ -31,18 +31,24 @@ package scala.tools.asm; /** * A visitor to visit a Java method. The methods of this class must be called in - * the following order: [ <tt>visitAnnotationDefault</tt> ] ( - * <tt>visitAnnotation</tt> | <tt>visitParameterAnnotation</tt> | - * <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> | - * <tt>visit</tt><i>X</i>Insn</tt> | <tt>visitLabel</tt> | - * <tt>visitTryCatchBlock</tt> | <tt>visitLocalVariable</tt> | + * the following order: ( <tt>visitParameter</tt> )* [ + * <tt>visitAnnotationDefault</tt> ] ( <tt>visitAnnotation</tt> | + * <tt>visitTypeAnnotation</tt> | <tt>visitAttribute</tt> )* [ + * <tt>visitCode</tt> ( <tt>visitFrame</tt> | <tt>visit<i>X</i>Insn</tt> | + * <tt>visitLabel</tt> | <tt>visitInsnAnnotation</tt> | + * <tt>visitTryCatchBlock</tt> | <tt>visitTryCatchBlockAnnotation</tt> | + * <tt>visitLocalVariable</tt> | <tt>visitLocalVariableAnnotation</tt> | * <tt>visitLineNumber</tt> )* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In - * addition, the <tt>visit</tt><i>X</i>Insn</tt> and <tt>visitLabel</tt> methods - * must be called in the sequential order of the bytecode instructions of the - * visited code, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the - * labels passed as arguments have been visited, and the - * <tt>visitLocalVariable</tt> and <tt>visitLineNumber</tt> methods must be - * called <i>after</i> the labels passed as arguments have been visited. + * addition, the <tt>visit<i>X</i>Insn</tt> and <tt>visitLabel</tt> methods must + * be called in the sequential order of the bytecode instructions of the visited + * code, <tt>visitInsnAnnotation</tt> must be called <i>after</i> the annotated + * instruction, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the + * labels passed as arguments have been visited, + * <tt>visitTryCatchBlockAnnotation</tt> must be called <i>after</i> the + * corresponding try catch block has been visited, and the + * <tt>visitLocalVariable</tt>, <tt>visitLocalVariableAnnotation</tt> and + * <tt>visitLineNumber</tt> methods must be called <i>after</i> 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,10 +96,29 @@ 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 <tt>ACC_FINAL</tt>, + * <tt>ACC_SYNTHETIC</tt> or/and <tt>ACC_MANDATED</tt> 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. * * @return a visitor to the visit the actual default value of this @@ -128,6 +153,42 @@ public abstract class MethodVisitor { } /** + * 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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or <tt>null</tt> 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. * * @param parameter @@ -201,9 +262,11 @@ public abstract class MethodVisitor { * <li>{@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 (<code>nLocals</code> is 1, 2 or 3).</li> - * <li>{@link Opcodes#F_FULL} representing complete frame data.</li></li> + * <li>{@link Opcodes#F_FULL} representing complete frame data.</li> + * </ul> + * </li> * </ul> - * </ul> <br> + * <br> * 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,14 +439,53 @@ 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. * * @param name @@ -558,6 +660,48 @@ public abstract class MethodVisitor { } } + /** + * Visits an annotation on an instruction. This method must be called just + * <i>after</i> 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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or <tt>null</tt> 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 // ------------------------------------------------------------------------- @@ -587,6 +731,38 @@ public abstract class MethodVisitor { } /** + * Visits an annotation on an exception handler type. This method must be + * called <i>after</i> 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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or <tt>null</tt> 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. * * @param name @@ -617,6 +793,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 + * <tt>null</tt> 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 + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or <tt>null</tt> 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. * * @param line diff --git a/src/asm/scala/tools/asm/MethodWriter.java b/src/asm/scala/tools/asm/MethodWriter.java index 87acab17c9..0c4130e499 100644 --- a/src/asm/scala/tools/asm/MethodWriter.java +++ b/src/asm/scala/tools/asm/MethodWriter.java @@ -192,6 +192,18 @@ class MethodWriter extends MethodVisitor { private AnnotationWriter ianns; /** + * The runtime visible type annotations of this method. May be <tt>null</tt> + * . + */ + private AnnotationWriter tanns; + + /** + * The runtime invisible type annotations of this method. May be + * <tt>null</tt>. + */ + private AnnotationWriter itanns; + + /** * The runtime visible parameter annotations of this method. May be * <tt>null</tt>. */ @@ -283,6 +295,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. */ private int localVarCount; @@ -313,6 +335,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 <tt>null</tt>. + */ + private AnnotationWriter ctanns; + + /** + * The runtime invisible type annotations of the code. May be <tt>null</tt>. + */ + private AnnotationWriter ictanns; + + /** * The non standard attributes of the method's code. */ private Attribute cattrs; @@ -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 { @@ -462,6 +499,16 @@ class MethodWriter extends MethodVisitor { // ------------------------------------------------------------------------ @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) { return null; @@ -491,6 +538,29 @@ class MethodWriter extends MethodVisitor { } @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) { if (!ClassReader.ANNOTATIONS) { @@ -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) { @@ -1176,6 +1258,30 @@ class MethodWriter extends MethodVisitor { } @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) { ++handlerCount; @@ -1194,6 +1300,29 @@ class MethodWriter extends MethodVisitor { } @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, final int index) { @@ -1226,6 +1355,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) { lineNumber = new ByteVector(); @@ -1237,6 +1401,14 @@ class MethodWriter extends MethodVisitor { @Override public void visitMaxs(final int maxStack, final int maxLocals) { + if (resize) { + // replaces the temporary jump opcodes introduced by Label.resolve. + if (ClassReader.RESIZE) { + resizeInstructions(); + } else { + throw new RuntimeException("Method code too large!"); + } + } if (ClassReader.FRAMES && compute == FRAMES) { // completes the control flow graph with exception handler blocks Handler handler = firstHandler; @@ -1858,22 +2030,12 @@ class MethodWriter extends MethodVisitor { if (classReaderOffset != 0) { return 6 + classReaderLength; } - if (resize) { - // replaces the temporary jump opcodes introduced by Label.resolve. - if (ClassReader.RESIZE) { - resizeInstructions(); - } else { - throw new RuntimeException("Method code too large!"); - } - } int size = 8; if (code.length > 0) { if (code.length > 65536) { String nameString = ""; - int i = 0; - // find item that corresponds to the index of our name - while (i < cw.items.length && (cw.items[i] == null || cw.items[i].index != name)) i++; - if (cw.items[i] != null) nameString = cw.items[i].strVal1 +"'s "; + Item nameItem = cw.findItemByIndex(name); + if (nameItem != null) nameString = nameItem.strVal1 +"'s "; throw new RuntimeException("Method "+ nameString +"code too large!"); } cw.newUTF8("Code"); @@ -1895,6 +2057,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); @@ -1920,6 +2090,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; @@ -1932,6 +2106,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); @@ -1988,6 +2170,9 @@ class MethodWriter extends MethodVisitor { if (ClassReader.SIGNATURES && signature != null) { ++attributeCount; } + if (methodParameters != null) { + ++attributeCount; + } if (ClassReader.ANNOTATIONS && annd != null) { ++attributeCount; } @@ -1997,6 +2182,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; } @@ -2021,6 +2212,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); @@ -2050,6 +2247,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(); } @@ -2075,6 +2278,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); } @@ -2100,6 +2311,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); @@ -2113,6 +2330,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); @@ -2464,49 +2689,50 @@ class MethodWriter extends MethodVisitor { } } - // recomputes the stack map frames - if (frameCount > 0) { - if (compute == FRAMES) { - frameCount = 0; - stackMap = null; - previousFrame = null; - frame = null; - Frame f = new Frame(); - f.owner = labels; - Type[] args = Type.getArgumentTypes(descriptor); - f.initInputFrame(cw, access, args, maxLocals); - visitFrame(f); - Label l = labels; - while (l != null) { - /* - * here we need the original label position. getNewOffset - * must therefore never have been called for this label. - */ - u = l.position - 3; - if ((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) { - getNewOffset(allIndexes, allSizes, l); - // TODO update offsets in UNINITIALIZED values - visitFrame(l.frame); - } - l = l.successor; - } - } else { + // updates the stack map frame labels + if (compute == FRAMES) { + Label l = labels; + while (l != null) { /* - * Resizing an existing stack map frame table is really hard. - * Not only the table must be parsed to update the offets, but - * new frames may be needed for jump instructions that were - * inserted by this method. And updating the offsets or - * inserting frames can change the format of the following - * frames, in case of packed frames. In practice the whole table - * must be recomputed. For this the frames are marked as - * potentially invalid. This will cause the whole class to be - * reread and rewritten with the COMPUTE_FRAMES option (see the - * ClassWriter.toByteArray method). This is not very efficient - * but is much easier and requires much less code than any other - * method I can think of. + * Detects the labels that are just after an IF instruction that + * has been resized with the IFNOT GOTO_W pattern. These labels + * are now the target of a jump instruction (the IFNOT + * instruction). Note that we need the original label position + * here. getNewOffset must therefore never have been called for + * this label. */ - cw.invalidFrames = true; + u = l.position - 3; + if (u >= 0 && resize[u]) { + l.status |= Label.TARGET; + } + getNewOffset(allIndexes, allSizes, l); + l = l.successor; } + // Update the offsets in the uninitialized types + for (i = 0; i < cw.typeTable.length; ++i) { + Item item = cw.typeTable[i]; + if (item != null && item.type == ClassWriter.TYPE_UNINIT) { + item.intVal = getNewOffset(allIndexes, allSizes, 0, + item.intVal); + } + } + // The stack map frames are not serialized yet, so we don't need + // to update them. They will be serialized in visitMaxs. + } else if (frameCount > 0) { + /* + * Resizing an existing stack map frame table is really hard. Not + * only the table must be parsed to update the offets, but new + * frames may be needed for jump instructions that were inserted by + * this method. And updating the offsets or inserting frames can + * change the format of the following frames, in case of packed + * frames. In practice the whole table must be recomputed. For this + * the frames are marked as potentially invalid. This will cause the + * whole class to be reread and rewritten with the COMPUTE_FRAMES + * option (see the ClassWriter.toByteArray method). This is not very + * efficient but is much easier and requires much less code than any + * other method I can think of. + */ + cw.invalidFrames = true; } // updates the exception handler block labels Handler h = firstHandler; diff --git a/src/asm/scala/tools/asm/Opcodes.java b/src/asm/scala/tools/asm/Opcodes.java index 809e5ae590..24eaffa717 100644 --- a/src/asm/scala/tools/asm/Opcodes.java +++ b/src/asm/scala/tools/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/src/asm/scala/tools/asm/Type.java b/src/asm/scala/tools/asm/Type.java index 7821a492e6..7887080dee 100644 --- a/src/asm/scala/tools/asm/Type.java +++ b/src/asm/scala/tools/asm/Type.java @@ -401,8 +401,8 @@ public class Type { * @return the size of the arguments of the method (plus one for the * implicit this argument), argSize, and the size of its return * value, retSize, packed into a single int i = - * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to - * <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). + * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to + * <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). */ public static int getArgumentsAndReturnSizes(final String desc) { int n = 1; @@ -606,9 +606,10 @@ public class Type { * * @return the size of the arguments (plus one for the implicit this * argument), argSize, and the size of the return value, retSize, - * packed into a single int i = <tt>(argSize << 2) | retSize</tt> - * (argSize is therefore equal to <tt>i >> 2</tt>, and retSize to - * <tt>i & 0x03</tt>). + * packed into a single + * int i = <tt>(argSize << 2) | retSize</tt> + * (argSize is therefore equal to <tt>i >> 2</tt>, + * and retSize to <tt>i & 0x03</tt>). */ public int getArgumentsAndReturnSizes() { return getArgumentsAndReturnSizes(getDescriptor()); diff --git a/src/asm/scala/tools/asm/TypePath.java b/src/asm/scala/tools/asm/TypePath.java new file mode 100644 index 0000000000..d4c6f0d857 --- /dev/null +++ b/src/asm/scala/tools/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 scala.tools.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/src/asm/scala/tools/asm/TypeReference.java b/src/asm/scala/tools/asm/TypeReference.java new file mode 100644 index 0000000000..118b0f6529 --- /dev/null +++ b/src/asm/scala/tools/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 scala.tools.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/src/asm/scala/tools/asm/signature/SignatureVisitor.java b/src/asm/scala/tools/asm/signature/SignatureVisitor.java index f38f81f53b..1e16bd3f7c 100644 --- a/src/asm/scala/tools/asm/signature/SignatureVisitor.java +++ b/src/asm/scala/tools/asm/signature/SignatureVisitor.java @@ -73,7 +73,7 @@ public abstract class SignatureVisitor { /** * 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; @@ -82,9 +82,12 @@ public abstract class SignatureVisitor { * * @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 SignatureVisitor(final int api) { + if (api != Opcodes.ASM4 && api != Opcodes.ASM5) { + throw new IllegalArgumentException(); + } this.api = api; } diff --git a/src/asm/scala/tools/asm/signature/SignatureWriter.java b/src/asm/scala/tools/asm/signature/SignatureWriter.java index ebf4fe07b4..65756eee51 100644 --- a/src/asm/scala/tools/asm/signature/SignatureWriter.java +++ b/src/asm/scala/tools/asm/signature/SignatureWriter.java @@ -66,7 +66,7 @@ public class SignatureWriter extends SignatureVisitor { * Constructs a new {@link SignatureWriter} object. */ public SignatureWriter() { - super(Opcodes.ASM4); + super(Opcodes.ASM5); } // ------------------------------------------------------------------------ diff --git a/src/asm/scala/tools/asm/tree/AbstractInsnNode.java b/src/asm/scala/tools/asm/tree/AbstractInsnNode.java index 411eead3c7..2ce0c8b6ee 100644 --- a/src/asm/scala/tools/asm/tree/AbstractInsnNode.java +++ b/src/asm/scala/tools/asm/tree/AbstractInsnNode.java @@ -29,6 +29,7 @@ */ package scala.tools.asm.tree; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -128,6 +129,28 @@ public abstract class AbstractInsnNode { protected int opcode; /** + * The runtime visible type annotations of this instruction. This field is + * only used for real instructions (i.e. not for labels, frames, or line + * number nodes). This list is a list of {@link TypeAnnotationNode} objects. + * May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List<TypeAnnotationNode> visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this instruction. This field is + * only used for real instructions (i.e. not for labels, frames, or line + * number nodes). This list is a list of {@link TypeAnnotationNode} objects. + * May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List<TypeAnnotationNode> invisibleTypeAnnotations; + + /** * Previous instruction in the list to which this instruction belongs. */ AbstractInsnNode prev; @@ -204,6 +227,29 @@ public abstract class AbstractInsnNode { public abstract void accept(final MethodVisitor cv); /** + * Makes the given visitor visit the annotations of this instruction. + * + * @param mv + * a method visitor. + */ + protected final void acceptAnnotations(final MethodVisitor mv) { + int n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } + } + + /** * Returns a copy of this instruction. * * @param labels @@ -245,4 +291,36 @@ public abstract class AbstractInsnNode { } return clones; } + + /** + * Clones the annotations of the given instruction into this instruction. + * + * @param insn + * the source instruction. + * @return this instruction. + */ + protected final AbstractInsnNode cloneAnnotations( + final AbstractInsnNode insn) { + if (insn.visibleTypeAnnotations != null) { + this.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(); + for (int i = 0; i < insn.visibleTypeAnnotations.size(); ++i) { + TypeAnnotationNode src = insn.visibleTypeAnnotations.get(i); + TypeAnnotationNode ann = new TypeAnnotationNode(src.typeRef, + src.typePath, src.desc); + src.accept(ann); + this.visibleTypeAnnotations.add(ann); + } + } + if (insn.invisibleTypeAnnotations != null) { + this.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(); + for (int i = 0; i < insn.invisibleTypeAnnotations.size(); ++i) { + TypeAnnotationNode src = insn.invisibleTypeAnnotations.get(i); + TypeAnnotationNode ann = new TypeAnnotationNode(src.typeRef, + src.typePath, src.desc); + src.accept(ann); + this.invisibleTypeAnnotations.add(ann); + } + } + return this; + } } diff --git a/src/asm/scala/tools/asm/tree/AnnotationNode.java b/src/asm/scala/tools/asm/tree/AnnotationNode.java index 1f4beef9f7..b8d5988066 100644 --- a/src/asm/scala/tools/asm/tree/AnnotationNode.java +++ b/src/asm/scala/tools/asm/tree/AnnotationNode.java @@ -67,9 +67,14 @@ public class AnnotationNode extends AnnotationVisitor { * * @param desc * the class descriptor of the annotation class. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public AnnotationNode(final String desc) { - this(Opcodes.ASM4, desc); + this(Opcodes.ASM5, desc); + if (getClass() != AnnotationNode.class) { + throw new IllegalStateException(); + } } /** @@ -77,7 +82,7 @@ public class AnnotationNode extends 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 desc * the class descriptor of the annotation class. */ @@ -93,7 +98,7 @@ public class AnnotationNode extends AnnotationVisitor { * where the visited values must be stored. */ AnnotationNode(final List<Object> values) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.values = values; } @@ -166,7 +171,8 @@ public class AnnotationNode extends AnnotationVisitor { * versions of the ASM API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { // nothing to do diff --git a/src/asm/scala/tools/asm/tree/ClassNode.java b/src/asm/scala/tools/asm/tree/ClassNode.java index c3d999985a..304b4ec9f5 100644 --- a/src/asm/scala/tools/asm/tree/ClassNode.java +++ b/src/asm/scala/tools/asm/tree/ClassNode.java @@ -39,6 +39,7 @@ import scala.tools.asm.ClassVisitor; import scala.tools.asm.FieldVisitor; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A node that represents a class. @@ -133,6 +134,24 @@ public class ClassNode extends ClassVisitor { public List<AnnotationNode> invisibleAnnotations; /** + * The runtime visible type annotations of this class. This list is a list + * of {@link TypeAnnotationNode} objects. May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List<TypeAnnotationNode> visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this class. This list is a list + * of {@link TypeAnnotationNode} objects. May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List<TypeAnnotationNode> invisibleTypeAnnotations; + + /** * The non standard attributes of this class. This list is a list of * {@link Attribute} objects. May be <tt>null</tt>. * @@ -168,9 +187,15 @@ public class ClassNode extends ClassVisitor { * Constructs a new {@link ClassNode}. <i>Subclasses must not use this * constructor</i>. Instead, they must use the {@link #ClassNode(int)} * version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public ClassNode() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != ClassNode.class) { + throw new IllegalStateException(); + } } /** @@ -178,7 +203,7 @@ public class ClassNode extends 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 ClassNode(final int api) { super(api); @@ -239,6 +264,24 @@ public class ClassNode extends ClassVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + + @Override public void visitAttribute(final Attribute attr) { if (attrs == null) { attrs = new ArrayList<Attribute>(1); @@ -286,10 +329,26 @@ public class ClassNode extends ClassVisitor { * API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + for (FieldNode f : fields) { + f.check(api); + } + for (MethodNode m : methods) { + m.check(api); + } + } } /** @@ -323,6 +382,19 @@ public class ClassNode extends ClassVisitor { AnnotationNode an = invisibleAnnotations.get(i); an.accept(cv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(cv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(cv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = attrs == null ? 0 : attrs.size(); for (i = 0; i < n; ++i) { cv.visitAttribute(attrs.get(i)); diff --git a/src/asm/scala/tools/asm/tree/FieldInsnNode.java b/src/asm/scala/tools/asm/tree/FieldInsnNode.java index 0c94f18adf..c027de109b 100644 --- a/src/asm/scala/tools/asm/tree/FieldInsnNode.java +++ b/src/asm/scala/tools/asm/tree/FieldInsnNode.java @@ -97,12 +97,14 @@ public class FieldInsnNode extends AbstractInsnNode { } @Override - public void accept(final MethodVisitor cv) { - cv.visitFieldInsn(opcode, owner, name, desc); + public void accept(final MethodVisitor mv) { + mv.visitFieldInsn(opcode, owner, name, desc); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new FieldInsnNode(opcode, owner, name, desc); + return new FieldInsnNode(opcode, owner, name, desc) + .cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/FieldNode.java b/src/asm/scala/tools/asm/tree/FieldNode.java index 61b614ec59..3fb14dac4f 100644 --- a/src/asm/scala/tools/asm/tree/FieldNode.java +++ b/src/asm/scala/tools/asm/tree/FieldNode.java @@ -37,6 +37,7 @@ import scala.tools.asm.Attribute; import scala.tools.asm.ClassVisitor; import scala.tools.asm.FieldVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A node that represents a field. @@ -92,6 +93,24 @@ public class FieldNode extends FieldVisitor { public List<AnnotationNode> invisibleAnnotations; /** + * The runtime visible type annotations of this field. This list is a list + * of {@link TypeAnnotationNode} objects. May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List<TypeAnnotationNode> visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this field. This list is a list + * of {@link TypeAnnotationNode} objects. May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List<TypeAnnotationNode> invisibleTypeAnnotations; + + /** * The non standard attributes of this field. This list is a list of * {@link Attribute} objects. May be <tt>null</tt>. * @@ -120,20 +139,24 @@ public class FieldNode extends FieldVisitor { * <tt>null</tt> if the field does not have an initial value, * must be an {@link Integer}, a {@link Float}, a {@link Long}, a * {@link Double} or a {@link String}. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public FieldNode(final int access, final String name, final String desc, final String signature, final Object value) { - this(Opcodes.ASM4, access, name, desc, signature, value); + this(Opcodes.ASM5, access, name, desc, signature, value); + if (getClass() != FieldNode.class) { + throw new IllegalStateException(); + } } /** * Constructs a new {@link FieldNode}. <i>Subclasses must not use this - * constructor</i>. Instead, they must use the - * {@link #FieldNode(int, int, String, String, String, Object)} version. + * constructor</i>. * * @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 access * the field's access flags (see * {@link scala.tools.asm.Opcodes}). This parameter also @@ -184,6 +207,24 @@ public class FieldNode extends FieldVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + + @Override public void visitAttribute(final Attribute attr) { if (attrs == null) { attrs = new ArrayList<Attribute>(1); @@ -206,10 +247,20 @@ public class FieldNode extends FieldVisitor { * API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + } } /** @@ -234,6 +285,19 @@ public class FieldNode extends FieldVisitor { AnnotationNode an = invisibleAnnotations.get(i); an.accept(fv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(fv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(fv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = attrs == null ? 0 : attrs.size(); for (i = 0; i < n; ++i) { fv.visitAttribute(attrs.get(i)); diff --git a/src/asm/scala/tools/asm/tree/IincInsnNode.java b/src/asm/scala/tools/asm/tree/IincInsnNode.java index f9adf2e38c..c37ac91c27 100644 --- a/src/asm/scala/tools/asm/tree/IincInsnNode.java +++ b/src/asm/scala/tools/asm/tree/IincInsnNode.java @@ -73,10 +73,11 @@ public class IincInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitIincInsn(var, incr); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new IincInsnNode(var, incr); + return new IincInsnNode(var, incr).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/InsnList.java b/src/asm/scala/tools/asm/tree/InsnList.java index b1e2d97c6f..e808712e78 100644 --- a/src/asm/scala/tools/asm/tree/InsnList.java +++ b/src/asm/scala/tools/asm/tree/InsnList.java @@ -100,7 +100,7 @@ public class InsnList { * the index of the instruction that must be returned. * @return the instruction whose index is given. * @throws IndexOutOfBoundsException - * if (index < 0 || index >= size()). + * if (index < 0 || index >= size()). */ public AbstractInsnNode get(final int index) { if (index < 0 || index >= size) { @@ -535,6 +535,8 @@ public class InsnList { AbstractInsnNode prev; + AbstractInsnNode remove; + InsnListIterator(int index) { if (index == size()) { next = null; @@ -556,12 +558,22 @@ public class InsnList { AbstractInsnNode result = next; prev = result; next = result.next; + remove = result; return result; } public void remove() { - InsnList.this.remove(prev); - prev = prev.prev; + if (remove != null) { + if (remove == next) { + next = next.next; + } else { + prev = prev.prev; + } + InsnList.this.remove(remove); + remove = null; + } else { + throw new IllegalStateException(); + } } public boolean hasPrevious() { @@ -572,6 +584,7 @@ public class InsnList { AbstractInsnNode result = prev; next = result; prev = result.prev; + remove = result; return result; } @@ -598,6 +611,7 @@ public class InsnList { public void add(Object o) { InsnList.this.insertBefore(next, (AbstractInsnNode) o); prev = (AbstractInsnNode) o; + remove = null; } public void set(Object o) { diff --git a/src/asm/scala/tools/asm/tree/InsnNode.java b/src/asm/scala/tools/asm/tree/InsnNode.java index 4d5288cafa..f5313929ee 100644 --- a/src/asm/scala/tools/asm/tree/InsnNode.java +++ b/src/asm/scala/tools/asm/tree/InsnNode.java @@ -78,10 +78,11 @@ public class InsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitInsn(opcode); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new InsnNode(opcode); + return new InsnNode(opcode).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/IntInsnNode.java b/src/asm/scala/tools/asm/tree/IntInsnNode.java index e0aeed4bc8..6bbe8d845c 100644 --- a/src/asm/scala/tools/asm/tree/IntInsnNode.java +++ b/src/asm/scala/tools/asm/tree/IntInsnNode.java @@ -78,10 +78,11 @@ public class IntInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitIntInsn(opcode, operand); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new IntInsnNode(opcode, operand); + return new IntInsnNode(opcode, operand).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java b/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java index 7ee84b875b..0f85e60291 100644 --- a/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java +++ b/src/asm/scala/tools/asm/tree/InvokeDynamicInsnNode.java @@ -91,10 +91,12 @@ public class InvokeDynamicInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs); + return new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs) + .cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/JumpInsnNode.java b/src/asm/scala/tools/asm/tree/JumpInsnNode.java index 81e1e09deb..8b8a769204 100644 --- a/src/asm/scala/tools/asm/tree/JumpInsnNode.java +++ b/src/asm/scala/tools/asm/tree/JumpInsnNode.java @@ -86,10 +86,12 @@ public class JumpInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitJumpInsn(opcode, label.getLabel()); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new JumpInsnNode(opcode, clone(label, labels)); + return new JumpInsnNode(opcode, clone(label, labels)) + .cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/LdcInsnNode.java b/src/asm/scala/tools/asm/tree/LdcInsnNode.java index 4e328f9b39..1cc850bb31 100644 --- a/src/asm/scala/tools/asm/tree/LdcInsnNode.java +++ b/src/asm/scala/tools/asm/tree/LdcInsnNode.java @@ -69,10 +69,11 @@ public class LdcInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitLdcInsn(cst); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new LdcInsnNode(cst); + return new LdcInsnNode(cst).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/LocalVariableAnnotationNode.java b/src/asm/scala/tools/asm/tree/LocalVariableAnnotationNode.java new file mode 100644 index 0000000000..d05b808171 --- /dev/null +++ b/src/asm/scala/tools/asm/tree/LocalVariableAnnotationNode.java @@ -0,0 +1,157 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package scala.tools.asm.tree; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import scala.tools.asm.Label; +import scala.tools.asm.MethodVisitor; +import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; + +/** + * A node that represents a type annotation on a local or resource variable. + * + * @author Eric Bruneton + */ +public class LocalVariableAnnotationNode extends TypeAnnotationNode { + + /** + * The fist instructions corresponding to the continuous ranges that make + * the scope of this local variable (inclusive). Must not be <tt>null</tt>. + */ + public List<LabelNode> start; + + /** + * The last instructions corresponding to the continuous ranges that make + * the scope of this local variable (exclusive). This list must have the + * same size as the 'start' list. Must not be <tt>null</tt>. + */ + public List<LabelNode> end; + + /** + * The local variable's index in each range. This list must have the same + * size as the 'start' list. Must not be <tt>null</tt>. + */ + public List<Integer> index; + + /** + * Constructs a new {@link LocalVariableAnnotationNode}. <i>Subclasses must + * not use this constructor</i>. Instead, they must use the + * {@link #LocalVariableAnnotationNode(int, TypePath, LabelNode[], LabelNode[], int[], String)} + * version. + * + * @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 + * <tt>null</tt> 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. + */ + public LocalVariableAnnotationNode(int typeRef, TypePath typePath, + LabelNode[] start, LabelNode[] end, int[] index, String desc) { + this(Opcodes.ASM5, typeRef, typePath, start, end, index, desc); + } + + /** + * Constructs a new {@link LocalVariableAnnotationNode}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @param typeRef + * a reference to the annotated type. See {@link TypeReference}. + * @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 typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + */ + public LocalVariableAnnotationNode(int api, int typeRef, TypePath typePath, + LabelNode[] start, LabelNode[] end, int[] index, String desc) { + super(api, typeRef, typePath, desc); + this.start = new ArrayList<LabelNode>(start.length); + this.start.addAll(Arrays.asList(start)); + this.end = new ArrayList<LabelNode>(end.length); + this.end.addAll(Arrays.asList(end)); + this.index = new ArrayList<Integer>(index.length); + for (int i : index) { + this.index.add(i); + } + } + + /** + * Makes the given visitor visit this type annotation. + * + * @param mv + * the visitor that must visit this annotation. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + */ + public void accept(final MethodVisitor mv, boolean visible) { + Label[] start = new Label[this.start.size()]; + Label[] end = new Label[this.end.size()]; + int[] index = new int[this.index.size()]; + for (int i = 0; i < start.length; ++i) { + start[i] = this.start.get(i).getLabel(); + end[i] = this.end.get(i).getLabel(); + index[i] = this.index.get(i); + } + accept(mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, + index, desc, true)); + } +} diff --git a/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java b/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java index d2479b4814..7db2f53ff4 100644 --- a/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java +++ b/src/asm/scala/tools/asm/tree/LookupSwitchInsnNode.java @@ -105,6 +105,7 @@ public class LookupSwitchInsnNode extends AbstractInsnNode { labels[i] = this.labels.get(i).getLabel(); } mv.visitLookupSwitchInsn(dflt.getLabel(), keys, labels); + acceptAnnotations(mv); } @Override @@ -112,6 +113,6 @@ public class LookupSwitchInsnNode extends AbstractInsnNode { LookupSwitchInsnNode clone = new LookupSwitchInsnNode(clone(dflt, labels), null, clone(this.labels, labels)); clone.keys.addAll(keys); - return clone; + return clone.cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/MethodInsnNode.java b/src/asm/scala/tools/asm/tree/MethodInsnNode.java index bf09f556d8..1ec46d473d 100644 --- a/src/asm/scala/tools/asm/tree/MethodInsnNode.java +++ b/src/asm/scala/tools/asm/tree/MethodInsnNode.java @@ -32,6 +32,7 @@ package scala.tools.asm.tree; import java.util.Map; import scala.tools.asm.MethodVisitor; +import scala.tools.asm.Opcodes; /** * A node that represents a method instruction. A method instruction is an @@ -58,6 +59,11 @@ public class MethodInsnNode extends AbstractInsnNode { public String desc; /** + * If the method's owner class if an interface. + */ + public boolean itf; + + /** * Constructs a new {@link MethodInsnNode}. * * @param opcode @@ -73,12 +79,37 @@ public class MethodInsnNode extends AbstractInsnNode { * @param desc * the method's descriptor (see {@link scala.tools.asm.Type}). */ + @Deprecated public MethodInsnNode(final int opcode, final String owner, final String name, final String desc) { + this(opcode, owner, name, desc, opcode == Opcodes.INVOKEINTERFACE); + } + + /** + * Constructs a new {@link MethodInsnNode}. + * + * @param opcode + * the opcode of the type instruction to be constructed. This + * opcode must be INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner + * the internal name of the method's owner class (see + * {@link scala.tools.asm.Type#getInternalName() + * getInternalName}). + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link scala.tools.asm.Type}). + * @param itf + * if the method's owner class is an interface. + */ + public MethodInsnNode(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { super(opcode); this.owner = owner; this.name = name; this.desc = desc; + this.itf = itf; } /** @@ -99,11 +130,11 @@ public class MethodInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { - mv.visitMethodInsn(opcode, owner, name, desc); + mv.visitMethodInsn(opcode, owner, name, desc, itf); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new MethodInsnNode(opcode, owner, name, desc); + return new MethodInsnNode(opcode, owner, name, desc, itf); } } diff --git a/src/asm/scala/tools/asm/tree/MethodNode.java b/src/asm/scala/tools/asm/tree/MethodNode.java index a161600edb..3dec50e02c 100644 --- a/src/asm/scala/tools/asm/tree/MethodNode.java +++ b/src/asm/scala/tools/asm/tree/MethodNode.java @@ -41,6 +41,7 @@ import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; /** * A node that represents a method. @@ -78,6 +79,11 @@ public class MethodNode extends MethodVisitor { public List<String> exceptions; /** + * The method parameter info (access flags and name) + */ + public List<ParameterNode> parameters; + + /** * The runtime visible annotations of this method. This list is a list of * {@link AnnotationNode} objects. May be <tt>null</tt>. * @@ -96,6 +102,24 @@ public class MethodNode extends MethodVisitor { public List<AnnotationNode> invisibleAnnotations; /** + * The runtime visible type annotations of this method. This list is a list + * of {@link TypeAnnotationNode} objects. May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List<TypeAnnotationNode> visibleTypeAnnotations; + + /** + * The runtime invisible type annotations of this method. This list is a + * list of {@link TypeAnnotationNode} objects. May be <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List<TypeAnnotationNode> invisibleTypeAnnotations; + + /** * The non standard attributes of this method. This list is a list of * {@link Attribute} objects. May be <tt>null</tt>. * @@ -167,6 +191,22 @@ public class MethodNode extends MethodVisitor { public List<LocalVariableNode> localVariables; /** + * The visible local variable annotations of this method. This list is a + * list of {@link LocalVariableAnnotationNode} objects. May be <tt>null</tt> + * + * @associates scala.tools.asm.tree.LocalVariableAnnotationNode + */ + public List<LocalVariableAnnotationNode> visibleLocalVariableAnnotations; + + /** + * The invisible local variable annotations of this method. This list is a + * list of {@link LocalVariableAnnotationNode} objects. May be <tt>null</tt> + * + * @associates scala.tools.asm.tree.LocalVariableAnnotationNode + */ + public List<LocalVariableAnnotationNode> invisibleLocalVariableAnnotations; + + /** * If the accept method has been called on this object. */ private boolean visited; @@ -175,9 +215,15 @@ public class MethodNode extends MethodVisitor { * Constructs an uninitialized {@link MethodNode}. <i>Subclasses must not * use this constructor</i>. Instead, they must use the * {@link #MethodNode(int)} version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public MethodNode() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != MethodNode.class) { + throw new IllegalStateException(); + } } /** @@ -185,7 +231,7 @@ public class MethodNode extends 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 MethodNode(final int api) { super(api); @@ -211,10 +257,15 @@ public class MethodNode extends MethodVisitor { * the internal names of the method's exception classes (see * {@link Type#getInternalName() getInternalName}). May be * <tt>null</tt>. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public MethodNode(final int access, final String name, final String desc, final String signature, final String[] exceptions) { - this(Opcodes.ASM4, access, name, desc, signature, exceptions); + this(Opcodes.ASM5, access, name, desc, signature, exceptions); + if (getClass() != MethodNode.class) { + throw new IllegalStateException(); + } } /** @@ -222,7 +273,7 @@ public class MethodNode extends 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 access * the method's access flags (see {@link Opcodes}). This * parameter also indicates if the method is synthetic and/or @@ -263,6 +314,15 @@ public class MethodNode extends MethodVisitor { // ------------------------------------------------------------------------ @Override + public void visitParameter(String name, int access) { + if (parameters == null) { + parameters = new ArrayList<ParameterNode>(5); + } + parameters.add(new ParameterNode(name, access)); + } + + @Override + @SuppressWarnings("serial") public AnnotationVisitor visitAnnotationDefault() { return new AnnotationNode(new ArrayList<Object>(0) { @Override @@ -292,6 +352,24 @@ public class MethodNode extends MethodVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (visibleTypeAnnotations == null) { + visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); + } + visibleTypeAnnotations.add(an); + } else { + if (invisibleTypeAnnotations == null) { + invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>(1); + } + invisibleTypeAnnotations.add(an); + } + return an; + } + + @Override public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { AnnotationNode an = new AnnotationNode(desc); @@ -365,13 +443,28 @@ public class MethodNode extends MethodVisitor { instructions.add(new FieldInsnNode(opcode, owner, name, desc)); } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } instructions.add(new MethodInsnNode(opcode, owner, name, desc)); } @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + instructions.add(new MethodInsnNode(opcode, owner, name, desc, itf)); + } + + @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { instructions.add(new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs)); @@ -417,6 +510,33 @@ public class MethodNode extends MethodVisitor { } @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + // Finds the last real instruction, i.e. the instruction targeted by + // this annotation. + AbstractInsnNode insn = instructions.getLast(); + while (insn.getOpcode() == -1) { + insn = insn.getPrevious(); + } + // Adds the annotation to this instruction. + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (insn.visibleTypeAnnotations == null) { + insn.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>( + 1); + } + insn.visibleTypeAnnotations.add(an); + } else { + if (insn.invisibleTypeAnnotations == null) { + insn.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>( + 1); + } + insn.invisibleTypeAnnotations.add(an); + } + return an; + } + + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { tryCatchBlocks.add(new TryCatchBlockNode(getLabelNode(start), @@ -424,6 +544,27 @@ public class MethodNode extends MethodVisitor { } @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + TryCatchBlockNode tcb = tryCatchBlocks.get((typeRef & 0x00FFFF00) >> 8); + TypeAnnotationNode an = new TypeAnnotationNode(typeRef, typePath, desc); + if (visible) { + if (tcb.visibleTypeAnnotations == null) { + tcb.visibleTypeAnnotations = new ArrayList<TypeAnnotationNode>( + 1); + } + tcb.visibleTypeAnnotations.add(an); + } else { + if (tcb.invisibleTypeAnnotations == null) { + tcb.invisibleTypeAnnotations = new ArrayList<TypeAnnotationNode>( + 1); + } + tcb.invisibleTypeAnnotations.add(an); + } + return an; + } + + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { @@ -432,6 +573,29 @@ public class MethodNode extends MethodVisitor { } @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + LocalVariableAnnotationNode an = new LocalVariableAnnotationNode( + typeRef, typePath, getLabelNodes(start), getLabelNodes(end), + index, desc); + if (visible) { + if (visibleLocalVariableAnnotations == null) { + visibleLocalVariableAnnotations = new ArrayList<LocalVariableAnnotationNode>( + 1); + } + visibleLocalVariableAnnotations.add(an); + } else { + if (invisibleLocalVariableAnnotations == null) { + invisibleLocalVariableAnnotations = new ArrayList<LocalVariableAnnotationNode>( + 1); + } + invisibleLocalVariableAnnotations.add(an); + } + return an; + } + + @Override public void visitLineNumber(final int line, final Label start) { instructions.add(new LineNumberNode(line, getLabelNode(start))); } @@ -494,10 +658,57 @@ public class MethodNode extends MethodVisitor { * versions of the ASM API than the given version. * * @param api - * an ASM API version. Must be one of {@link Opcodes#ASM4}. + * an ASM API version. Must be one of {@link Opcodes#ASM4} or + * {@link Opcodes#ASM5}. */ public void check(final int api) { - // nothing to do + if (api == Opcodes.ASM4) { + if (visibleTypeAnnotations != null + && visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleTypeAnnotations != null + && invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + int n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size(); + for (int i = 0; i < n; ++i) { + TryCatchBlockNode tcb = tryCatchBlocks.get(i); + if (tcb.visibleTypeAnnotations != null + && tcb.visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (tcb.invisibleTypeAnnotations != null + && tcb.invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + } + for (int i = 0; i < instructions.size(); ++i) { + AbstractInsnNode insn = instructions.get(i); + if (insn.visibleTypeAnnotations != null + && insn.visibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (insn.invisibleTypeAnnotations != null + && insn.invisibleTypeAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (insn instanceof MethodInsnNode) { + boolean itf = ((MethodInsnNode) insn).itf; + if (itf != (insn.opcode == Opcodes.INVOKEINTERFACE)) { + throw new RuntimeException(); + } + } + } + if (visibleLocalVariableAnnotations != null + && visibleLocalVariableAnnotations.size() > 0) { + throw new RuntimeException(); + } + if (invisibleLocalVariableAnnotations != null + && invisibleLocalVariableAnnotations.size() > 0) { + throw new RuntimeException(); + } + } } /** @@ -523,8 +734,14 @@ public class MethodNode extends MethodVisitor { * a method visitor. */ public void accept(final MethodVisitor mv) { - // visits the method attributes + // visits the method parameters int i, j, n; + n = parameters == null ? 0 : parameters.size(); + for (i = 0; i < n; i++) { + ParameterNode parameter = parameters.get(i); + mv.visitParameter(parameter.name, parameter.access); + } + // visits the method attributes if (annotationDefault != null) { AnnotationVisitor av = mv.visitAnnotationDefault(); AnnotationNode.accept(av, null, annotationDefault); @@ -542,6 +759,19 @@ public class MethodNode extends MethodVisitor { AnnotationNode an = invisibleAnnotations.get(i); an.accept(mv.visitAnnotation(an.desc, false)); } + n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations.size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitTypeAnnotation(an.typeRef, an.typePath, an.desc, + false)); + } n = visibleParameterAnnotations == null ? 0 : visibleParameterAnnotations.length; for (i = 0; i < n; ++i) { @@ -579,6 +809,7 @@ public class MethodNode extends MethodVisitor { // visits try catch blocks n = tryCatchBlocks == null ? 0 : tryCatchBlocks.size(); for (i = 0; i < n; ++i) { + tryCatchBlocks.get(i).updateIndex(i); tryCatchBlocks.get(i).accept(mv); } // visits instructions @@ -588,6 +819,17 @@ public class MethodNode extends MethodVisitor { for (i = 0; i < n; ++i) { localVariables.get(i).accept(mv); } + // visits local variable annotations + n = visibleLocalVariableAnnotations == null ? 0 + : visibleLocalVariableAnnotations.size(); + for (i = 0; i < n; ++i) { + visibleLocalVariableAnnotations.get(i).accept(mv, true); + } + n = invisibleLocalVariableAnnotations == null ? 0 + : invisibleLocalVariableAnnotations.size(); + for (i = 0; i < n; ++i) { + invisibleLocalVariableAnnotations.get(i).accept(mv, false); + } // visits maxs mv.visitMaxs(maxStack, maxLocals); visited = true; diff --git a/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java b/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java index fe5e8832b3..a8339a20b5 100644 --- a/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java +++ b/src/asm/scala/tools/asm/tree/MultiANewArrayInsnNode.java @@ -73,11 +73,12 @@ public class MultiANewArrayInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitMultiANewArrayInsn(desc, dims); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new MultiANewArrayInsnNode(desc, dims); + return new MultiANewArrayInsnNode(desc, dims).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/ParameterNode.java b/src/asm/scala/tools/asm/tree/ParameterNode.java new file mode 100644 index 0000000000..a3e55d5629 --- /dev/null +++ b/src/asm/scala/tools/asm/tree/ParameterNode.java @@ -0,0 +1,76 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package scala.tools.asm.tree; + +import scala.tools.asm.MethodVisitor; + +/** + * A node that represents a parameter access and name. + * + * @author Remi Forax + */ +public class ParameterNode { + /** + * The parameter's name. + */ + public String name; + + /** + * The parameter's access flags (see {@link scala.tools.asm.Opcodes}). + * Valid values are <tt>ACC_FINAL</tt>, <tt>ACC_SYNTHETIC</tt> and + * <tt>ACC_MANDATED</tt>. + */ + public int access; + + /** + * Constructs a new {@link ParameterNode}. + * + * @param access + * The parameter's access flags. Valid values are + * <tt>ACC_FINAL</tt>, <tt>ACC_SYNTHETIC</tt> or/and + * <tt>ACC_MANDATED</tt> (see {@link scala.tools.asm.Opcodes}). + * @param name + * the parameter's name. + */ + public ParameterNode(final String name, final int access) { + this.name = name; + this.access = access; + } + + /** + * Makes the given visitor visit this parameter declaration. + * + * @param mv + * a method visitor. + */ + public void accept(final MethodVisitor mv) { + mv.visitParameter(name, access); + } +} diff --git a/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java b/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java index 9b3c2a3437..fb17b9e2e9 100644 --- a/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java +++ b/src/asm/scala/tools/asm/tree/TableSwitchInsnNode.java @@ -103,11 +103,12 @@ public class TableSwitchInsnNode extends AbstractInsnNode { labels[i] = this.labels.get(i).getLabel(); } mv.visitTableSwitchInsn(min, max, dflt.getLabel(), labels); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { return new TableSwitchInsnNode(min, max, clone(dflt, labels), clone( - this.labels, labels)); + this.labels, labels)).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java b/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java index ab4fa97c34..c639b9aa8b 100644 --- a/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java +++ b/src/asm/scala/tools/asm/tree/TryCatchBlockNode.java @@ -29,6 +29,8 @@ */ package scala.tools.asm.tree; +import java.util.List; + import scala.tools.asm.MethodVisitor; /** @@ -60,6 +62,26 @@ public class TryCatchBlockNode { public String type; /** + * The runtime visible type annotations on the exception handler type. This + * list is a list of {@link TypeAnnotationNode} objects. May be + * <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label visible + */ + public List<TypeAnnotationNode> visibleTypeAnnotations; + + /** + * The runtime invisible type annotations on the exception handler type. + * This list is a list of {@link TypeAnnotationNode} objects. May be + * <tt>null</tt>. + * + * @associates scala.tools.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List<TypeAnnotationNode> invisibleTypeAnnotations; + + /** * Constructs a new {@link TryCatchBlockNode}. * * @param start @@ -82,6 +104,29 @@ public class TryCatchBlockNode { } /** + * Updates the index of this try catch block in the method's list of try + * catch block nodes. This index maybe stored in the 'target' field of the + * type annotations of this block. + * + * @param index + * the new index of this try catch block in the method's list of + * try catch block nodes. + */ + public void updateIndex(final int index) { + int newTypeRef = 0x42000000 | (index << 8); + if (visibleTypeAnnotations != null) { + for (TypeAnnotationNode tan : visibleTypeAnnotations) { + tan.typeRef = newTypeRef; + } + } + if (invisibleTypeAnnotations != null) { + for (TypeAnnotationNode tan : invisibleTypeAnnotations) { + tan.typeRef = newTypeRef; + } + } + } + + /** * Makes the given visitor visit this try catch block. * * @param mv @@ -90,5 +135,19 @@ public class TryCatchBlockNode { public void accept(final MethodVisitor mv) { mv.visitTryCatchBlock(start.getLabel(), end.getLabel(), handler == null ? null : handler.getLabel(), type); + int n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = visibleTypeAnnotations.get(i); + an.accept(mv.visitTryCatchAnnotation(an.typeRef, an.typePath, + an.desc, true)); + } + n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations + .size(); + for (int i = 0; i < n; ++i) { + TypeAnnotationNode an = invisibleTypeAnnotations.get(i); + an.accept(mv.visitTryCatchAnnotation(an.typeRef, an.typePath, + an.desc, false)); + } } } diff --git a/src/asm/scala/tools/asm/tree/TypeAnnotationNode.java b/src/asm/scala/tools/asm/tree/TypeAnnotationNode.java new file mode 100644 index 0000000000..73b29624f7 --- /dev/null +++ b/src/asm/scala/tools/asm/tree/TypeAnnotationNode.java @@ -0,0 +1,100 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package scala.tools.asm.tree; + +import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; + +/** + * A node that represents a type annotationn. + * + * @author Eric Bruneton + */ +public class TypeAnnotationNode extends AnnotationNode { + + /** + * A reference to the annotated type. See {@link TypeReference}. + */ + public int typeRef; + + /** + * The path to the annotated type argument, wildcard bound, array element + * type, or static outer type within the referenced type. May be + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + */ + public TypePath typePath; + + /** + * Constructs a new {@link AnnotationNode}. <i>Subclasses must not use this + * constructor</i>. Instead, they must use the + * {@link #TypeAnnotationNode(int, int, TypePath, String)} version. + * + * @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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @throws IllegalStateException + * If a subclass calls this constructor. + */ + public TypeAnnotationNode(final int typeRef, final TypePath typePath, + final String desc) { + this(Opcodes.ASM5, typeRef, typePath, desc); + if (getClass() != TypeAnnotationNode.class) { + throw new IllegalStateException(); + } + } + + /** + * Constructs a new {@link AnnotationNode}. + * + * @param api + * the ASM API version implemented by this visitor. Must be one + * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. + * @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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + */ + public TypeAnnotationNode(final int api, final int typeRef, + final TypePath typePath, final String desc) { + super(api, desc); + this.typeRef = typeRef; + this.typePath = typePath; + } +} diff --git a/src/asm/scala/tools/asm/tree/TypeInsnNode.java b/src/asm/scala/tools/asm/tree/TypeInsnNode.java index 3210dd60e6..401400c3cb 100644 --- a/src/asm/scala/tools/asm/tree/TypeInsnNode.java +++ b/src/asm/scala/tools/asm/tree/TypeInsnNode.java @@ -81,10 +81,11 @@ public class TypeInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitTypeInsn(opcode, desc); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new TypeInsnNode(opcode, desc); + return new TypeInsnNode(opcode, desc).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/VarInsnNode.java b/src/asm/scala/tools/asm/tree/VarInsnNode.java index 5dd9ef6726..685e4fce2c 100644 --- a/src/asm/scala/tools/asm/tree/VarInsnNode.java +++ b/src/asm/scala/tools/asm/tree/VarInsnNode.java @@ -84,10 +84,11 @@ public class VarInsnNode extends AbstractInsnNode { @Override public void accept(final MethodVisitor mv) { mv.visitVarInsn(opcode, var); + acceptAnnotations(mv); } @Override public AbstractInsnNode clone(final Map<LabelNode, LabelNode> labels) { - return new VarInsnNode(opcode, var); + return new VarInsnNode(opcode, var).cloneAnnotations(this); } } diff --git a/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java b/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java index 5e3f51f21a..52b2a11d6f 100644 --- a/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java +++ b/src/asm/scala/tools/asm/tree/analysis/AnalyzerException.java @@ -37,6 +37,7 @@ import scala.tools.asm.tree.AbstractInsnNode; * @author Bing Ran * @author Eric Bruneton */ +@SuppressWarnings("serial") public class AnalyzerException extends Exception { public final AbstractInsnNode node; diff --git a/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java b/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java index 8d6653c1c5..7d0b7b0694 100644 --- a/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java +++ b/src/asm/scala/tools/asm/tree/analysis/BasicInterpreter.java @@ -53,7 +53,7 @@ public class BasicInterpreter extends Interpreter<BasicValue> implements Opcodes { public BasicInterpreter() { - super(ASM4); + super(ASM5); } protected BasicInterpreter(final int api) { diff --git a/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java b/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java index 71666edb74..b852f20acf 100644 --- a/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java +++ b/src/asm/scala/tools/asm/tree/analysis/BasicVerifier.java @@ -47,7 +47,7 @@ import scala.tools.asm.tree.MethodInsnNode; public class BasicVerifier extends BasicInterpreter { public BasicVerifier() { - super(ASM4); + super(ASM5); } protected BasicVerifier(final int api) { diff --git a/src/asm/scala/tools/asm/tree/analysis/Frame.java b/src/asm/scala/tools/asm/tree/analysis/Frame.java index 0d92edc4d6..44a07ee27c 100644 --- a/src/asm/scala/tools/asm/tree/analysis/Frame.java +++ b/src/asm/scala/tools/asm/tree/analysis/Frame.java @@ -134,6 +134,15 @@ public class Frame<V extends Value> { } /** + * Returns the maximum stack size of this frame. + * + * @return the maximum stack size of this frame. + */ + public int getMaxStackSize() { + return values.length - locals; + } + + /** * Returns the value of the given local variable. * * @param i diff --git a/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java b/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java index eaecd057ea..a345981f36 100644 --- a/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java +++ b/src/asm/scala/tools/asm/tree/analysis/SimpleVerifier.java @@ -107,7 +107,7 @@ public class SimpleVerifier extends BasicVerifier { public SimpleVerifier(final Type currentClass, final Type currentSuperClass, final List<Type> currentClassInterfaces, final boolean isInterface) { - this(ASM4, currentClass, currentSuperClass, currentClassInterfaces, + this(ASM5, currentClass, currentSuperClass, currentClassInterfaces, isInterface); } diff --git a/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java b/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java index a68086c073..7d739d3df9 100644 --- a/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java +++ b/src/asm/scala/tools/asm/tree/analysis/SourceInterpreter.java @@ -50,7 +50,7 @@ public class SourceInterpreter extends Interpreter<SourceValue> implements Opcodes { public SourceInterpreter() { - super(ASM4); + super(ASM5); } protected SourceInterpreter(final int api) { diff --git a/src/asm/scala/tools/asm/util/ASMifier.java b/src/asm/scala/tools/asm/util/ASMifier.java index 7e6b223853..521e07541b 100644 --- a/src/asm/scala/tools/asm/util/ASMifier.java +++ b/src/asm/scala/tools/asm/util/ASMifier.java @@ -40,6 +40,7 @@ import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; /** * A {@link Printer} that prints the ASM code to generate the classes if visits. @@ -83,9 +84,15 @@ public class ASMifier extends Printer { * Constructs a new {@link ASMifier}. <i>Subclasses must not use this * constructor</i>. Instead, they must use the * {@link #ASMifier(int, String, int)} version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public ASMifier() { - this(Opcodes.ASM4, "cw", 0); + this(Opcodes.ASM5, "cw", 0); + if (getClass() != ASMifier.class) { + throw new IllegalStateException(); + } } /** @@ -93,7 +100,7 @@ public class ASMifier extends Printer { * * @param api * the ASM API version implemented by this class. Must be one of - * {@link Opcodes#ASM4}. + * {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. * @param name * the name of the visitor variable in the produced code. * @param id @@ -170,7 +177,6 @@ public class ASMifier extends Printer { } text.add("import java.util.*;\n"); text.add("import scala.tools.asm.*;\n"); - text.add("import scala.tools.asm.attrs.*;\n"); text.add("public class " + simpleName + "Dump implements Opcodes {\n\n"); text.add("public static byte[] dump () throws Exception {\n\n"); text.add("ClassWriter cw = new ClassWriter(0);\n"); @@ -261,6 +267,12 @@ public class ASMifier extends Printer { } @Override + public ASMifier visitClassTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override public void visitClassAttribute(final Attribute attr) { visitAttribute(attr); } @@ -423,6 +435,12 @@ public class ASMifier extends Printer { } @Override + public ASMifier visitFieldTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override public void visitFieldAttribute(final Attribute attr) { visitAttribute(attr); } @@ -439,6 +457,16 @@ public class ASMifier extends Printer { // ------------------------------------------------------------------------ @Override + public void visitParameter(String parameterName, int access) { + buf.setLength(0); + buf.append(name).append(".visitParameter("); + appendString(buf, parameterName); + buf.append(", "); + appendAccess(access); + text.add(buf.append(");\n").toString()); + } + + @Override public ASMifier visitAnnotationDefault() { buf.setLength(0); buf.append("{\n").append("av0 = ").append(name) @@ -457,6 +485,12 @@ public class ASMifier extends Printer { } @Override + public ASMifier visitMethodTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override public ASMifier visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { buf.setLength(0); @@ -582,9 +616,30 @@ public class ASMifier extends Printer { text.add(buf.toString()); } + @Deprecated @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, + opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, itf); + } + + private void doVisitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { buf.setLength(0); buf.append(this.name).append(".visitMethodInsn(") .append(OPCODES[opcode]).append(", "); @@ -593,6 +648,8 @@ public class ASMifier extends Printer { appendConstant(name); buf.append(", "); appendConstant(desc); + buf.append(", "); + buf.append(itf ? "true" : "false"); buf.append(");\n"); text.add(buf.toString()); } @@ -711,6 +768,13 @@ public class ASMifier extends Printer { } @Override + public ASMifier visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitInsnAnnotation", typeRef, typePath, + desc, visible); + } + + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { buf.setLength(0); @@ -730,6 +794,13 @@ public class ASMifier extends Printer { } @Override + public ASMifier visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitTryCatchAnnotation", typeRef, + typePath, desc, visible); + } + + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { @@ -749,6 +820,39 @@ public class ASMifier extends Printer { } @Override + public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath, + Label[] start, Label[] end, int[] index, String desc, + boolean visible) { + buf.setLength(0); + buf.append("{\n").append("av0 = ").append(name) + .append(".visitLocalVariableAnnotation("); + buf.append(typeRef); + buf.append(", TypePath.fromString(\"").append(typePath).append("\"), "); + buf.append("new Label[] {"); + for (int i = 0; i < start.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(start[i]); + } + buf.append(" }, new Label[] {"); + for (int i = 0; i < end.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(end[i]); + } + buf.append(" }, new int[] {"); + for (int i = 0; i < index.length; ++i) { + buf.append(i == 0 ? " " : ", ").append(index[i]); + } + buf.append(" }, "); + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifier a = createASMifier("av", 0); + text.add(a.getText()); + text.add("}\n"); + return a; + } + + @Override public void visitLineNumber(final int line, final Label start) { buf.setLength(0); buf.append(name).append(".visitLineNumber(").append(line).append(", "); @@ -789,6 +893,28 @@ public class ASMifier extends Printer { return a; } + public ASMifier visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + return visitTypeAnnotation("visitTypeAnnotation", typeRef, typePath, + desc, visible); + } + + public ASMifier visitTypeAnnotation(final String method, final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + buf.setLength(0); + buf.append("{\n").append("av0 = ").append(name).append(".") + .append(method).append("("); + buf.append(typeRef); + buf.append(", TypePath.fromString(\"").append(typePath).append("\"), "); + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifier a = createASMifier("av", 0); + text.add(a.getText()); + text.add("}\n"); + return a; + } + public void visitAttribute(final Attribute attr) { buf.setLength(0); buf.append("// ATTRIBUTE ").append(attr.type).append('\n'); @@ -809,7 +935,7 @@ public class ASMifier extends Printer { // ------------------------------------------------------------------------ protected ASMifier createASMifier(final String name, final int id) { - return new ASMifier(Opcodes.ASM4, name, id); + return new ASMifier(Opcodes.ASM5, name, id); } /** @@ -950,6 +1076,13 @@ public class ASMifier extends Printer { buf.append("ACC_DEPRECATED"); first = false; } + if ((access & Opcodes.ACC_MANDATED) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_MANDATED"); + first = false; + } if (first) { buf.append('0'); } diff --git a/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java b/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java index f00a8f04a2..70441d1df4 100644 --- a/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckAnnotationAdapter.java @@ -49,7 +49,7 @@ public class CheckAnnotationAdapter extends AnnotationVisitor { } CheckAnnotationAdapter(final AnnotationVisitor av, final boolean named) { - super(Opcodes.ASM4, av); + super(Opcodes.ASM5, av); this.named = named; } @@ -70,7 +70,7 @@ public class CheckAnnotationAdapter extends AnnotationVisitor { } if (value instanceof Type) { int sort = ((Type) value).getSort(); - if (sort != Type.OBJECT && sort != Type.ARRAY) { + if (sort == Type.METHOD) { throw new IllegalArgumentException("Invalid annotation value"); } } diff --git a/src/asm/scala/tools/asm/util/CheckClassAdapter.java b/src/asm/scala/tools/asm/util/CheckClassAdapter.java index 0bfa143a95..9909208cc4 100644 --- a/src/asm/scala/tools/asm/util/CheckClassAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckClassAdapter.java @@ -46,6 +46,8 @@ import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; import scala.tools.asm.tree.ClassNode; import scala.tools.asm.tree.MethodNode; import scala.tools.asm.tree.analysis.Analyzer; @@ -91,9 +93,9 @@ import scala.tools.asm.tree.analysis.SimpleVerifier; * insnNumber locals : stack): * * <pre> - * scala.tools.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found . - * at scala.tools.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289) - * at scala.tools.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135) + * org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 71: Expected I, but found . + * at org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:289) + * at org.objectweb.asm.util.CheckClassAdapter.verify(CheckClassAdapter.java:135) * ... * remove()V * 00000 LinkedBlockingQueue$Itr . . . . . . . . : @@ -106,7 +108,7 @@ import scala.tools.asm.tree.analysis.SimpleVerifier; * 00071 LinkedBlockingQueue$Itr <b>.</b> I . . . . . . : * ILOAD 1 * 00072 <b>?</b> - * INVOKESPECIAL java/lang/Integer.<init> (I)V + * INVOKESPECIAL java/lang/Integer.<init> (I)V * ... * </pre> * @@ -215,7 +217,7 @@ public class CheckClassAdapter extends ClassVisitor { List<Type> interfaces = new ArrayList<Type>(); for (Iterator<String> i = cn.interfaces.iterator(); i.hasNext();) { - interfaces.add(Type.getObjectType(i.next().toString())); + interfaces.add(Type.getObjectType(i.next())); } for (int i = 0; i < methods.size(); ++i) { @@ -328,9 +330,14 @@ public class CheckClassAdapter extends ClassVisitor { * <tt>false</tt> to not perform any data flow check (see * {@link CheckMethodAdapter}). This option requires valid * maxLocals and maxStack values. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckClassAdapter(final ClassVisitor cv, final boolean checkDataFlow) { - this(Opcodes.ASM4, cv, checkDataFlow); + this(Opcodes.ASM5, cv, checkDataFlow); + if (getClass() != CheckClassAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -338,7 +345,7 @@ public class CheckClassAdapter extends 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 adapter must delegate calls. * @param checkDataFlow @@ -440,7 +447,15 @@ public class CheckClassAdapter extends ClassVisitor { CheckMethodAdapter.checkInternalName(outerName, "outer class name"); } if (innerName != null) { - CheckMethodAdapter.checkIdentifier(innerName, "inner class name"); + int start = 0; + while (start < innerName.length() + && Character.isDigit(innerName.charAt(start))) { + start++; + } + if (start == 0 || start < innerName.length()) { + CheckMethodAdapter.checkIdentifier(innerName, start, -1, + "inner class name"); + } } checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC @@ -517,6 +532,23 @@ public class CheckClassAdapter extends ClassVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkState(); + int sort = typeRef >>> 24; + if (sort != TypeReference.CLASS_TYPE_PARAMETER + && sort != TypeReference.CLASS_TYPE_PARAMETER_BOUND + && sort != TypeReference.CLASS_EXTENDS) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + + @Override public void visitAttribute(final Attribute attr) { checkState(); if (attr == null) { @@ -661,6 +693,77 @@ public class CheckClassAdapter extends ClassVisitor { } /** + * Checks the reference to a type in a type annotation. + * + * @param typeRef + * a reference to an annotated type. + * @param typePath + * the path to the annotated type argument, wildcard bound, array + * element type, or static inner type within 'typeRef'. May be + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + */ + static void checkTypeRefAndPath(int typeRef, TypePath typePath) { + int mask = 0; + switch (typeRef >>> 24) { + case TypeReference.CLASS_TYPE_PARAMETER: + case TypeReference.METHOD_TYPE_PARAMETER: + case TypeReference.METHOD_FORMAL_PARAMETER: + mask = 0xFFFF0000; + break; + case TypeReference.FIELD: + case TypeReference.METHOD_RETURN: + case TypeReference.METHOD_RECEIVER: + case TypeReference.LOCAL_VARIABLE: + case TypeReference.RESOURCE_VARIABLE: + case TypeReference.INSTANCEOF: + case TypeReference.NEW: + case TypeReference.CONSTRUCTOR_REFERENCE: + case TypeReference.METHOD_REFERENCE: + mask = 0xFF000000; + break; + case TypeReference.CLASS_EXTENDS: + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + case TypeReference.THROWS: + case TypeReference.EXCEPTION_PARAMETER: + mask = 0xFFFFFF00; + break; + case TypeReference.CAST: + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + mask = 0xFF0000FF; + break; + default: + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(typeRef >>> 24)); + } + if ((typeRef & ~mask) != 0) { + throw new IllegalArgumentException("Invalid type reference 0x" + + Integer.toHexString(typeRef)); + } + if (typePath != null) { + for (int i = 0; i < typePath.getLength(); ++i) { + int step = typePath.getStep(i); + if (step != TypePath.ARRAY_ELEMENT + && step != TypePath.INNER_TYPE + && step != TypePath.TYPE_ARGUMENT + && step != TypePath.WILDCARD_BOUND) { + throw new IllegalArgumentException( + "Invalid type path step " + i + " in " + typePath); + } + if (step != TypePath.TYPE_ARGUMENT + && typePath.getStepArgument(i) != 0) { + throw new IllegalArgumentException( + "Invalid type path step argument for step " + i + + " in " + typePath); + } + } + } + } + + /** * Checks the formal type parameters of a class or method signature. * * @param signature diff --git a/src/asm/scala/tools/asm/util/CheckFieldAdapter.java b/src/asm/scala/tools/asm/util/CheckFieldAdapter.java index 4657605936..e682df47af 100644 --- a/src/asm/scala/tools/asm/util/CheckFieldAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckFieldAdapter.java @@ -33,6 +33,8 @@ import scala.tools.asm.AnnotationVisitor; import scala.tools.asm.Attribute; import scala.tools.asm.FieldVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; /** * A {@link FieldVisitor} that checks that its methods are properly used. @@ -48,9 +50,14 @@ public class CheckFieldAdapter extends FieldVisitor { * * @param fv * the field visitor to which this adapter must delegate calls. + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckFieldAdapter(final FieldVisitor fv) { - this(Opcodes.ASM4, fv); + this(Opcodes.ASM5, fv); + if (getClass() != CheckFieldAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -58,7 +65,7 @@ public class CheckFieldAdapter extends 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 adapter must delegate calls. */ @@ -75,6 +82,21 @@ public class CheckFieldAdapter extends FieldVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkEnd(); + int sort = typeRef >>> 24; + if (sort != TypeReference.FIELD) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + + @Override public void visitAttribute(final Attribute attr) { checkEnd(); if (attr == null) { diff --git a/src/asm/scala/tools/asm/util/CheckMethodAdapter.java b/src/asm/scala/tools/asm/util/CheckMethodAdapter.java index 9da01c9d6e..131dfa5e5b 100644 --- a/src/asm/scala/tools/asm/util/CheckMethodAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckMethodAdapter.java @@ -46,6 +46,8 @@ import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; import scala.tools.asm.tree.MethodNode; import scala.tools.asm.tree.analysis.Analyzer; import scala.tools.asm.tree.analysis.BasicValue; @@ -390,10 +392,15 @@ public class CheckMethodAdapter extends MethodVisitor { * the method visitor to which this adapter must delegate calls. * @param labels * a map of already visited labels (in other methods). + * @throws IllegalStateException + * If a subclass calls this constructor. */ public CheckMethodAdapter(final MethodVisitor mv, final Map<Label, Integer> labels) { - this(Opcodes.ASM4, mv, labels); + this(Opcodes.ASM5, mv, labels); + if (getClass() != CheckMethodAdapter.class) { + throw new IllegalStateException(); + } } /** @@ -434,7 +441,7 @@ public class CheckMethodAdapter extends MethodVisitor { public CheckMethodAdapter(final int access, final String name, final String desc, final MethodVisitor cmv, final Map<Label, Integer> labels) { - this(new MethodNode(access, name, desc, null, null) { + this(new MethodNode(Opcodes.ASM5, access, name, desc, null, null) { @Override public void visitEnd() { Analyzer<BasicValue> a = new Analyzer<BasicValue>( @@ -462,6 +469,16 @@ public class CheckMethodAdapter extends MethodVisitor { } @Override + public void visitParameter(String name, int access) { + if (name != null) { + checkUnqualifiedName(version, name, "name"); + } + CheckClassAdapter.checkAccess(access, Opcodes.ACC_FINAL + + Opcodes.ACC_MANDATED + Opcodes.ACC_SYNTHETIC); + super.visitParameter(name, access); + } + + @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { checkEndMethod(); @@ -470,6 +487,26 @@ public class CheckMethodAdapter extends MethodVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkEndMethod(); + int sort = typeRef >>> 24; + if (sort != TypeReference.METHOD_TYPE_PARAMETER + && sort != TypeReference.METHOD_TYPE_PARAMETER_BOUND + && sort != TypeReference.METHOD_RETURN + && sort != TypeReference.METHOD_RECEIVER + && sort != TypeReference.METHOD_FORMAL_PARAMETER + && sort != TypeReference.THROWS) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, + typePath, desc, visible)); + } + + @Override public AnnotationVisitor visitAnnotationDefault() { checkEndMethod(); return new CheckAnnotationAdapter(super.visitAnnotationDefault(), false); @@ -647,9 +684,30 @@ public class CheckMethodAdapter extends MethodVisitor { ++insnCount; } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, + opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, itf); + } + + private void doVisitMethodInsn(int opcode, final String owner, + final String name, final String desc, final boolean itf) { checkStartCode(); checkEndCode(); checkOpcode(opcode, 5); @@ -658,7 +716,21 @@ public class CheckMethodAdapter extends MethodVisitor { } checkInternalName(owner, "owner"); checkMethodDesc(desc); - super.visitMethodInsn(opcode, owner, name, desc); + if (opcode == Opcodes.INVOKEVIRTUAL && itf) { + throw new IllegalArgumentException( + "INVOKEVIRTUAL can't be used with interfaces"); + } + if (opcode == Opcodes.INVOKEINTERFACE && !itf) { + throw new IllegalArgumentException( + "INVOKEINTERFACE can't be used with classes"); + } + // Calling super.visitMethodInsn requires to call the correct version + // depending on this.api (otherwise infinite loops can occur). To + // simplify and to make it easier to automatically remove the backward + // compatibility code, we inline the code of the overridden method here. + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } ++insnCount; } @@ -797,6 +869,29 @@ public class CheckMethodAdapter extends MethodVisitor { } @Override + public AnnotationVisitor visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.INSTANCEOF && sort != TypeReference.NEW + && sort != TypeReference.CONSTRUCTOR_REFERENCE + && sort != TypeReference.METHOD_REFERENCE + && sort != TypeReference.CAST + && sort != TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT + && sort != TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT + && sort != TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitInsnAnnotation(typeRef, + typePath, desc, visible)); + } + + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { checkStartCode(); @@ -821,6 +916,22 @@ public class CheckMethodAdapter extends MethodVisitor { } @Override + public AnnotationVisitor visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.EXCEPTION_PARAMETER) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(super.visitTryCatchAnnotation( + typeRef, typePath, desc, visible)); + } + + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { @@ -841,6 +952,40 @@ public class CheckMethodAdapter extends MethodVisitor { } @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + checkStartCode(); + checkEndCode(); + int sort = typeRef >>> 24; + if (sort != TypeReference.LOCAL_VARIABLE + && sort != TypeReference.RESOURCE_VARIABLE) { + throw new IllegalArgumentException("Invalid type reference sort 0x" + + Integer.toHexString(sort)); + } + CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath); + checkDesc(desc, false); + if (start == null || end == null || index == null + || end.length != start.length || index.length != start.length) { + throw new IllegalArgumentException( + "Invalid start, end and index arrays (must be non null and of identical length"); + } + for (int i = 0; i < start.length; ++i) { + checkLabel(start[i], true, "start label"); + checkLabel(end[i], true, "end label"); + checkUnsignedShort(index[i], "Invalid variable index"); + int s = labels.get(start[i]).intValue(); + int e = labels.get(end[i]).intValue(); + if (e < s) { + throw new IllegalArgumentException( + "Invalid start and end labels (end must be greater than start)"); + } + } + return super.visitLocalVariableAnnotation(typeRef, typePath, start, + end, index, desc, visible); + } + + @Override public void visitLineNumber(final int line, final Label start) { checkStartCode(); checkEndCode(); @@ -1202,7 +1347,7 @@ public class CheckMethodAdapter extends MethodVisitor { checkIdentifier(name, begin, slash, null); begin = slash + 1; } while (slash != max); - } catch (IllegalArgumentException _) { + } catch (IllegalArgumentException unused) { throw new IllegalArgumentException( "Invalid " + msg @@ -1280,7 +1425,7 @@ public class CheckMethodAdapter extends MethodVisitor { } try { checkInternalName(desc, start + 1, index, null); - } catch (IllegalArgumentException _) { + } catch (IllegalArgumentException unused) { throw new IllegalArgumentException("Invalid descriptor: " + desc); } diff --git a/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java b/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java index e69302b8a6..54c9033c90 100644 --- a/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java +++ b/src/asm/scala/tools/asm/util/CheckSignatureAdapter.java @@ -113,7 +113,7 @@ public class CheckSignatureAdapter extends SignatureVisitor { * <tt>null</tt>. */ public CheckSignatureAdapter(final int type, final SignatureVisitor sv) { - this(Opcodes.ASM4, type, sv); + this(Opcodes.ASM5, type, sv); } /** @@ -121,7 +121,7 @@ public class CheckSignatureAdapter extends SignatureVisitor { * * @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 type * the type of signature to be checked. See * {@link #CLASS_SIGNATURE}, {@link #METHOD_SIGNATURE} and diff --git a/src/asm/scala/tools/asm/util/Printer.java b/src/asm/scala/tools/asm/util/Printer.java index 86e0f9e122..4135672c6b 100644 --- a/src/asm/scala/tools/asm/util/Printer.java +++ b/src/asm/scala/tools/asm/util/Printer.java @@ -37,6 +37,7 @@ import scala.tools.asm.Attribute; import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * An abstract converter from visit events to text. @@ -116,7 +117,7 @@ public abstract class Printer { /** * The ASM API version implemented by this class. 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; @@ -175,6 +176,15 @@ public abstract class Printer { final boolean visible); /** + * Class type annotation. See + * {@link scala.tools.asm.ClassVisitor#visitTypeAnnotation}. + */ + public Printer visitClassTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** * Class attribute. See * {@link scala.tools.asm.ClassVisitor#visitAttribute}. */ @@ -249,6 +259,15 @@ public abstract class Printer { final boolean visible); /** + * Field type annotation. See + * {@link scala.tools.asm.FieldVisitor#visitTypeAnnotation}. + */ + public Printer visitFieldTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** * Field attribute. See * {@link scala.tools.asm.FieldVisitor#visitAttribute}. */ @@ -264,6 +283,14 @@ public abstract class Printer { // ------------------------------------------------------------------------ /** + * Method parameter. See + * {@link scala.tools.asm.MethodVisitor#visitParameter(String, int)}. + */ + public void visitParameter(String name, int access) { + throw new RuntimeException("Must be overriden"); + } + + /** * Method default annotation. See * {@link scala.tools.asm.MethodVisitor#visitAnnotationDefault}. */ @@ -277,6 +304,15 @@ public abstract class Printer { final boolean visible); /** + * Method type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitTypeAnnotation}. + */ + public Printer visitMethodTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** * Method parameter annotation. See * {@link scala.tools.asm.MethodVisitor#visitParameterAnnotation}. */ @@ -336,8 +372,33 @@ public abstract class Printer { * Method instruction. See * {@link scala.tools.asm.MethodVisitor#visitMethodInsn}. */ - public abstract void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc); + @Deprecated + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc) { + if (api >= Opcodes.ASM5) { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + throw new RuntimeException("Must be overriden"); + } + + /** + * Method instruction. See + * {@link scala.tools.asm.MethodVisitor#visitMethodInsn}. + */ + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final 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; + } + throw new RuntimeException("Must be overriden"); + } /** * Method instruction. See @@ -391,6 +452,15 @@ public abstract class Printer { final int dims); /** + * Instruction type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitInsnAnnotation}. + */ + public Printer visitInsnAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** * Method exception handler. See * {@link scala.tools.asm.MethodVisitor#visitTryCatchBlock}. */ @@ -398,6 +468,15 @@ public abstract class Printer { final Label handler, final String type); /** + * Try catch block type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitTryCatchAnnotation}. + */ + public Printer visitTryCatchAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** * Method debug info. See * {@link scala.tools.asm.MethodVisitor#visitLocalVariable}. */ @@ -406,6 +485,16 @@ public abstract class Printer { final Label end, final int index); /** + * Local variable type annotation. See + * {@link scala.tools.asm.MethodVisitor#visitTryCatchAnnotation}. + */ + public Printer visitLocalVariableAnnotation(final int typeRef, + final TypePath typePath, final Label[] start, final Label[] end, + final int[] index, final String desc, final boolean visible) { + throw new RuntimeException("Must be overriden"); + } + + /** * Method debug info. See * {@link scala.tools.asm.MethodVisitor#visitLineNumber}. */ diff --git a/src/asm/scala/tools/asm/util/Textifier.java b/src/asm/scala/tools/asm/util/Textifier.java index a5c4f6779e..373e46f5ed 100644 --- a/src/asm/scala/tools/asm/util/Textifier.java +++ b/src/asm/scala/tools/asm/util/Textifier.java @@ -40,6 +40,8 @@ import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.Opcodes; import scala.tools.asm.Type; +import scala.tools.asm.TypePath; +import scala.tools.asm.TypeReference; import scala.tools.asm.signature.SignatureReader; /** @@ -135,15 +137,26 @@ public class Textifier extends Printer { */ protected Map<Label, String> labelNames; + /** + * Class access flags + */ + private int access; + private int valueNumber = 0; /** * Constructs a new {@link Textifier}. <i>Subclasses must not use this * constructor</i>. Instead, they must use the {@link #Textifier(int)} * version. + * + * @throws IllegalStateException + * If a subclass calls this constructor. */ public Textifier() { - this(Opcodes.ASM4); + this(Opcodes.ASM5); + if (getClass() != Textifier.class) { + throw new IllegalStateException(); + } } /** @@ -151,7 +164,7 @@ public class Textifier extends Printer { * * @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}. */ protected Textifier(final int api) { super(api); @@ -208,6 +221,7 @@ public class Textifier extends Printer { public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { + this.access = access; int major = version & 0xFFFF; int minor = version >>> 16; buf.setLength(0); @@ -294,6 +308,13 @@ public class Textifier extends Printer { } @Override + public Printer visitClassTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + text.add("\n"); + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override public void visitClassAttribute(final Attribute attr) { text.add("\n"); visitAttribute(attr); @@ -393,7 +414,7 @@ public class Textifier extends Printer { } buf.append(tab); - appendAccess(access); + appendAccess(access & ~Opcodes.ACC_VOLATILE); if ((access & Opcodes.ACC_NATIVE) != 0) { buf.append("native "); } @@ -403,6 +424,11 @@ public class Textifier extends Printer { if ((access & Opcodes.ACC_BRIDGE) != 0) { buf.append("bridge "); } + if ((this.access & Opcodes.ACC_INTERFACE) != 0 + && (access & Opcodes.ACC_ABSTRACT) == 0 + && (access & Opcodes.ACC_STATIC) == 0) { + buf.append("default "); + } buf.append(name); appendDescriptor(METHOD_DESCRIPTOR, desc); @@ -617,6 +643,12 @@ public class Textifier extends Printer { } @Override + public Printer visitFieldTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override public void visitFieldAttribute(final Attribute attr) { visitAttribute(attr); } @@ -630,6 +662,16 @@ public class Textifier extends Printer { // ------------------------------------------------------------------------ @Override + public void visitParameter(final String name, final int access) { + buf.setLength(0); + buf.append(tab2).append("// parameter "); + appendAccess(access); + buf.append(' ').append((name == null) ? "<no name>" : name) + .append('\n'); + text.add(buf.toString()); + } + + @Override public Textifier visitAnnotationDefault() { text.add(tab2 + "default="); Textifier t = createTextifier(); @@ -645,6 +687,12 @@ public class Textifier extends Printer { } @Override + public Printer visitMethodTypeAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override public Textifier visitParameterAnnotation(final int parameter, final String desc, final boolean visible) { buf.setLength(0); @@ -761,9 +809,30 @@ public class Textifier extends Printer { text.add(buf.toString()); } + @Deprecated @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, + opcode == Opcodes.INVOKEINTERFACE); + } + + @Override + public void visitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + doVisitMethodInsn(opcode, owner, name, desc, itf); + } + + private void doVisitMethodInsn(final int opcode, final String owner, + final String name, final String desc, final boolean itf) { buf.setLength(0); buf.append(tab2).append(OPCODES[opcode]).append(' '); appendDescriptor(INTERNAL_NAME, owner); @@ -781,26 +850,35 @@ public class Textifier extends Printer { buf.append(name); appendDescriptor(METHOD_DESCRIPTOR, desc); buf.append(" ["); + buf.append('\n'); + buf.append(tab3); appendHandle(bsm); + buf.append('\n'); buf.append(tab3).append("// arguments:"); if (bsmArgs.length == 0) { buf.append(" none"); } else { - buf.append('\n').append(tab3); + buf.append('\n'); for (int i = 0; i < bsmArgs.length; i++) { + buf.append(tab3); Object cst = bsmArgs[i]; if (cst instanceof String) { Printer.appendString(buf, (String) cst); } else if (cst instanceof Type) { - buf.append(((Type) cst).getDescriptor()).append(".class"); + Type type = (Type) cst; + if(type.getSort() == Type.METHOD){ + appendDescriptor(METHOD_DESCRIPTOR, type.getDescriptor()); + } else { + buf.append(type.getDescriptor()).append(".class"); + } } else if (cst instanceof Handle) { appendHandle((Handle) cst); } else { buf.append(cst); } - buf.append(", "); + buf.append(", \n"); } - buf.setLength(buf.length() - 2); + buf.setLength(buf.length() - 3); } buf.append('\n'); buf.append(tab2).append("]\n"); @@ -890,6 +968,12 @@ public class Textifier extends Printer { } @Override + public Printer visitInsnAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + return visitTypeAnnotation(typeRef, typePath, desc, visible); + } + + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { buf.setLength(0); @@ -906,6 +990,25 @@ public class Textifier extends Printer { } @Override + public Printer visitTryCatchAnnotation(int typeRef, TypePath typePath, + String desc, boolean visible) { + buf.setLength(0); + buf.append(tab2).append("TRYCATCHBLOCK @"); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { @@ -932,6 +1035,33 @@ public class Textifier extends Printer { } @Override + public Printer visitLocalVariableAnnotation(int typeRef, TypePath typePath, + Label[] start, Label[] end, int[] index, String desc, + boolean visible) { + buf.setLength(0); + buf.append(tab2).append("LOCALVARIABLE @"); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + for (int i = 0; i < start.length; ++i) { + buf.append(" [ "); + appendLabel(start[i]); + buf.append(" - "); + appendLabel(end[i]); + buf.append(" - ").append(index[i]).append(" ]"); + } + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + + @Override public void visitLineNumber(final int line, final Label start) { buf.setLength(0); buf.append(tab2).append("LINENUMBER ").append(line).append(' '); @@ -981,6 +1111,39 @@ public class Textifier extends Printer { } /** + * Prints a disassembled view of the given type annotation. + * + * @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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values. + */ + public Textifier visitTypeAnnotation(final int typeRef, + final TypePath typePath, final String desc, final boolean visible) { + buf.setLength(0); + buf.append(tab).append('@'); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + Textifier t = createTextifier(); + text.add(t.getText()); + buf.setLength(0); + buf.append(") : "); + appendTypeReference(typeRef); + buf.append(", ").append(typePath); + buf.append(visible ? "\n" : " // invisible\n"); + text.add(buf.toString()); + return t; + } + + /** * Prints a disassembled view of the given attribute. * * @param attr @@ -1061,10 +1224,10 @@ public class Textifier extends Printer { * a handle, non null. */ protected void appendHandle(final Handle h) { - buf.append('\n').append(tab3); int tag = h.getTag(); buf.append("// handle kind 0x").append(Integer.toHexString(tag)) .append(" : "); + boolean isMethodHandle = false; switch (tag) { case Opcodes.H_GETFIELD: buf.append("GETFIELD"); @@ -1080,18 +1243,23 @@ public class Textifier extends Printer { break; case Opcodes.H_INVOKEINTERFACE: buf.append("INVOKEINTERFACE"); + isMethodHandle = true; break; case Opcodes.H_INVOKESPECIAL: buf.append("INVOKESPECIAL"); + isMethodHandle = true; break; case Opcodes.H_INVOKESTATIC: buf.append("INVOKESTATIC"); + isMethodHandle = true; break; case Opcodes.H_INVOKEVIRTUAL: buf.append("INVOKEVIRTUAL"); + isMethodHandle = true; break; case Opcodes.H_NEWINVOKESPECIAL: buf.append("NEWINVOKESPECIAL"); + isMethodHandle = true; break; } buf.append('\n'); @@ -1099,9 +1267,13 @@ public class Textifier extends Printer { appendDescriptor(INTERNAL_NAME, h.getOwner()); buf.append('.'); buf.append(h.getName()); - buf.append('('); + if(!isMethodHandle){ + buf.append('('); + } appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc()); - buf.append(')').append('\n'); + if(!isMethodHandle){ + buf.append(')'); + } } /** @@ -1145,6 +1317,9 @@ public class Textifier extends Printer { if ((access & Opcodes.ACC_SYNTHETIC) != 0) { buf.append("synthetic "); } + if ((access & Opcodes.ACC_MANDATED) != 0) { + buf.append("mandated "); + } if ((access & Opcodes.ACC_ENUM) != 0) { buf.append("enum "); } @@ -1156,6 +1331,90 @@ public class Textifier extends Printer { } } + private void appendTypeReference(final int typeRef) { + TypeReference ref = new TypeReference(typeRef); + switch (ref.getSort()) { + case TypeReference.CLASS_TYPE_PARAMETER: + buf.append("CLASS_TYPE_PARAMETER ").append( + ref.getTypeParameterIndex()); + break; + case TypeReference.METHOD_TYPE_PARAMETER: + buf.append("METHOD_TYPE_PARAMETER ").append( + ref.getTypeParameterIndex()); + break; + case TypeReference.CLASS_EXTENDS: + buf.append("CLASS_EXTENDS ").append(ref.getSuperTypeIndex()); + break; + case TypeReference.CLASS_TYPE_PARAMETER_BOUND: + buf.append("CLASS_TYPE_PARAMETER_BOUND ") + .append(ref.getTypeParameterIndex()).append(", ") + .append(ref.getTypeParameterBoundIndex()); + break; + case TypeReference.METHOD_TYPE_PARAMETER_BOUND: + buf.append("METHOD_TYPE_PARAMETER_BOUND ") + .append(ref.getTypeParameterIndex()).append(", ") + .append(ref.getTypeParameterBoundIndex()); + break; + case TypeReference.FIELD: + buf.append("FIELD"); + break; + case TypeReference.METHOD_RETURN: + buf.append("METHOD_RETURN"); + break; + case TypeReference.METHOD_RECEIVER: + buf.append("METHOD_RECEIVER"); + break; + case TypeReference.METHOD_FORMAL_PARAMETER: + buf.append("METHOD_FORMAL_PARAMETER ").append( + ref.getFormalParameterIndex()); + break; + case TypeReference.THROWS: + buf.append("THROWS ").append(ref.getExceptionIndex()); + break; + case TypeReference.LOCAL_VARIABLE: + buf.append("LOCAL_VARIABLE"); + break; + case TypeReference.RESOURCE_VARIABLE: + buf.append("RESOURCE_VARIABLE"); + break; + case TypeReference.EXCEPTION_PARAMETER: + buf.append("EXCEPTION_PARAMETER ").append( + ref.getTryCatchBlockIndex()); + break; + case TypeReference.INSTANCEOF: + buf.append("INSTANCEOF"); + break; + case TypeReference.NEW: + buf.append("NEW"); + break; + case TypeReference.CONSTRUCTOR_REFERENCE: + buf.append("CONSTRUCTOR_REFERENCE"); + break; + case TypeReference.METHOD_REFERENCE: + buf.append("METHOD_REFERENCE"); + break; + case TypeReference.CAST: + buf.append("CAST ").append(ref.getTypeArgumentIndex()); + break; + case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + buf.append("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: + buf.append("METHOD_INVOCATION_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + buf.append("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: + buf.append("METHOD_REFERENCE_TYPE_ARGUMENT ").append( + ref.getTypeArgumentIndex()); + break; + } + } + private void appendFrameTypes(final int n, final Object[] o) { for (int i = 0; i < n; ++i) { if (i > 0) { diff --git a/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java b/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java index 33e7cf0b26..7a9dbfef06 100644 --- a/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceAnnotationVisitor.java @@ -47,7 +47,7 @@ public final class TraceAnnotationVisitor extends AnnotationVisitor { } public TraceAnnotationVisitor(final AnnotationVisitor av, final Printer p) { - super(Opcodes.ASM4, av); + super(Opcodes.ASM5, av); this.p = p; } diff --git a/src/asm/scala/tools/asm/util/TraceClassVisitor.java b/src/asm/scala/tools/asm/util/TraceClassVisitor.java index ff7a017482..842d286672 100644 --- a/src/asm/scala/tools/asm/util/TraceClassVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceClassVisitor.java @@ -37,6 +37,7 @@ import scala.tools.asm.ClassVisitor; import scala.tools.asm.FieldVisitor; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A {@link ClassVisitor} that prints the classes it visits with a @@ -130,7 +131,7 @@ public final class TraceClassVisitor extends ClassVisitor { */ public TraceClassVisitor(final ClassVisitor cv, final Printer p, final PrintWriter pw) { - super(Opcodes.ASM4, cv); + super(Opcodes.ASM5, cv); this.pw = pw; this.p = p; } @@ -166,6 +167,16 @@ public final class TraceClassVisitor extends ClassVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitClassTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = cv == null ? null : cv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + + @Override public void visitAttribute(final Attribute attr) { p.visitClassAttribute(attr); super.visitAttribute(attr); diff --git a/src/asm/scala/tools/asm/util/TraceFieldVisitor.java b/src/asm/scala/tools/asm/util/TraceFieldVisitor.java index 9547a70008..1d0743a424 100644 --- a/src/asm/scala/tools/asm/util/TraceFieldVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceFieldVisitor.java @@ -33,6 +33,7 @@ import scala.tools.asm.AnnotationVisitor; import scala.tools.asm.Attribute; import scala.tools.asm.FieldVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A {@link FieldVisitor} that prints the fields it visits with a @@ -49,7 +50,7 @@ public final class TraceFieldVisitor extends FieldVisitor { } public TraceFieldVisitor(final FieldVisitor fv, final Printer p) { - super(Opcodes.ASM4, fv); + super(Opcodes.ASM5, fv); this.p = p; } @@ -63,6 +64,16 @@ public final class TraceFieldVisitor extends FieldVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitFieldTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = fv == null ? null : fv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + + @Override public void visitAttribute(final Attribute attr) { p.visitFieldAttribute(attr); super.visitAttribute(attr); diff --git a/src/asm/scala/tools/asm/util/TraceMethodVisitor.java b/src/asm/scala/tools/asm/util/TraceMethodVisitor.java index 9034567c8f..db5f051003 100644 --- a/src/asm/scala/tools/asm/util/TraceMethodVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceMethodVisitor.java @@ -35,6 +35,7 @@ import scala.tools.asm.Handle; import scala.tools.asm.Label; import scala.tools.asm.MethodVisitor; import scala.tools.asm.Opcodes; +import scala.tools.asm.TypePath; /** * A {@link MethodVisitor} that prints the methods it visits with a @@ -51,11 +52,17 @@ public final class TraceMethodVisitor extends MethodVisitor { } public TraceMethodVisitor(final MethodVisitor mv, final Printer p) { - super(Opcodes.ASM4, mv); + super(Opcodes.ASM5, mv); this.p = p; } @Override + public void visitParameter(String name, int access) { + p.visitParameter(name, access); + super.visitParameter(name, access); + } + + @Override public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) { Printer p = this.p.visitMethodAnnotation(desc, visible); @@ -65,6 +72,16 @@ public final class TraceMethodVisitor extends MethodVisitor { } @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitMethodTypeAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = mv == null ? null : mv.visitTypeAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + + @Override public void visitAttribute(final Attribute attr) { p.visitMethodAttribute(attr); super.visitAttribute(attr); @@ -130,11 +147,31 @@ public final class TraceMethodVisitor extends MethodVisitor { super.visitFieldInsn(opcode, owner, name, desc); } + @Deprecated @Override - public void visitMethodInsn(final int opcode, final String owner, - final String name, final String desc) { + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + if (api >= Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc); + return; + } p.visitMethodInsn(opcode, owner, name, desc); - super.visitMethodInsn(opcode, owner, name, desc); + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + if (api < Opcodes.ASM5) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + p.visitMethodInsn(opcode, owner, name, desc, itf); + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } } @Override @@ -189,6 +226,16 @@ public final class TraceMethodVisitor extends MethodVisitor { } @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p + .visitInsnAnnotation(typeRef, typePath, desc, visible); + AnnotationVisitor av = mv == null ? null : mv.visitInsnAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + + @Override public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { p.visitTryCatchBlock(start, end, handler, type); @@ -196,6 +243,16 @@ public final class TraceMethodVisitor extends MethodVisitor { } @Override + public AnnotationVisitor visitTryCatchAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + Printer p = this.p.visitTryCatchAnnotation(typeRef, typePath, desc, + visible); + AnnotationVisitor av = mv == null ? null : mv.visitTryCatchAnnotation( + typeRef, typePath, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + + @Override public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end, final int index) { @@ -204,6 +261,18 @@ public final class TraceMethodVisitor extends MethodVisitor { } @Override + public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, + TypePath typePath, Label[] start, Label[] end, int[] index, + String desc, boolean visible) { + Printer p = this.p.visitLocalVariableAnnotation(typeRef, typePath, + start, end, index, desc, visible); + AnnotationVisitor av = mv == null ? null : mv + .visitLocalVariableAnnotation(typeRef, typePath, start, end, + index, desc, visible); + return new TraceAnnotationVisitor(av, p); + } + + @Override public void visitLineNumber(final int line, final Label start) { p.visitLineNumber(line, start); super.visitLineNumber(line, start); diff --git a/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java b/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java index 1e23c7ef1a..f99ec2b0c2 100644 --- a/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java +++ b/src/asm/scala/tools/asm/util/TraceSignatureVisitor.java @@ -75,13 +75,13 @@ public final class TraceSignatureVisitor extends SignatureVisitor { private String separator = ""; public TraceSignatureVisitor(final int access) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); isInterface = (access & Opcodes.ACC_INTERFACE) != 0; this.declaration = new StringBuffer(); } private TraceSignatureVisitor(final StringBuffer buf) { - super(Opcodes.ASM4); + super(Opcodes.ASM5); this.declaration = buf; } diff --git a/src/build/bnd/scala-actors.bnd b/src/build/bnd/scala-actors.bnd index 8d0555777f..69885fc2bf 100644 --- a/src/build/bnd/scala-actors.bnd +++ b/src/build/bnd/scala-actors.bnd @@ -3,3 +3,5 @@ Bundle-SymbolicName: org.scala-lang.scala-actors ver: @VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);${ver}}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-compiler-doc.bnd b/src/build/bnd/scala-compiler-doc.bnd index 4910e5fcb0..9d6d0304d1 100644 --- a/src/build/bnd/scala-compiler-doc.bnd +++ b/src/build/bnd/scala-compiler-doc.bnd @@ -3,4 +3,5 @@ Bundle-SymbolicName: org.scala-lang.modules.scala-compiler-doc_@SCALA_BINARY_VER ver: @SCALA_COMPILER_DOC_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} -Import-Package: * +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-compiler-interactive.bnd b/src/build/bnd/scala-compiler-interactive.bnd index 34d2f2956d..07e3de35b0 100644 --- a/src/build/bnd/scala-compiler-interactive.bnd +++ b/src/build/bnd/scala-compiler-interactive.bnd @@ -3,4 +3,5 @@ Bundle-SymbolicName: org.scala-lang.modules.scala-compiler-interactive_@SCALA_BI ver: @SCALA_COMPILER_INTERACTIVE_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} -Import-Package: * +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-compiler.bnd b/src/build/bnd/scala-compiler.bnd index dc30513db4..2bd24d780d 100644 --- a/src/build/bnd/scala-compiler.bnd +++ b/src/build/bnd/scala-compiler.bnd @@ -5,4 +5,8 @@ Bundle-Version: ${ver} Export-Package: *;version=${ver} Import-Package: jline.*;resolution:=optional, \ org.apache.tools.ant.*;resolution:=optional, \ + scala.util.parsing.*;version="${range;[====,====];@PARSER_COMBINATORS_VERSION@}";resolution:=optional, \ + scala.xml.*;version="${range;[====,====];@XML_VERSION@}";resolution:=optional, \ + scala.*;version="${range;[==,=+);${ver}}", \ * +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-continuations-library.bnd b/src/build/bnd/scala-continuations-library.bnd index bb505b60a9..b36718cc5b 100644 --- a/src/build/bnd/scala-continuations-library.bnd +++ b/src/build/bnd/scala-continuations-library.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Delimited Continuations Library Bundle-SymbolicName: org.scala-lang.plugins.scala-continuations-library -ver: @VERSION@ +ver: @CONTINUATIONS_LIBRARY_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-continuations-plugin.bnd b/src/build/bnd/scala-continuations-plugin.bnd index cd66614a22..2f2464b452 100644 --- a/src/build/bnd/scala-continuations-plugin.bnd +++ b/src/build/bnd/scala-continuations-plugin.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Delimited Continuations Compiler Plugin Bundle-SymbolicName: org.scala-lang.plugins.scala-continuations-plugin -ver: @VERSION@ +ver: @CONTINUATIONS_PLUGIN_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-library.bnd b/src/build/bnd/scala-library.bnd index 03aff45672..7eb4fa4b2a 100644 --- a/src/build/bnd/scala-library.bnd +++ b/src/build/bnd/scala-library.bnd @@ -4,3 +4,4 @@ ver: @VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} Import-Package: sun.misc;resolution:=optional, * +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-parser-combinators.bnd b/src/build/bnd/scala-parser-combinators.bnd index 6ffc3b2760..ef8646cbd0 100644 --- a/src/build/bnd/scala-parser-combinators.bnd +++ b/src/build/bnd/scala-parser-combinators.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Parser Combinators Library Bundle-SymbolicName: org.scala-lang.modules.scala-parser-combinators -ver: @VERSION@ +ver: @PARSER_COMBINATORS_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-reflect.bnd b/src/build/bnd/scala-reflect.bnd index 6cda346d3a..e4bc54e52e 100644 --- a/src/build/bnd/scala-reflect.bnd +++ b/src/build/bnd/scala-reflect.bnd @@ -3,4 +3,7 @@ Bundle-SymbolicName: org.scala-lang.scala-reflect ver: @VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} -Import-Package: scala.tools.nsc;resolution:=optional, * +Import-Package: scala.*;version="${range;[==,=+);${ver}}", \ + scala.tools.nsc;resolution:=optional;version="${range;[==,=+);${ver}}", \ + * +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/bnd/scala-swing.bnd b/src/build/bnd/scala-swing.bnd index 7cccb1343b..f8b50baa91 100644 --- a/src/build/bnd/scala-swing.bnd +++ b/src/build/bnd/scala-swing.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala Swing Bundle-SymbolicName: org.scala-lang.modules.scala-swing -ver: @VERSION@ +ver: @SCALA_SWING_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6,JavaSE-1.7 diff --git a/src/build/bnd/scala-xml.bnd b/src/build/bnd/scala-xml.bnd index 5d64c05e65..01bf0144eb 100644 --- a/src/build/bnd/scala-xml.bnd +++ b/src/build/bnd/scala-xml.bnd @@ -1,5 +1,7 @@ Bundle-Name: Scala XML Library Bundle-SymbolicName: org.scala-lang.modules.scala-xml -ver: @VERSION@ +ver: @XML_VERSION@ Bundle-Version: ${ver} Export-Package: *;version=${ver} +Import-Package: scala.*;version="${range;[==,=+);@VERSION@}",* +Bundle-RequiredExecutionEnvironment: JavaSE-1.6, JavaSE-1.7 diff --git a/src/build/maven/scala-dist-pom.xml b/src/build/maven/scala-dist-pom.xml index 22a24dea21..9477e14285 100644 --- a/src/build/maven/scala-dist-pom.xml +++ b/src/build/maven/scala-dist-pom.xml @@ -40,6 +40,11 @@ <version>@VERSION@</version> </dependency> <dependency> + <groupId>org.scala-lang</groupId> + <artifactId>scalap</artifactId> + <version>@VERSION@</version> + </dependency> + <dependency> <groupId>org.scala-lang.plugins</groupId> <!-- plugins are fully cross-versioned. But, we don't publish with 2.11.0-SNAPSHOT, instead use full version of the last non-snapshot version --> <artifactId>scala-continuations-plugin_@SCALA_FULL_VERSION@</artifactId> diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 0da66d43f7..0ad3c2c76b 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1424,11 +1424,18 @@ abstract class GenICode extends SubComponent { def genZandOrZor(and: Boolean): Boolean = { val ctxInterm = ctx.newBlock() - val branchesReachable = if (and) genCond(lhs, ctx, ctxInterm, elseCtx) + val lhsBranchesReachable = if (and) genCond(lhs, ctx, ctxInterm, elseCtx) else genCond(lhs, ctx, thenCtx, ctxInterm) - ctxInterm.bb killUnless branchesReachable + // If lhs is known to throw, we can kill the just created ctxInterm. + ctxInterm.bb killUnless lhsBranchesReachable - genCond(rhs, ctxInterm, thenCtx, elseCtx) + val rhsBranchesReachable = genCond(rhs, ctxInterm, thenCtx, elseCtx) + + // Reachable means "it does not always throw", i.e. "it might not throw". + // In an expression (a && b) or (a || b), the b branch might not be evaluated. + // Such an expression is therefore known to throw only if both expressions throw. Or, + // successors are reachable if either of the two is reachable (SI-8625). + lhsBranchesReachable || rhsBranchesReachable } def genRefEq(isEq: Boolean) = { val f = genEqEqPrimitive(lhs, rhs, ctx) _ diff --git a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala new file mode 100644 index 0000000000..2af2037fec --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala @@ -0,0 +1,61 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2014 LAMP/EPFL + * @author Martin Odersky + */ + +package scala.tools.nsc.backend.jvm + +import scala.tools.asm.tree.{ClassNode, MethodNode} +import java.io.PrintWriter +import scala.tools.asm.util.{TraceClassVisitor, TraceMethodVisitor, Textifier} +import scala.tools.asm.ClassReader + +object AsmUtils { + + /** + * Print the bytecode of methods generated by GenBCode to the standard output. Only methods + * whose name contains `traceMethodPattern` are traced. + */ + final val traceMethodEnabled = false + final val traceMethodPattern = "" + + /** + * Print the bytecode of classes generated by GenBCode to the standard output. + */ + final val traceClassEnabled = false + final val traceClassPattern = "" + + /** + * Print the bytedcode of classes as they are serialized by the ASM library. The serialization + * performed by `asm.ClassWriter` can change the code generated by GenBCode. For example, it + * introduces stack map frames, it computes the maximal stack sizes, and it replaces dead + * code by NOPs (see also https://github.com/scala/scala/pull/3726#issuecomment-42861780). + */ + final val traceSerializedClassEnabled = false + final val traceSerializedClassPattern = "" + + def traceMethod(mnode: MethodNode): Unit = { + println(s"Bytecode for method ${mnode.name}") + val p = new Textifier + val tracer = new TraceMethodVisitor(p) + mnode.accept(tracer) + val w = new PrintWriter(System.out) + p.print(w) + w.flush() + } + + def traceClass(cnode: ClassNode): Unit = { + println(s"Bytecode for class ${cnode.name}") + val w = new PrintWriter(System.out) + cnode.accept(new TraceClassVisitor(w)) + w.flush() + } + + def traceClass(bytes: Array[Byte]): Unit = traceClass(readClass(bytes)) + + def readClass(bytes: Array[Byte]): ClassNode = { + val node = new ClassNode() + new ClassReader(bytes).accept(node, 0) + node + } +} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 92ebe5027a..1285ca6862 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -24,6 +24,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { import global._ import definitions._ import bCodeICodeCommon._ + import bTypes._ /* * Functionality to build the body of ASM MethodNode, except for `synchronized` and `try` expressions. @@ -46,16 +47,16 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def emit(opc: Int) { mnode.visitInsn(opc) } def emitZeroOf(tk: BType) { - (tk.sort: @switch) match { - case asm.Type.BOOLEAN => bc.boolconst(false) - case asm.Type.BYTE | - asm.Type.SHORT | - asm.Type.CHAR | - asm.Type.INT => bc.iconst(0) - case asm.Type.LONG => bc.lconst(0) - case asm.Type.FLOAT => bc.fconst(0) - case asm.Type.DOUBLE => bc.dconst(0) - case asm.Type.VOID => () + tk match { + case BOOL => bc.boolconst(false) + case BYTE | + SHORT | + CHAR | + INT => bc.iconst(0) + case LONG => bc.lconst(0) + case FLOAT => bc.fconst(0) + case DOUBLE => bc.dconst(0) + case UNIT => () case _ => emit(asm.Opcodes.ACONST_NULL) } } @@ -166,7 +167,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { // load argument on stack assert(args.length == 1, s"Too many arguments for array get operation: $tree"); genLoad(args.head, INT) - generatedType = k.getComponentType + generatedType = k.asArrayBType.componentType bc.aload(elementType) } else if (scalaPrimitives.isArraySet(code)) { @@ -320,7 +321,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) generatedType = if (tree.symbol == ArrayClass) ObjectReference - else brefType(thisName) // inner class (if any) for claszSymbol already tracked. + else ClassBType(thisName) // inner class (if any) for claszSymbol already tracked. } case Select(Ident(nme.EMPTY_PACKAGE_NAME), module) => @@ -418,7 +419,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { if (hostClass == null) internalName(field.owner) else internalName(hostClass) val fieldJName = field.javaSimpleName.toString - val fieldDescr = symInfoTK(field).getDescriptor + val fieldDescr = symInfoTK(field).descriptor val isStatic = field.isStaticMember val opc = if (isLoad) { if (isStatic) asm.Opcodes.GETSTATIC else asm.Opcodes.GETFIELD } @@ -459,7 +460,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case ClazzTag => val toPush: BType = { val kind = toTypeKind(const.typeValue) - if (kind.isValueType) classLiteral(kind) + if (kind.isPrimitive) classLiteral(kind) else kind } mnode.visitLdcInsn(toPush.toASMType) @@ -468,7 +469,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { val sym = const.symbolValue val ownerName = internalName(sym.owner) val fieldName = sym.javaSimpleName.toString - val fieldDesc = toTypeKind(sym.tpe.underlying).getDescriptor + val fieldDesc = toTypeKind(sym.tpe.underlying).descriptor mnode.visitFieldInsn( asm.Opcodes.GETSTATIC, ownerName, @@ -540,26 +541,28 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { def genTypeApply(): BType = { genLoadQualifier(fun) - if (l.isValueType && r.isValueType) + // TODO @lry make pattern match + if (l.isPrimitive && r.isPrimitive) genConversion(l, r, cast) - else if (l.isValueType) { + else if (l.isPrimitive) { bc drop l if (cast) { - mnode.visitTypeInsn(asm.Opcodes.NEW, classCastExceptionReference.getInternalName) + mnode.visitTypeInsn(asm.Opcodes.NEW, classCastExceptionReference.internalName) bc dup ObjectReference emit(asm.Opcodes.ATHROW) } else { bc boolconst false } } - else if (r.isValueType && cast) { + else if (r.isPrimitive && cast) { abort(s"Erasure should have added an unboxing operation to prevent this cast. Tree: $app") } - else if (r.isValueType) { + else if (r.isPrimitive) { bc isInstance classLiteral(r) } else { - genCast(r, cast) + assert(r.isRef, r) // ensure that it's not a method + genCast(r.asRefBType, cast) } if (cast) r else BOOL @@ -579,7 +582,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) genLoadArguments(args, paramTKs(app)) genCallMethod(fun.symbol, invokeStyle, pos = app.pos) - generatedType = asmMethodType(fun.symbol).getReturnType + generatedType = asmMethodType(fun.symbol).returnType // 'new' constructor call: Note: since constructors are // thought to return an instance of what they construct, @@ -590,13 +593,13 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { assert(ctor.isClassConstructor, s"'new' call to non-constructor: ${ctor.name}") generatedType = tpeTK(tpt) - assert(generatedType.isRefOrArrayType, s"Non reference type cannot be instantiated: $generatedType") + assert(generatedType.isRef, s"Non reference type cannot be instantiated: $generatedType") generatedType match { - case arr if generatedType.isArray => + case arr @ ArrayBType(componentType) => genLoadArguments(args, paramTKs(app)) - val dims = arr.getDimensions - var elemKind = arr.getElementType + val dims = arr.dimension + var elemKind = arr.elementType val argsSize = args.length if (argsSize > dims) { cunit.error(app.pos, s"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)") @@ -606,18 +609,18 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { * elemKind = new BType(BType.ARRAY, arr.off + argsSize, arr.len - argsSize) * however the above does not enter a TypeName for each nested arrays in chrs. */ - for (i <- args.length until dims) elemKind = arrayOf(elemKind) + for (i <- args.length until dims) elemKind = ArrayBType(elemKind) } (argsSize : @switch) match { case 1 => bc newarray elemKind case _ => - val descr = ('[' * argsSize) + elemKind.getDescriptor // denotes the same as: arrayN(elemKind, argsSize).getDescriptor + val descr = ('[' * argsSize) + elemKind.descriptor // denotes the same as: arrayN(elemKind, argsSize).descriptor mnode.visitMultiANewArrayInsn(descr, argsSize) } - case rt if generatedType.hasObjectSort => + case rt: ClassBType => assert(exemplar(ctor.owner).c == rt, s"Symbol ${ctor.owner.fullName} is different from $rt") - mnode.visitTypeInsn(asm.Opcodes.NEW, rt.getInternalName) + mnode.visitTypeInsn(asm.Opcodes.NEW, rt.internalName) bc dup generatedType genLoadArguments(args, paramTKs(app)) genCallMethod(ctor, icodes.opcodes.Static(onInstance = true)) @@ -630,7 +633,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { val nativeKind = tpeTK(expr) genLoad(expr, nativeKind) val MethodNameAndType(mname, mdesc) = asmBoxTo(nativeKind) - bc.invokestatic(BoxesRunTime.getInternalName, mname, mdesc) + bc.invokestatic(BoxesRunTime.internalName, mname, mdesc) generatedType = boxResultType(fun.symbol) // was toTypeKind(fun.symbol.tpe.resultType) case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isUnbox(fun.symbol) => @@ -638,7 +641,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { val boxType = unboxResultType(fun.symbol) // was toTypeKind(fun.symbol.owner.linkedClassOfClass.tpe) generatedType = boxType val MethodNameAndType(mname, mdesc) = asmUnboxTo(boxType) - bc.invokestatic(BoxesRunTime.getInternalName, mname, mdesc) + bc.invokestatic(BoxesRunTime.internalName, mname, mdesc) case app @ Apply(fun, args) => val sym = fun.symbol @@ -683,7 +686,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case _ => } if ((targetTypeKind != null) && (sym == definitions.Array_clone) && invokeStyle.isDynamic) { - val target: String = targetTypeKind.getInternalName + val target: String = targetTypeKind.asClassBType.internalName bc.invokevirtual(target, "clone", "()Ljava/lang/Object;") } else { @@ -694,7 +697,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { genNormalMethodCall() - generatedType = asmMethodType(sym).getReturnType + generatedType = asmMethodType(sym).returnType } } @@ -706,7 +709,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { val ArrayValue(tpt @ TypeTree(), elems) = av val elmKind = tpeTK(tpt) - val generatedType = arrayOf(elmKind) + val generatedType = ArrayBType(elmKind) lineNumber(av) bc iconst elems.length @@ -808,7 +811,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { emit(asm.Opcodes.ATHROW) // ICode enters here into enterIgnoreMode, we'll rely instead on DCE at ClassNode level. } else if (from.isNullType) { bc drop from - mnode.visitInsn(asm.Opcodes.ACONST_NULL) + emit(asm.Opcodes.ACONST_NULL) } else (from, to) match { case (BYTE, LONG) | (SHORT, LONG) | (CHAR, LONG) | (INT, LONG) => bc.emitT2T(INT, LONG) @@ -876,12 +879,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { if (claszSymbol == module.moduleClass && jMethodName != "readResolve" && !inStaticMethod) { mnode.visitVarInsn(asm.Opcodes.ALOAD, 0) } else { - val mbt = symInfoTK(module) + val mbt = symInfoTK(module).asClassBType mnode.visitFieldInsn( asm.Opcodes.GETSTATIC, - mbt.getInternalName /* + "$" */ , + mbt.internalName /* + "$" */ , strMODULE_INSTANCE_FIELD, - mbt.getDescriptor // for nostalgics: toTypeKind(module.tpe).getDescriptor + mbt.descriptor // for nostalgics: toTypeKind(module.tpe).descriptor ) } } @@ -894,7 +897,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { } } - def genCast(to: BType, cast: Boolean) { + def genCast(to: RefBType, cast: Boolean) { if (cast) { bc checkCast to } else { bc isInstance to } } @@ -959,10 +962,10 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { ) val receiver = if (useMethodOwner) methodOwner else hostSymbol val bmOwner = asmClassType(receiver) - val jowner = bmOwner.getInternalName + val jowner = bmOwner.internalName val jname = method.javaSimpleName.toString val bmType = asmMethodType(method) - val mdescr = bmType.getDescriptor + val mdescr = bmType.descriptor def initModule() { // we initialize the MODULE$ field immediately after the super ctor @@ -1025,7 +1028,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType) { if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT bc.emitIF_ICMP(op, success) - } else if (tk.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) + } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_) bc.emitIF_ACMP(op, success) } else { (tk: @unchecked) match { @@ -1046,7 +1049,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType) { if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT bc.emitIF(op, success) - } else if (tk.isRefOrArrayType) { // REFERENCE(_) | ARRAY(_) + } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_) // @unchecked because references aren't compared with GT, GE, LT, LE. (op : @unchecked) match { case icodes.EQ => bc emitIFNULL success @@ -1131,7 +1134,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case ZOR => genZandOrZor(and = false) case code => // TODO !!!!!!!!!! isReferenceType, in the sense of TypeKind? (ie non-array, non-boxed, non-nothing, may be null) - if (scalaPrimitives.isUniversalEqualityOp(code) && tpeTK(lhs).hasObjectSort) { + if (scalaPrimitives.isUniversalEqualityOp(code) && tpeTK(lhs).isClass) { // `lhs` has reference type if (code == EQ) genEqEqPrimitive(lhs, rhs, success, failure) else genEqEqPrimitive(lhs, rhs, failure, success) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala deleted file mode 100644 index aa7e73a36b..0000000000 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeGlue.scala +++ /dev/null @@ -1,718 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ - -package scala -package tools.nsc -package backend.jvm - -import scala.tools.asm -import scala.annotation.switch -import scala.collection.{ immutable, mutable } - -/* - * Immutable representations of bytecode-level types. - * - * @author Miguel Garcia, http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded - * @version 1.0 - * - */ -abstract class BCodeGlue extends SubComponent { - - import global._ - - protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global) - - object BType { - - import global.chrs - - // ------------- sorts ------------- - - val VOID : Int = 0 - val BOOLEAN: Int = 1 - val CHAR : Int = 2 - val BYTE : Int = 3 - val SHORT : Int = 4 - val INT : Int = 5 - val FLOAT : Int = 6 - val LONG : Int = 7 - val DOUBLE : Int = 8 - val ARRAY : Int = 9 - val OBJECT : Int = 10 - val METHOD : Int = 11 - - // ------------- primitive types ------------- - - val VOID_TYPE = new BType(VOID, ('V' << 24) | (5 << 16) | (0 << 8) | 0, 1) - val BOOLEAN_TYPE = new BType(BOOLEAN, ('Z' << 24) | (0 << 16) | (5 << 8) | 1, 1) - val CHAR_TYPE = new BType(CHAR, ('C' << 24) | (0 << 16) | (6 << 8) | 1, 1) - val BYTE_TYPE = new BType(BYTE, ('B' << 24) | (0 << 16) | (5 << 8) | 1, 1) - val SHORT_TYPE = new BType(SHORT, ('S' << 24) | (0 << 16) | (7 << 8) | 1, 1) - val INT_TYPE = new BType(INT, ('I' << 24) | (0 << 16) | (0 << 8) | 1, 1) - val FLOAT_TYPE = new BType(FLOAT, ('F' << 24) | (2 << 16) | (2 << 8) | 1, 1) - val LONG_TYPE = new BType(LONG, ('J' << 24) | (1 << 16) | (1 << 8) | 2, 1) - val DOUBLE_TYPE = new BType(DOUBLE, ('D' << 24) | (3 << 16) | (3 << 8) | 2, 1) - - /* - * Returns the Java type corresponding to the given type descriptor. - * - * @param off the offset of this descriptor in the chrs buffer. - * @return the Java type corresponding to the given type descriptor. - * - * can-multi-thread - */ - def getType(off: Int): BType = { - var len = 0 - chrs(off) match { - case 'V' => VOID_TYPE - case 'Z' => BOOLEAN_TYPE - case 'C' => CHAR_TYPE - case 'B' => BYTE_TYPE - case 'S' => SHORT_TYPE - case 'I' => INT_TYPE - case 'F' => FLOAT_TYPE - case 'J' => LONG_TYPE - case 'D' => DOUBLE_TYPE - case '[' => - len = 1 - while (chrs(off + len) == '[') { - len += 1 - } - if (chrs(off + len) == 'L') { - len += 1 - while (chrs(off + len) != ';') { - len += 1 - } - } - new BType(ARRAY, off, len + 1) - case 'L' => - len = 1 - while (chrs(off + len) != ';') { - len += 1 - } - new BType(OBJECT, off + 1, len - 1) - // case '(': - case _ => - assert(chrs(off) == '(') - var resPos = off + 1 - while (chrs(resPos) != ')') { resPos += 1 } - val resType = getType(resPos + 1) - val len = resPos - off + 1 + resType.len; - new BType( - METHOD, - off, - if (resType.hasObjectSort) { - len + 2 // "+ 2" accounts for the "L ... ;" in a descriptor for a non-array reference. - } else { - len - } - ) - } - } - - /* Params denote an internal name. - * can-multi-thread - */ - def getObjectType(index: Int, length: Int): BType = { - val sort = if (chrs(index) == '[') ARRAY else OBJECT; - new BType(sort, index, length) - } - - /* - * @param methodDescriptor a method descriptor. - * - * must-single-thread - */ - def getMethodType(methodDescriptor: String): BType = { - val n = global.newTypeName(methodDescriptor) - new BType(BType.METHOD, n.start, n.length) // TODO assert isValidMethodDescriptor - } - - /* - * Returns the Java method type corresponding to the given argument and return types. - * - * @param returnType the return type of the method. - * @param argumentTypes the argument types of the method. - * @return the Java type corresponding to the given argument and return types. - * - * must-single-thread - */ - def getMethodType(returnType: BType, argumentTypes: Array[BType]): BType = { - val n = global.newTypeName(getMethodDescriptor(returnType, argumentTypes)) - new BType(BType.METHOD, n.start, n.length) - } - - /* - * Returns the Java types corresponding to the argument types of method descriptor whose first argument starts at idx0. - * - * @param idx0 index into chrs of the first argument. - * @return the Java types corresponding to the argument types of the given method descriptor. - * - * can-multi-thread - */ - private def getArgumentTypes(idx0: Int): Array[BType] = { - assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.") - val args = new Array[BType](getArgumentCount(idx0)) - var off = idx0 - var size = 0 - while (chrs(off) != ')') { - args(size) = getType(off) - off += args(size).len - if (args(size).sort == OBJECT) { off += 2 } - // debug: assert("LVZBSCIJFD[)".contains(chrs(off))) - size += 1 - } - // debug: var check = 0; while (check < args.length) { assert(args(check) != null); check += 1 } - args - } - - /* - * Returns the number of argument types of this method type, whose first argument starts at idx0. - * - * @param idx0 index into chrs of the first argument. - * @return the number of argument types of this method type. - * - * can-multi-thread - */ - private def getArgumentCount(idx0: Int): Int = { - assert(chrs(idx0 - 1) == '(', "doesn't look like a method descriptor.") - var off = idx0 - var size = 0 - var keepGoing = true - while (keepGoing) { - val car = chrs(off) - off += 1 - if (car == ')') { - keepGoing = false - } else if (car == 'L') { - while (chrs(off) != ';') { off += 1 } - off += 1 - size += 1 - } else if (car != '[') { - size += 1 - } - } - - size - } - - /* - * Returns the Java type corresponding to the return type of the given - * method descriptor. - * - * @param methodDescriptor a method descriptor. - * @return the Java type corresponding to the return type of the given method descriptor. - * - * must-single-thread - */ - def getReturnType(methodDescriptor: String): BType = { - val n = global.newTypeName(methodDescriptor) - val delta = n.pos(')') // `delta` is relative to the Name's zero-based start position, not a valid index into chrs. - assert(delta < n.length, s"not a valid method descriptor: $methodDescriptor") - getType(n.start + delta + 1) - } - - /* - * Returns the descriptor corresponding to the given argument and return types. - * Note: no BType is created here for the resulting method descriptor, - * if that's desired the invoker is responsible for that. - * - * @param returnType the return type of the method. - * @param argumentTypes the argument types of the method. - * @return the descriptor corresponding to the given argument and return types. - * - * can-multi-thread - */ - def getMethodDescriptor( - returnType: BType, - argumentTypes: Array[BType]): String = - { - val buf = new StringBuffer() - buf.append('(') - var i = 0 - while (i < argumentTypes.length) { - argumentTypes(i).getDescriptor(buf) - i += 1 - } - buf.append(')') - returnType.getDescriptor(buf) - buf.toString() - } - - } // end of object BType - - /* - * Based on ASM's Type class. Namer's chrs is used in this class for the same purposes as the `buf` char array in asm.Type. - * - * All methods of this classs can-multi-thread - */ - final class BType(val sort: Int, val off: Int, val len: Int) { - - import global.chrs - - /* - * can-multi-thread - */ - def toASMType: scala.tools.asm.Type = { - import scala.tools.asm - // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match" - (sort: @switch) match { - case asm.Type.VOID => asm.Type.VOID_TYPE - case asm.Type.BOOLEAN => asm.Type.BOOLEAN_TYPE - case asm.Type.CHAR => asm.Type.CHAR_TYPE - case asm.Type.BYTE => asm.Type.BYTE_TYPE - case asm.Type.SHORT => asm.Type.SHORT_TYPE - case asm.Type.INT => asm.Type.INT_TYPE - case asm.Type.FLOAT => asm.Type.FLOAT_TYPE - case asm.Type.LONG => asm.Type.LONG_TYPE - case asm.Type.DOUBLE => asm.Type.DOUBLE_TYPE - case asm.Type.ARRAY | - asm.Type.OBJECT => asm.Type.getObjectType(getInternalName) - case asm.Type.METHOD => asm.Type.getMethodType(getDescriptor) - } - } - - /* - * Unlike for ICode's REFERENCE, isBoxedType(t) implies isReferenceType(t) - * Also, `isReferenceType(RT_NOTHING) == true` , similarly for RT_NULL. - * Use isNullType() , isNothingType() to detect Nothing and Null. - * - * can-multi-thread - */ - def hasObjectSort = (sort == BType.OBJECT) - - /* - * Returns the number of dimensions of this array type. This method should - * only be used for an array type. - * - * @return the number of dimensions of this array type. - * - * can-multi-thread - */ - def getDimensions: Int = { - var i = 1 - while (chrs(off + i) == '[') { - i += 1 - } - i - } - - /* - * Returns the (ultimate) element type of this array type. - * This method should only be used for an array type. - * - * @return Returns the type of the elements of this array type. - * - * can-multi-thread - */ - def getElementType: BType = { - assert(isArray, s"Asked for the element type of a non-array type: $this") - BType.getType(off + getDimensions) - } - - /* - * Returns the internal name of the class corresponding to this object or - * array type. The internal name of a class is its fully qualified name (as - * returned by Class.getName(), where '.' are replaced by '/'. This method - * should only be used for an object or array type. - * - * @return the internal name of the class corresponding to this object type. - * - * can-multi-thread - */ - def getInternalName: String = { - new String(chrs, off, len) - } - - /* - * @return the suffix of the internal name until the last '/' (if '/' present), internal name otherwise. - * - * can-multi-thread - */ - def getSimpleName: String = { - assert(hasObjectSort, s"not of object sort: $toString") - val iname = getInternalName - val idx = iname.lastIndexOf('/') - if (idx == -1) iname - else iname.substring(idx + 1) - } - - /* - * Returns the argument types of methods of this type. - * This method should only be used for method types. - * - * @return the argument types of methods of this type. - * - * can-multi-thread - */ - def getArgumentTypes: Array[BType] = { - BType.getArgumentTypes(off + 1) - } - - /* - * Returns the return type of methods of this type. - * This method should only be used for method types. - * - * @return the return type of methods of this type. - * - * can-multi-thread - */ - def getReturnType: BType = { - assert(chrs(off) == '(', s"doesn't look like a method descriptor: $toString") - var resPos = off + 1 - while (chrs(resPos) != ')') { resPos += 1 } - BType.getType(resPos + 1) - } - - // ------------------------------------------------------------------------ - // Inspector methods - // ------------------------------------------------------------------------ - - def isPrimitiveOrVoid = (sort < BType.ARRAY) // can-multi-thread - def isValueType = (sort < BType.ARRAY) // can-multi-thread - def isArray = (sort == BType.ARRAY) // can-multi-thread - def isUnitType = (sort == BType.VOID) // can-multi-thread - - def isRefOrArrayType = { hasObjectSort || isArray } // can-multi-thread - def isNonUnitValueType = { isValueType && !isUnitType } // can-multi-thread - - def isNonSpecial = { !isValueType && !isArray && !isPhantomType } // can-multi-thread - def isNothingType = { (this == RT_NOTHING) || (this == CT_NOTHING) } // can-multi-thread - def isNullType = { (this == RT_NULL) || (this == CT_NULL) } // can-multi-thread - def isPhantomType = { isNothingType || isNullType } // can-multi-thread - - /* - * can-multi-thread - */ - def isBoxed = { - this match { - case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR | - BOXED_BYTE | BOXED_SHORT | BOXED_INT | - BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE - => true - case _ - => false - } - } - - /* On the JVM, - * BOOL, BYTE, CHAR, SHORT, and INT - * are like Ints for the purpose of lub calculation. - * - * can-multi-thread - */ - def isIntSizedType = { - (sort : @switch) match { - case BType.BOOLEAN | BType.CHAR | - BType.BYTE | BType.SHORT | BType.INT - => true - case _ - => false - } - } - - /* On the JVM, similar to isIntSizedType except that BOOL isn't integral while LONG is. - * - * can-multi-thread - */ - def isIntegralType = { - (sort : @switch) match { - case BType.CHAR | - BType.BYTE | BType.SHORT | BType.INT | - BType.LONG - => true - case _ - => false - } - } - - /* On the JVM, FLOAT and DOUBLE. - * - * can-multi-thread - */ - def isRealType = { (sort == BType.FLOAT ) || (sort == BType.DOUBLE) } - - def isNumericType = (isIntegralType || isRealType) // can-multi-thread - - /* Is this type a category 2 type in JVM terms? (ie, is it LONG or DOUBLE?) - * - * can-multi-thread - */ - def isWideType = (getSize == 2) - - /* - * Element vs. Component type of an array: - * Quoting from the JVMS, Sec. 2.4 "Reference Types and Values" - * - * An array type consists of a component type with a single dimension (whose - * length is not given by the type). The component type of an array type may itself be - * an array type. If, starting from any array type, one considers its component type, - * and then (if that is also an array type) the component type of that type, and so on, - * eventually one must reach a component type that is not an array type; this is called - * the element type of the array type. The element type of an array type is necessarily - * either a primitive type, or a class type, or an interface type. - * - */ - - /* The type of items this array holds. - * - * can-multi-thread - */ - def getComponentType: BType = { - assert(isArray, s"Asked for the component type of a non-array type: $this") - BType.getType(off + 1) - } - - // ------------------------------------------------------------------------ - // Conversion to type descriptors - // ------------------------------------------------------------------------ - - /* - * @return the descriptor corresponding to this Java type. - * - * can-multi-thread - */ - def getDescriptor: String = { - val buf = new StringBuffer() - getDescriptor(buf) - buf.toString() - } - - /* - * Appends the descriptor corresponding to this Java type to the given string buffer. - * - * @param buf the string buffer to which the descriptor must be appended. - * - * can-multi-thread - */ - private def getDescriptor(buf: StringBuffer) { - if (isPrimitiveOrVoid) { - // descriptor is in byte 3 of 'off' for primitive types (buf == null) - buf.append(((off & 0xFF000000) >>> 24).asInstanceOf[Char]) - } else if (sort == BType.OBJECT) { - buf.append('L') - buf.append(chrs, off, len) - buf.append(';') - } else { // sort == ARRAY || sort == METHOD - buf.append(chrs, off, len) - } - } - - // ------------------------------------------------------------------------ - // Corresponding size and opcodes - // ------------------------------------------------------------------------ - - /* - * Returns the size of values of this type. - * This method must not be used for method types. - * - * @return the size of values of this type, i.e., 2 for <tt>long</tt> and - * <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise. - * - * can-multi-thread - */ - def getSize: Int = { - // the size is in byte 0 of 'off' for primitive types (buf == null) - if (isPrimitiveOrVoid) (off & 0xFF) else 1 - } - - /* - * Returns a JVM instruction opcode adapted to this Java type. This method - * must not be used for method types. - * - * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, - * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, - * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. - * @return an opcode that is similar to the given opcode, but adapted to - * this Java type. For example, if this type is <tt>float</tt> and - * <tt>opcode</tt> is IRETURN, this method returns FRETURN. - * - * can-multi-thread - */ - def getOpcode(opcode: Int): Int = { - import scala.tools.asm.Opcodes - if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { - // the offset for IALOAD or IASTORE is in byte 1 of 'off' for - // primitive types (buf == null) - opcode + (if (isPrimitiveOrVoid) (off & 0xFF00) >> 8 else 4) - } else { - // the offset for other instructions is in byte 2 of 'off' for - // primitive types (buf == null) - opcode + (if (isPrimitiveOrVoid) (off & 0xFF0000) >> 16 else 4) - } - } - - // ------------------------------------------------------------------------ - // Equals, hashCode and toString - // ------------------------------------------------------------------------ - - /* - * Tests if the given object is equal to this type. - * - * @param o the object to be compared to this type. - * @return <tt>true</tt> if the given object is equal to this type. - * - * can-multi-thread - */ - override def equals(o: Any): Boolean = { - if (!(o.isInstanceOf[BType])) { - return false - } - val t = o.asInstanceOf[BType] - if (this eq t) { - return true - } - if (sort != t.sort) { - return false - } - if (sort >= BType.ARRAY) { - if (len != t.len) { - return false - } - // sort checked already - if (off == t.off) { - return true - } - var i = 0 - while (i < len) { - if (chrs(off + i) != chrs(t.off + i)) { - return false - } - i += 1 - } - // If we reach here, we could update the largest of (this.off, t.off) to match the other, so as to simplify future == comparisons. - // But that would require a var rather than val. - } - true - } - - /* - * @return a hash code value for this type. - * - * can-multi-thread - */ - override def hashCode(): Int = { - var hc = 13 * sort; - if (sort >= BType.ARRAY) { - var i = off - val end = i + len - while (i < end) { - hc = 17 * (hc + chrs(i)) - i += 1 - } - } - hc - } - - /* - * @return the descriptor of this type. - * - * can-multi-thread - */ - override def toString: String = { getDescriptor } - - } - - /* - * Creates a TypeName and the BType token for it. - * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that. - * - * must-single-thread - */ - def brefType(iname: String): BType = { brefType(newTypeName(iname.toCharArray(), 0, iname.length())) } - - /* - * Creates a BType token for the TypeName received as argument. - * This method does not add to `innerClassBufferASM`, use `internalName()` or `asmType()` or `toTypeKind()` for that. - * - * can-multi-thread - */ - def brefType(iname: TypeName): BType = { BType.getObjectType(iname.start, iname.length) } - - // due to keyboard economy only - val UNIT = BType.VOID_TYPE - val BOOL = BType.BOOLEAN_TYPE - val CHAR = BType.CHAR_TYPE - val BYTE = BType.BYTE_TYPE - val SHORT = BType.SHORT_TYPE - val INT = BType.INT_TYPE - val LONG = BType.LONG_TYPE - val FLOAT = BType.FLOAT_TYPE - val DOUBLE = BType.DOUBLE_TYPE - - val BOXED_UNIT = brefType("java/lang/Void") - val BOXED_BOOLEAN = brefType("java/lang/Boolean") - val BOXED_BYTE = brefType("java/lang/Byte") - val BOXED_SHORT = brefType("java/lang/Short") - val BOXED_CHAR = brefType("java/lang/Character") - val BOXED_INT = brefType("java/lang/Integer") - val BOXED_LONG = brefType("java/lang/Long") - val BOXED_FLOAT = brefType("java/lang/Float") - val BOXED_DOUBLE = brefType("java/lang/Double") - - /* - * RT_NOTHING and RT_NULL exist at run-time only. - * They are the bytecode-level manifestation (in method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs. - * Therefore, when RT_NOTHING or RT_NULL are to be emitted, - * a mapping is needed: the internal names of NothingClass and NullClass can't be emitted as-is. - */ - val RT_NOTHING = brefType("scala/runtime/Nothing$") - val RT_NULL = brefType("scala/runtime/Null$") - val CT_NOTHING = brefType("scala/Nothing") // TODO needed? - val CT_NULL = brefType("scala/Null") // TODO needed? - - val srBooleanRef = brefType("scala/runtime/BooleanRef") - val srByteRef = brefType("scala/runtime/ByteRef") - val srCharRef = brefType("scala/runtime/CharRef") - val srIntRef = brefType("scala/runtime/IntRef") - val srLongRef = brefType("scala/runtime/LongRef") - val srFloatRef = brefType("scala/runtime/FloatRef") - val srDoubleRef = brefType("scala/runtime/DoubleRef") - - /* Map from type kinds to the Java reference types. - * Useful when pushing class literals onto the operand stack (ldc instruction taking a class literal). - * @see Predef.classOf - * @see genConstant() - */ - val classLiteral = immutable.Map[BType, BType]( - UNIT -> BOXED_UNIT, - BOOL -> BOXED_BOOLEAN, - BYTE -> BOXED_BYTE, - SHORT -> BOXED_SHORT, - CHAR -> BOXED_CHAR, - INT -> BOXED_INT, - LONG -> BOXED_LONG, - FLOAT -> BOXED_FLOAT, - DOUBLE -> BOXED_DOUBLE - ) - - case class MethodNameAndType(mname: String, mdesc: String) - - val asmBoxTo: Map[BType, MethodNameAndType] = { - Map( - BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) , - BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) , - CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") , - SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) , - INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) , - LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) , - FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) , - DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" ) - ) - } - - val asmUnboxTo: Map[BType, MethodNameAndType] = { - Map( - BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") , - BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") , - CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") , - SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") , - INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") , - LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") , - FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") , - DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D") - ) - } -} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala index f800dbf9cd..51bfdf0027 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala @@ -20,8 +20,8 @@ import scala.tools.nsc.io.AbstractFile * */ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { - import global._ + import bTypes._ /* * must-single-thread @@ -56,7 +56,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { /* * can-multi-thread */ - def firstCommonSuffix(as: List[Tracked], bs: List[Tracked]): BType = { + def firstCommonSuffix(as: List[Tracked], bs: List[Tracked]): ClassBType = { var chainA = as var chainB = bs var fcs: Tracked = null @@ -77,17 +77,18 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { */ final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) { - /* - * This method is thread re-entrant because chrs never grows during its operation (that's because all TypeNames being looked up have already been entered). - * To stress this point, rather than using `newTypeName()` we use `lookupTypeName()` + /** + * This method is thread re-entrant because chrs never grows during its operation (that's + * because all TypeNames being looked up have already been entered). + * To stress this point, rather than using `newTypeName()` we use `lookupTypeName()` * - * can-multi-thread + * can-multi-thread */ override def getCommonSuperClass(inameA: String, inameB: String): String = { - val a = brefType(lookupTypeName(inameA.toCharArray)) - val b = brefType(lookupTypeName(inameB.toCharArray)) + val a = ClassBType(lookupTypeName(inameA.toCharArray)) + val b = ClassBType(lookupTypeName(inameB.toCharArray)) val lca = jvmWiseLUB(a, b) - val lcaName = lca.getInternalName // don't call javaName because that side-effects innerClassBuffer. + val lcaName = lca.internalName // don't call javaName because that side-effects innerClassBuffer. assert(lcaName != "scala/Any") lcaName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things. @@ -95,16 +96,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { } - /* - * Finding the least upper bound in agreement with the bytecode verifier (given two internal names handed out by ASM) - * Background: - * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf - * http://comments.gmane.org/gmane.comp.java.vm.languages/2293 - * https://issues.scala-lang.org/browse/SI-3872 + /** + * Finding the least upper bound in agreement with the bytecode verifier (given two internal names + * handed out by ASM) + * Background: + * http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf + * http://comments.gmane.org/gmane.comp.java.vm.languages/2293 + * https://issues.scala-lang.org/browse/SI-3872 * - * can-multi-thread + * can-multi-thread */ - def jvmWiseLUB(a: BType, b: BType): BType = { + def jvmWiseLUB(a: ClassBType, b: ClassBType): ClassBType = { assert(a.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $a") assert(b.isNonSpecial, s"jvmWiseLUB() received a non-plain-class $b") @@ -401,14 +403,14 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { * * must-single-thread */ - final def internalName(sym: Symbol): String = { asmClassType(sym).getInternalName } + final def internalName(sym: Symbol): String = asmClassType(sym).internalName /* * Tracks (if needed) the inner class given by `sym`. * * must-single-thread */ - final def asmClassType(sym: Symbol): BType = { + final def asmClassType(sym: Symbol): ClassBType = { assert( hasInternalName(sym), { @@ -514,17 +516,18 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { case _: ConstantType => toTypeKind(t.underlying) case TypeRef(_, sym, args) => - if (sym == ArrayClass) arrayOf(toTypeKind(args.head)) + if (sym == ArrayClass) ArrayBType(toTypeKind(args.head)) else primitiveOrRefType2(sym) case ClassInfoType(_, _, sym) => assert(sym != ArrayClass, "ClassInfoType to ArrayClass!") primitiveOrRefType(sym) + // TODO @lry check below comments / todo's // !!! Iulian says types which make no sense after erasure should not reach here, which includes the ExistentialType, AnnotatedType, RefinedType. case ExistentialType(_, t) => toTypeKind(t) // TODO shouldn't get here but the following does: akka-actor/src/main/scala/akka/util/WildcardTree.scala case AnnotatedType(_, w) => toTypeKind(w) // TODO test/files/jvm/annotations.scala causes an AnnotatedType to reach here. - case RefinedType(parents, _) => parents map toTypeKind reduceLeft jvmWiseLUB + case RefinedType(parents, _) => parents.map(toTypeKind(_).asClassBType) reduceLeft jvmWiseLUB // For sure WildcardTypes shouldn't reach here either, but when debugging such situations this may come in handy. // case WildcardType => REFERENCE(ObjectClass) @@ -538,12 +541,12 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { /* * must-single-thread */ - def asmMethodType(msym: Symbol): BType = { + def asmMethodType(msym: Symbol): MethodBType = { assert(msym.isMethod, s"not a method-symbol: $msym") val resT: BType = - if (msym.isClassConstructor || msym.isConstructor) BType.VOID_TYPE - else toTypeKind(msym.tpe.resultType); - BType.getMethodType( resT, mkArray(msym.tpe.paramTypes map toTypeKind) ) + if (msym.isClassConstructor || msym.isConstructor) UNIT + else toTypeKind(msym.tpe.resultType) + MethodBType(msym.tpe.paramTypes map toTypeKind, resT) } /* @@ -577,14 +580,14 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { * * must-single-thread */ - final def descriptor(t: Type): String = { toTypeKind(t).getDescriptor } + final def descriptor(t: Type): String = { toTypeKind(t).descriptor } /* * Tracks (if needed) the inner class given by `sym`. * * must-single-thread */ - final def descriptor(sym: Symbol): String = { asmClassType(sym).getDescriptor } + final def descriptor(sym: Symbol): String = { asmClassType(sym).descriptor } } // end of trait BCInnerClassGen @@ -800,7 +803,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { val thrownExceptions: List[String] = getExceptions(throws) val jReturnType = toTypeKind(methodInfo.resultType) - val mdesc = BType.getMethodType(jReturnType, mkArray(paramJavaTypes)).getDescriptor + val mdesc = MethodBType(paramJavaTypes, jReturnType).descriptor val mirrorMethodName = m.javaSimpleName.toString val mirrorMethod: asm.MethodVisitor = jclass.visitMethod( flags, @@ -819,13 +822,13 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { var index = 0 for(jparamType <- paramJavaTypes) { - mirrorMethod.visitVarInsn(jparamType.getOpcode(asm.Opcodes.ILOAD), index) - assert(jparamType.sort != BType.METHOD, jparamType) - index += jparamType.getSize + mirrorMethod.visitVarInsn(jparamType.typedOpcode(asm.Opcodes.ILOAD), index) + assert(!jparamType.isInstanceOf[MethodBType], jparamType) + index += jparamType.size } - mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, asmMethodType(m).getDescriptor) - mirrorMethod.visitInsn(jReturnType.getOpcode(asm.Opcodes.IRETURN)) + mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, asmMethodType(m).descriptor, false) + mirrorMethod.visitInsn(jReturnType.typedOpcode(asm.Opcodes.IRETURN)) mirrorMethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments mirrorMethod.visitEnd() @@ -993,7 +996,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { flags, mirrorName, null /* no java-generic-signature */, - JAVA_LANG_OBJECT.getInternalName, + JAVA_LANG_OBJECT.internalName, EMPTY_STRING_ARRAY ) @@ -1085,12 +1088,11 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { EMPTY_STRING_ARRAY // no throwable exceptions ) - val stringArrayJType: BType = arrayOf(JAVA_LANG_STRING) - val conJType: BType = - BType.getMethodType( - BType.VOID_TYPE, - Array(exemplar(definitions.ClassClass).c, stringArrayJType, stringArrayJType) - ) + val stringArrayJType: BType = ArrayBType(JAVA_LANG_STRING) + val conJType: BType = MethodBType( + exemplar(definitions.ClassClass).c :: stringArrayJType :: stringArrayJType :: Nil, + UNIT + ) def push(lst: List[String]) { var fi = 0 @@ -1099,7 +1101,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { constructor.visitLdcInsn(new java.lang.Integer(fi)) if (f == null) { constructor.visitInsn(asm.Opcodes.ACONST_NULL) } else { constructor.visitLdcInsn(f) } - constructor.visitInsn(JAVA_LANG_STRING.getOpcode(asm.Opcodes.IASTORE)) + constructor.visitInsn(JAVA_LANG_STRING.typedOpcode(asm.Opcodes.IASTORE)) fi += 1 } } @@ -1108,21 +1110,21 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { constructor.visitVarInsn(asm.Opcodes.ALOAD, 0) // push the class - constructor.visitLdcInsn(exemplar(cls).c) + constructor.visitLdcInsn(exemplar(cls).c.toASMType) // push the string array of field information constructor.visitLdcInsn(new java.lang.Integer(fieldList.length)) - constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.getInternalName) + constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName) push(fieldList) // push the string array of method information constructor.visitLdcInsn(new java.lang.Integer(methodList.length)) - constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.getInternalName) + constructor.visitTypeInsn(asm.Opcodes.ANEWARRAY, JAVA_LANG_STRING.internalName) push(methodList) // invoke the superclass constructor, which will do the // necessary java reflection and create Method objects. - constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor) + constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.descriptor, false) constructor.visitInsn(asm.Opcodes.RETURN) constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments @@ -1161,7 +1163,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { def legacyAddCreatorCode(clinit: asm.MethodVisitor, cnode: asm.tree.ClassNode, thisName: String) { // this tracks the inner class in innerClassBufferASM, if needed. val androidCreatorType = asmClassType(AndroidCreatorClass) - val tdesc_creator = androidCreatorType.getDescriptor + val tdesc_creator = androidCreatorType.descriptor cnode.visitField( PublicStaticFinal, @@ -1182,12 +1184,13 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { ) // INVOKEVIRTUAL `moduleName`.CREATOR() : android.os.Parcelable$Creator; - val bt = BType.getMethodType(androidCreatorType, Array.empty[BType]) + val bt = MethodBType(Nil, androidCreatorType) clinit.visitMethodInsn( asm.Opcodes.INVOKEVIRTUAL, moduleName, "CREATOR", - bt.getDescriptor + bt.descriptor, + false ) // PUTSTATIC `thisName`.CREATOR; diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala index c3492b79a9..9b7c975960 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala @@ -9,8 +9,7 @@ package backend.jvm import scala.tools.asm import scala.annotation.switch -import scala.collection.{ immutable, mutable } -import collection.convert.Wrappers.JListWrapper +import scala.collection.mutable /* * A high-level facade to the ASM API for bytecode generation. @@ -19,9 +18,17 @@ import collection.convert.Wrappers.JListWrapper * @version 1.0 * */ -abstract class BCodeIdiomatic extends BCodeGlue { +abstract class BCodeIdiomatic extends SubComponent { + protected val bCodeICodeCommon: BCodeICodeCommon[global.type] = new BCodeICodeCommon(global) + + val bTypes = new BTypes[global.type](global) { + def chrs = global.chrs + override type BTypeName = global.TypeName + override def createNewName(s: String) = global.newTypeName(s) + } import global._ + import bTypes._ val classfileVersion: Int = settings.target.value match { case "jvm-1.5" => asm.Opcodes.V1_5 @@ -44,12 +51,12 @@ abstract class BCodeIdiomatic extends BCodeGlue { val CLASS_CONSTRUCTOR_NAME = "<clinit>" val INSTANCE_CONSTRUCTOR_NAME = "<init>" - val ObjectReference = brefType("java/lang/Object") + val ObjectReference = ClassBType("java/lang/Object") val AnyRefReference = ObjectReference - val objArrayReference = arrayOf(ObjectReference) + val objArrayReference = ArrayBType(ObjectReference) val JAVA_LANG_OBJECT = ObjectReference - val JAVA_LANG_STRING = brefType("java/lang/String") + val JAVA_LANG_STRING = ClassBType("java/lang/String") var StringBuilderReference: BType = null @@ -108,17 +115,6 @@ abstract class BCodeIdiomatic extends BCodeGlue { a } - /* - * The type of 1-dimensional arrays of `elem` type. - * The invoker is responsible for tracking (if needed) the inner class given by the elem BType. - * - * must-single-thread - */ - final def arrayOf(elem: BType): BType = { - assert(!(elem.isUnitType), s"The element type of an array can't be: $elem") - brefType("[" + elem.getDescriptor) - } - /* Just a namespace for utilities that encapsulate MethodVisitor idioms. * In the ASM world, org.objectweb.asm.commons.InstructionAdapter plays a similar role, * but the methods here allow choosing when to transition from ICode to ASM types @@ -242,12 +238,12 @@ abstract class BCodeIdiomatic extends BCodeGlue { final def genStringConcat(el: BType) { val jtype = - if (el.isArray || el.hasObjectSort) JAVA_LANG_OBJECT - else el; + if (el.isArray || el.isClass) JAVA_LANG_OBJECT + else el - val bt = BType.getMethodType(StringBuilderReference, Array(jtype)) + val bt = MethodBType(List(jtype), StringBuilderReference) - invokevirtual(StringBuilderClassName, "append", bt.getDescriptor) + invokevirtual(StringBuilderClassName, "append", bt.descriptor) } /* @@ -268,7 +264,7 @@ abstract class BCodeIdiomatic extends BCodeGlue { final def emitT2T(from: BType, to: BType) { assert( - from.isNonUnitValueType && to.isNonUnitValueType, + from.isNonVoidPrimitiveType && to.isNonVoidPrimitiveType, s"Cannot emit primitive conversion from $from to $to" ) @@ -290,37 +286,37 @@ abstract class BCodeIdiomatic extends BCodeGlue { assert(from != BOOL && to != BOOL, s"inconvertible types : $from -> $to") // We're done with BOOL already - (from.sort: @switch) match { + from match { // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match" - case asm.Type.BYTE => pickOne(JCodeMethodN.fromByteT2T) - case asm.Type.SHORT => pickOne(JCodeMethodN.fromShortT2T) - case asm.Type.CHAR => pickOne(JCodeMethodN.fromCharT2T) - case asm.Type.INT => pickOne(JCodeMethodN.fromIntT2T) + case BYTE => pickOne(JCodeMethodN.fromByteT2T) + case SHORT => pickOne(JCodeMethodN.fromShortT2T) + case CHAR => pickOne(JCodeMethodN.fromCharT2T) + case INT => pickOne(JCodeMethodN.fromIntT2T) - case asm.Type.FLOAT => + case FLOAT => import asm.Opcodes.{ F2L, F2D, F2I } - (to.sort: @switch) match { - case asm.Type.LONG => emit(F2L) - case asm.Type.DOUBLE => emit(F2D) - case _ => emit(F2I); emitT2T(INT, to) + to match { + case LONG => emit(F2L) + case DOUBLE => emit(F2D) + case _ => emit(F2I); emitT2T(INT, to) } - case asm.Type.LONG => + case LONG => import asm.Opcodes.{ L2F, L2D, L2I } - (to.sort: @switch) match { - case asm.Type.FLOAT => emit(L2F) - case asm.Type.DOUBLE => emit(L2D) - case _ => emit(L2I); emitT2T(INT, to) + to match { + case FLOAT => emit(L2F) + case DOUBLE => emit(L2D) + case _ => emit(L2I); emitT2T(INT, to) } - case asm.Type.DOUBLE => + case DOUBLE => import asm.Opcodes.{ D2L, D2F, D2I } - (to.sort: @switch) match { - case asm.Type.FLOAT => emit(D2F) - case asm.Type.LONG => emit(D2L) - case _ => emit(D2I); emitT2T(INT, to) + to match { + case FLOAT => emit(D2F) + case LONG => emit(D2L) + case _ => emit(D2I); emitT2T(INT, to) } } } // end of emitT2T() @@ -372,24 +368,26 @@ abstract class BCodeIdiomatic extends BCodeGlue { // can-multi-thread final def newarray(elem: BType) { - if (elem.isRefOrArrayType || elem.isPhantomType ) { - /* phantom type at play in `Array(null)`, SI-1513. On the other hand, Array(()) has element type `scala.runtime.BoxedUnit` which hasObjectSort. */ - jmethod.visitTypeInsn(Opcodes.ANEWARRAY, elem.getInternalName) - } else { - val rand = { - // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match" - (elem.sort: @switch) match { - case asm.Type.BOOLEAN => Opcodes.T_BOOLEAN - case asm.Type.BYTE => Opcodes.T_BYTE - case asm.Type.SHORT => Opcodes.T_SHORT - case asm.Type.CHAR => Opcodes.T_CHAR - case asm.Type.INT => Opcodes.T_INT - case asm.Type.LONG => Opcodes.T_LONG - case asm.Type.FLOAT => Opcodes.T_FLOAT - case asm.Type.DOUBLE => Opcodes.T_DOUBLE + elem match { + case c: RefBType => + /* phantom type at play in `Array(null)`, SI-1513. On the other hand, Array(()) has element type `scala.runtime.BoxedUnit` which isObject. */ + jmethod.visitTypeInsn(Opcodes.ANEWARRAY, c.classOrArrayType) + case _ => + assert(elem.isNonVoidPrimitiveType) + val rand = { + // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match" + elem match { + case BOOL => Opcodes.T_BOOLEAN + case BYTE => Opcodes.T_BYTE + case SHORT => Opcodes.T_SHORT + case CHAR => Opcodes.T_CHAR + case INT => Opcodes.T_INT + case LONG => Opcodes.T_LONG + case FLOAT => Opcodes.T_FLOAT + case DOUBLE => Opcodes.T_DOUBLE + } } - } - jmethod.visitIntInsn(Opcodes.NEWARRAY, rand) + jmethod.visitIntInsn(Opcodes.NEWARRAY, rand) } } @@ -409,19 +407,19 @@ abstract class BCodeIdiomatic extends BCodeGlue { // can-multi-thread final def invokespecial(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false) } // can-multi-thread final def invokestatic(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false) } // can-multi-thread final def invokeinterface(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true) } // can-multi-thread final def invokevirtual(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false) } // can-multi-thread @@ -529,7 +527,7 @@ abstract class BCodeIdiomatic extends BCodeGlue { // can-multi-thread final def emitVarInsn(opc: Int, idx: Int, tk: BType) { assert((opc == Opcodes.ILOAD) || (opc == Opcodes.ISTORE), opc) - jmethod.visitVarInsn(tk.getOpcode(opc), idx) + jmethod.visitVarInsn(tk.typedOpcode(opc), idx) } // ---------------- array load and store ---------------- @@ -538,7 +536,7 @@ abstract class BCodeIdiomatic extends BCodeGlue { final def emitTypeBased(opcs: Array[Int], tk: BType) { assert(tk != UNIT, tk) val opc = { - if (tk.isRefOrArrayType) { opcs(0) } + if (tk.isRef) { opcs(0) } else if (tk.isIntSizedType) { (tk: @unchecked) match { case BOOL | BYTE => opcs(1) @@ -563,11 +561,11 @@ abstract class BCodeIdiomatic extends BCodeGlue { final def emitPrimitive(opcs: Array[Int], tk: BType) { val opc = { // using `asm.Type.SHORT` instead of `BType.SHORT` because otherwise "warning: could not emit switch for @switch annotated match" - (tk.sort: @switch) match { - case asm.Type.LONG => opcs(1) - case asm.Type.FLOAT => opcs(2) - case asm.Type.DOUBLE => opcs(3) - case _ => opcs(0) + tk match { + case LONG => opcs(1) + case FLOAT => opcs(2) + case DOUBLE => opcs(3) + case _ => opcs(0) } } emit(opc) @@ -582,15 +580,14 @@ abstract class BCodeIdiomatic extends BCodeGlue { // ---------------- type checks and casts ---------------- // can-multi-thread - final def isInstance(tk: BType) { - jmethod.visitTypeInsn(Opcodes.INSTANCEOF, tk.getInternalName) + final def isInstance(tk: RefBType): Unit = { + jmethod.visitTypeInsn(Opcodes.INSTANCEOF, tk.classOrArrayType) } // can-multi-thread - final def checkCast(tk: BType) { - assert(tk.isRefOrArrayType, s"checkcast on primitive type: $tk") + final def checkCast(tk: RefBType): Unit = { // TODO ICode also requires: but that's too much, right? assert(!isBoxedType(tk), "checkcast on boxed type: " + tk) - jmethod.visitTypeInsn(Opcodes.CHECKCAST, tk.getInternalName) + jmethod.visitTypeInsn(Opcodes.CHECKCAST, tk.classOrArrayType) } } // end of class JCodeMethodN diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index 360ce58ecc..effc68c5e3 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -14,6 +14,8 @@ import scala.tools.nsc.symtab._ import scala.annotation.switch import scala.tools.asm +import scala.tools.asm.util.{TraceMethodVisitor, ASMifier} +import java.io.PrintWriter /* * @@ -23,6 +25,7 @@ import scala.tools.asm */ abstract class BCodeSkelBuilder extends BCodeHelpers { import global._ + import bTypes._ /* * There's a dedicated PlainClassBuilder for each CompilationUnit, @@ -114,9 +117,13 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { addClassFields() innerClassBufferASM ++= trackMemberClasses(claszSymbol, Nil) - gen(cd.impl) + addInnerClassesASM(cnode, innerClassBufferASM.toList) + + if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern)) + AsmUtils.traceClass(cnode) + cnode.innerClasses assert(cd.symbol == claszSymbol, "Someone messed up BCodePhase.claszSymbol during genPlainClass().") } // end of method genPlainClass() @@ -127,7 +134,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { private def initJClass(jclass: asm.ClassVisitor) { val ps = claszSymbol.info.parents - val superClass: String = if (ps.isEmpty) JAVA_LANG_OBJECT.getInternalName else internalName(ps.head.typeSymbol); + val superClass: String = if (ps.isEmpty) JAVA_LANG_OBJECT.internalName else internalName(ps.head.typeSymbol); val ifaces: Array[String] = { val arrIfacesTr: Array[Tracked] = exemplar(claszSymbol).ifaces val arrIfaces = new Array[String](arrIfacesTr.length) @@ -136,7 +143,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val ifaceTr = arrIfacesTr(i) val bt = ifaceTr.c if (ifaceTr.isInnerClass) { innerClassBufferASM += bt } - arrIfaces(i) = bt.getInternalName + arrIfaces(i) = bt.internalName i += 1 } arrIfaces @@ -160,7 +167,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val enclM = getEnclosingMethodAttribute(claszSymbol) if (enclM != null) { val EnclMethodEntry(className, methodName, methodType) = enclM - cnode.visitOuterClass(className, methodName, methodType.getDescriptor) + cnode.visitOuterClass(className, methodName, methodType.descriptor) } val ssa = getAnnotPickle(thisName, claszSymbol) @@ -228,7 +235,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { if (isCZStaticModule) { clinit.visitTypeInsn(asm.Opcodes.NEW, thisName) clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, - thisName, INSTANCE_CONSTRUCTOR_NAME, "()V") + thisName, INSTANCE_CONSTRUCTOR_NAME, "()V", false) } if (isCZParcelable) { legacyAddCreatorCode(clinit, cnode, thisName) } clinit.visitInsn(asm.Opcodes.RETURN) @@ -255,7 +262,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val jfield = new asm.tree.FieldNode( flags, f.javaSimpleName.toString, - symInfoTK(f).getDescriptor, + symInfoTK(f).descriptor, javagensig, null // no initial value ) @@ -391,8 +398,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { assert(nxtIdx != -1, "not a valid start index") val loc = Local(tk, sym.javaSimpleName.toString, nxtIdx, sym.isSynthetic) slots += (sym -> loc) - assert(tk.getSize > 0, "makeLocal called for a symbol whose type is Unit.") - nxtIdx += tk.getSize + assert(tk.size > 0, "makeLocal called for a symbol whose type is Unit.") + nxtIdx += tk.size loc } @@ -525,7 +532,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { if (isMethSymStaticCtor) CLASS_CONSTRUCTOR_NAME else jMethodName - val mdesc = asmMethodType(methSymbol).getDescriptor + val mdesc = asmMethodType(methSymbol).descriptor mnode = cnode.visitMethod( flags, bytecodeName, @@ -549,7 +556,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { methSymbol = dd.symbol jMethodName = methSymbol.javaSimpleName.toString - returnType = asmMethodType(dd.symbol).getReturnType + returnType = asmMethodType(dd.symbol).returnType isMethSymStaticCtor = methSymbol.isStaticConstructor resetMethodBookkeeping(dd) @@ -639,6 +646,10 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { // Note we don't invoke visitMax, thus there are no FrameNode among mnode.instructions. // The only non-instruction nodes to be found are LabelNode and LineNumberNode. } + + if (AsmUtils.traceMethodEnabled && mnode.name.contains(AsmUtils.traceMethodPattern)) + AsmUtils.traceMethod(mnode) + mnode = null } // end of method genDefDef() @@ -675,8 +686,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val callee = methSymbol.enclClass.primaryConstructor val jname = callee.javaSimpleName.toString val jowner = internalName(callee.owner) - val jtype = asmMethodType(callee).getDescriptor - insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype) + val jtype = asmMethodType(callee).descriptor + insnModB = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESPECIAL, jowner, jname, jtype, false) } var insnParcA: asm.tree.AbstractInsnNode = null @@ -684,7 +695,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { // android creator code if (isCZParcelable) { // add a static field ("CREATOR") to this class to cache android.os.Parcelable$Creator - val andrFieldDescr = asmClassType(AndroidCreatorClass).getDescriptor + val andrFieldDescr = asmClassType(AndroidCreatorClass).descriptor cnode.visitField( asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL, "CREATOR", @@ -696,8 +707,8 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { val callee = definitions.getMember(claszSymbol.companionModule, androidFieldName) val jowner = internalName(callee.owner) val jname = callee.javaSimpleName.toString - val jtype = asmMethodType(callee).getDescriptor - insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype) + val jtype = asmMethodType(callee).descriptor + insnParcA = new asm.tree.MethodInsnNode(asm.Opcodes.INVOKESTATIC, jowner, jname, jtype, false) // PUTSTATIC `thisName`.CREATOR; insnParcB = new asm.tree.FieldInsnNode(asm.Opcodes.PUTSTATIC, thisName, "CREATOR", andrFieldDescr) } @@ -713,7 +724,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { def emitLocalVarScope(sym: Symbol, start: asm.Label, end: asm.Label, force: Boolean = false) { val Local(tk, name, idx, isSynth) = locals(sym) if (force || !isSynth) { - mnode.visitLocalVariable(name, tk.getDescriptor, null, start, end, idx) + mnode.visitLocalVariable(name, tk.descriptor, null, start, end, idx) } } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala index 9ddb7a3ce8..c271e7b129 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala @@ -9,9 +9,7 @@ package tools.nsc package backend package jvm -import scala.collection.{ mutable, immutable } -import scala.annotation.switch - +import scala.collection.immutable import scala.tools.asm /* @@ -22,6 +20,7 @@ import scala.tools.asm */ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { import global._ + import bTypes._ /* @@ -184,7 +183,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { val caseHandlers: List[EHClause] = for (CaseDef(pat, _, caseBody) <- catches) yield { pat match { - case Typed(Ident(nme.WILDCARD), tpt) => NamelessEH(tpeTK(tpt), caseBody) + case Typed(Ident(nme.WILDCARD), tpt) => NamelessEH(tpeTK(tpt).asClassBType, caseBody) case Ident(nme.WILDCARD) => NamelessEH(ThrowableReference, caseBody) case Bind(_, _) => BoundEH (pat.symbol, caseBody) } @@ -250,7 +249,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { // (2.a) emit case clause proper val startHandler = currProgramPoint() var endHandler: asm.Label = null - var excType: BType = null + var excType: ClassBType = null registerCleanup(finCleanup) ch match { case NamelessEH(typeToDrop, caseBody) => @@ -269,7 +268,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { nopIfNeeded(startHandler) endHandler = currProgramPoint() emitLocalVarScope(patSymbol, startHandler, endHandler) - excType = patTK + excType = patTK.asClassBType } unregisterCleanup(finCleanup) // (2.b) mark the try-body as protected by this case clause. @@ -357,10 +356,10 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { } } - def protect(start: asm.Label, end: asm.Label, handler: asm.Label, excType: BType) { + def protect(start: asm.Label, end: asm.Label, handler: asm.Label, excType: ClassBType) { val excInternalName: String = if (excType == null) null - else excType.getInternalName + else excType.internalName assert(start != end, "protecting a range of zero instructions leads to illegal class format. Solution: add a NOP to that range.") mnode.visitTryCatchBlock(start, end, handler, excInternalName) } @@ -387,7 +386,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { def mayCleanStack(tree: Tree): Boolean = tree exists { t => t.isInstanceOf[Try] } trait EHClause - case class NamelessEH(typeToDrop: BType, caseBody: Tree) extends EHClause + case class NamelessEH(typeToDrop: ClassBType, caseBody: Tree) extends EHClause case class BoundEH (patSymbol: Symbol, caseBody: Tree) extends EHClause } diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala index 1eca69936a..62dfb4917d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeTypes.scala @@ -18,27 +18,25 @@ import scala.collection.{ immutable, mutable } * */ abstract class BCodeTypes extends BCodeIdiomatic { - import global._ + import bTypes._ // when compiling the Scala library, some assertions don't hold (e.g., scala.Boolean has null superClass although it's not an interface) val isCompilingStdLib = !(settings.sourcepath.isDefault) - val srBoxedUnit = brefType("scala/runtime/BoxedUnit") - // special names - var StringReference : BType = null - var ThrowableReference : BType = null - var jlCloneableReference : BType = null // java/lang/Cloneable - var jlNPEReference : BType = null // java/lang/NullPointerException - var jioSerializableReference : BType = null // java/io/Serializable - var scalaSerializableReference : BType = null // scala/Serializable - var classCastExceptionReference : BType = null // java/lang/ClassCastException + var StringReference : ClassBType = null + var ThrowableReference : ClassBType = null + var jlCloneableReference : ClassBType = null // java/lang/Cloneable + var jlNPEReference : ClassBType = null // java/lang/NullPointerException + var jioSerializableReference : ClassBType = null // java/io/Serializable + var scalaSerializableReference : ClassBType = null // scala/Serializable + var classCastExceptionReference : ClassBType = null // java/lang/ClassCastException /* A map from scala primitive type-symbols to BTypes */ var primitiveTypeMap: Map[Symbol, BType] = null /* A map from scala type-symbols for Nothing and Null to (runtime version) BTypes */ - var phantomTypeMap: Map[Symbol, BType] = null + var phantomTypeMap: Map[Symbol, ClassBType] = null /* Maps the method symbol for a box method to the boxed type of the result. * For example, the method symbol for `Byte.box()`) is mapped to the BType `Ljava/lang/Integer;`. */ var boxResultType: Map[Symbol, BType] = null @@ -63,10 +61,10 @@ abstract class BCodeTypes extends BCodeIdiomatic { val AbstractFunctionReference = new Array[Tracked](definitions.MaxFunctionArity + 1) val abstractFunctionArityMap = mutable.Map.empty[BType, Int] - var PartialFunctionReference: BType = null // scala.PartialFunction - var AbstractPartialFunctionReference: BType = null // scala.runtime.AbstractPartialFunction + var PartialFunctionReference: ClassBType = null // scala.PartialFunction + var AbstractPartialFunctionReference: ClassBType = null // scala.runtime.AbstractPartialFunction - var BoxesRunTime: BType = null + var BoxesRunTime: ClassBType = null /* * must-single-thread @@ -107,7 +105,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { // Other than that, they aren't needed there (e.g., `isSubtypeOf()` special-cases boxed classes, similarly for others). val boxedClasses = List(BoxedBooleanClass, BoxedCharacterClass, BoxedByteClass, BoxedShortClass, BoxedIntClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass) for(csym <- boxedClasses) { - val key = brefType(csym.javaBinaryName.toTypeName) + val key = ClassBType(csym.javaBinaryName.toTypeName) val tr = buildExemplar(key, csym) symExemplars.put(csym, tr) exemplars.put(tr.c, tr) @@ -147,16 +145,6 @@ abstract class BCodeTypes extends BCodeIdiomatic { scalaSerializableReference = exemplar(SerializableClass).c classCastExceptionReference = exemplar(ClassCastExceptionClass).c - /* - * The bytecode emitter special-cases String concatenation, in that three methods of `JCodeMethodN` - * ( `genStartConcat()` , `genStringConcat()` , and `genEndConcat()` ) - * don't obtain the method descriptor of the callee via `asmMethodType()` (as normally done) - * but directly emit callsites on StringBuilder using literal constant for method descriptors. - * In order to make sure those method descriptors are available as BTypes, they are initialized here. - */ - BType.getMethodType("()V") // necessary for JCodeMethodN.genStartConcat - BType.getMethodType("()Ljava/lang/String;") // necessary for JCodeMethodN.genEndConcat - PartialFunctionReference = exemplar(PartialFunctionClass).c for(idx <- 0 to definitions.MaxFunctionArity) { FunctionReference(idx) = exemplar(FunctionClass(idx)) @@ -165,12 +153,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { AbstractPartialFunctionReference = exemplar(AbstractPartialFunctionClass).c } - // later a few analyses (e.g. refreshInnerClasses) will look up BTypes based on descriptors in instructions - // we make sure those BTypes can be found via lookup as opposed to creating them on the fly. - BoxesRunTime = brefType("scala/runtime/BoxesRunTime") - asmBoxTo.values foreach { mnat: MethodNameAndType => BType.getMethodType(mnat.mdesc) } - asmUnboxTo.values foreach { mnat: MethodNameAndType => BType.getMethodType(mnat.mdesc) } - + BoxesRunTime = ClassBType("scala/runtime/BoxesRunTime") } /* @@ -191,28 +174,41 @@ abstract class BCodeTypes extends BCodeIdiomatic { // allowing answering `conforms()` without resorting to typer. // ------------------------------------------------ - val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked] - val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked] + /** + * TODO @lry should probably be a map form ClassBType to Tracked + */ + val exemplars = new java.util.concurrent.ConcurrentHashMap[BType, Tracked] - /* - * Typically, a question about a BType can be answered only by using the BType as lookup key in one or more maps. - * A `Tracked` object saves time by holding together information required to answer those questions: + /** + * Maps class symbols to their corresponding `Tracked` instance. + */ + val symExemplars = new java.util.concurrent.ConcurrentHashMap[Symbol, Tracked] + + /** + * A `Tracked` instance stores information about a BType. This allows ansering type questions + * without resolving to the compiler, in a thread-safe manner, in particular isSubtypeOf. * - * - `sc` denotes the bytecode-level superclass if any, null otherwise + * @param c the BType described by this `Tracked` + * @param flags the java flags for the type, computed by BCodeTypes#javaFlags + * @param sc the bytecode-level superclass if any, null otherwise + * @param ifaces the interfaces explicitly declared. Not included are those transitively + * supported, but the utility method `allLeafIfaces()` can be used for that. + * @param innersChain the containing classes for a non-package-level class `c`, null otherwise. * - * - `ifaces` denotes the interfaces explicitly declared. - * Not included are those transitively supported, but the utility method `allLeafIfaces()` can be used for that. + * Note: the optimizer may inline anonymous closures, thus eliding those inner classes (no + * physical class file is emitted for elided classes). Before committing `innersChain` to + * bytecode, cross-check with the list of elided classes (SI-6546). * - * - `innersChain` denotes the containing classes for a non-package-level class `c`, null otherwise. - * Note: the optimizer may inline anonymous closures, thus eliding those inner classes - * (no physical class file is emitted for elided classes). - * Before committing `innersChain` to bytecode, cross-check with the list of elided classes (SI-6546). + * All methods of this class can-multi-thread * - * All methods of this class can-multi-thread + * TODO @lry c: ClassBType. rename to ClassBTypeInfo */ - case class Tracked(c: BType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) { + case class Tracked(c: ClassBType, flags: Int, sc: Tracked, ifaces: Array[Tracked], innersChain: Array[InnerClassEntry]) { // not a case-field because we initialize it only for JVM classes we emit. + // TODO @lry make it an Option[List[BType]] + // TODO: this is currently not used. a commit in the optimizer branch uses this field to + // re-compute inner classes (ee4c185). leaving it in for now. private var _directMemberClasses: List[BType] = null def directMemberClasses: List[BType] = { @@ -223,9 +219,9 @@ abstract class BCodeTypes extends BCodeIdiomatic { def directMemberClasses_=(bs: List[BType]) { if (_directMemberClasses != null) { // TODO we enter here when both mirror class and plain class are emitted for the same ModuleClassSymbol. - assert(_directMemberClasses == bs.sortBy(_.off)) + assert(_directMemberClasses.sameElements(bs)) } - _directMemberClasses = bs.sortBy(_.off) + _directMemberClasses = bs } /* `isCompilingStdLib` saves the day when compiling: @@ -247,7 +243,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { def isInnerClass = { innersChain != null } def isLambda = { // ie isLCC || isTraditionalClosureClass - isFinal && (c.getSimpleName.contains(tpnme.ANON_FUN_NAME.toString)) && isFunctionType(c) + isFinal && (c.simpleName.contains(tpnme.ANON_FUN_NAME.toString)) && isFunctionType(c) } /* can-multi-thread */ @@ -350,8 +346,8 @@ abstract class BCodeTypes extends BCodeIdiomatic { val superInterfaces0: List[Symbol] = csym.mixinClasses val superInterfaces = existingSymbols(superInterfaces0 ++ csym.annotations.map(newParentForAttr)).distinct - assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString}") - assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString}") + assert(!superInterfaces.contains(NoSymbol), s"found NoSymbol among: ${superInterfaces.mkString(", ")}") + assert(superInterfaces.forall(s => s.isInterface || s.isTrait), s"found non-interface among: ${superInterfaces.mkString(", ")}") minimizeInterfaces(superInterfaces) } @@ -380,8 +376,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { if (opt != null) { return opt } - - val key = brefType(csym.javaBinaryName.toTypeName) + val key = new ClassBType(csym.javaBinaryName.toTypeName) assert(key.isNonSpecial || isCompilingStdLib, s"Not a class to track: ${csym.fullName}") // TODO accomodate the fix for SI-5031 of https://github.com/scala/scala/commit/0527b2549bcada2fda2201daa630369b377d0877 @@ -395,12 +390,10 @@ abstract class BCodeTypes extends BCodeIdiomatic { tr } - val EMPTY_TRACKED_ARRAY = Array.empty[Tracked] - /* * must-single-thread */ - private def buildExemplar(key: BType, csym: Symbol): Tracked = { + private def buildExemplar(key: ClassBType, csym: Symbol): Tracked = { val sc = if (csym.isImplClass) definitions.ObjectClass else csym.superClass @@ -413,14 +406,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { ((sc != NoSymbol) && !sc.isInterface) || isCompilingStdLib, "superClass out of order" ) - val ifaces = getSuperInterfaces(csym) map exemplar; - val ifacesArr = - if (ifaces.isEmpty) EMPTY_TRACKED_ARRAY - else { - val arr = new Array[Tracked](ifaces.size) - ifaces.copyToArray(arr) - arr - } + val ifacesArr = getSuperInterfaces(csym).map(exemplar).toArray val flags = mkFlags( javaFlags(csym), @@ -476,29 +462,30 @@ abstract class BCodeTypes extends BCodeIdiomatic { if ((b == jlCloneableReference) || (b == jioSerializableReference) || (b == AnyRefReference)) { true } - else if (b.isArray) { conforms(a.getComponentType, b.getComponentType) } + else if (b.isArray) { conforms(a.asArrayBType.componentType, // TODO @lry change to pattern match, get rid of casts + b.asArrayBType.componentType) } else { false } } else if (a.isBoxed) { // may be null if (b.isBoxed) { a == b } else if (b == AnyRefReference) { true } - else if (!(b.hasObjectSort)) { false } + else if (!(b.isClass)) { false } else { exemplars.get(a).isSubtypeOf(b) } // e.g., java/lang/Double conforms to java/lang/Number } else if (a.isNullType) { // known to be null if (b.isNothingType) { false } - else if (b.isValueType) { false } + else if (b.isPrimitive) { false } else { true } } else if (a.isNothingType) { // known to be Nothing true } - else if (a.isUnitType) { - b.isUnitType + else if (a == UNIT) { + b == UNIT } - else if (a.hasObjectSort) { // may be null + else if (a.isClass) { // may be null if (a.isNothingType) { true } - else if (b.hasObjectSort) { exemplars.get(a).isSubtypeOf(b) } + else if (b.isClass) { exemplars.get(a).isSubtypeOf(b) } else if (b.isArray) { a.isNullType } // documentation only, because `if(a.isNullType)` (above) covers this case already. else { false } } @@ -506,8 +493,8 @@ abstract class BCodeTypes extends BCodeIdiomatic { def msg = s"(a: $a, b: $b)" - assert(a.isNonUnitValueType, s"a isn't a non-Unit value type. $msg") - assert(b.isValueType, s"b isn't a value type. $msg") + assert(a.isNonVoidPrimitiveType, s"a isn't a non-Unit value type. $msg") + assert(b.isPrimitive, s"b isn't a value type. $msg") (a eq b) || (a match { case BOOL | BYTE | SHORT | CHAR => b == INT || b == LONG // TODO Actually, BOOL does NOT conform to LONG. Even with adapt(). @@ -521,7 +508,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { * can-multi-thread */ def maxValueType(a: BType, other: BType): BType = { - assert(a.isValueType, "maxValueType() is defined only for 1st arg valuetypes (2nd arg doesn't matter).") + assert(a.isPrimitive, "maxValueType() is defined only for 1st arg valuetypes (2nd arg doesn't matter).") def uncomparable: Nothing = { abort(s"Uncomparable BTypes: $a with $other") @@ -537,30 +524,30 @@ abstract class BCodeTypes extends BCodeIdiomatic { case BOOL => uncomparable case BYTE => - if (other == CHAR) INT + if (other == CHAR) INT else if (other.isNumericType) other else uncomparable case SHORT => other match { - case BYTE => SHORT - case CHAR => INT + case BYTE => SHORT + case CHAR => INT case INT | LONG | FLOAT | DOUBLE => other - case _ => uncomparable + case _ => uncomparable } case CHAR => other match { - case BYTE | SHORT => INT + case BYTE | SHORT => INT case INT | LONG | FLOAT | DOUBLE => other - case _ => uncomparable + case _ => uncomparable } case INT => other match { case BYTE | SHORT | CHAR => INT case LONG | FLOAT | DOUBLE => other - case _ => uncomparable + case _ => uncomparable } case LONG => @@ -569,7 +556,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { else uncomparable case FLOAT => - if (other == DOUBLE) DOUBLE + if (other == DOUBLE) DOUBLE else if (other.isNumericType) FLOAT else uncomparable @@ -586,18 +573,18 @@ abstract class BCodeTypes extends BCodeIdiomatic { * can-multi-thread */ final def maxType(a: BType, other: BType): BType = { - if (a.isValueType) { maxValueType(a, other) } + if (a.isPrimitive) { maxValueType(a, other) } else { if (a.isNothingType) return other; if (other.isNothingType) return a; if (a == other) return a; // Approximate `lub`. The common type of two references is always AnyRef. // For 'real' least upper bound wrt to subclassing use method 'lub'. - assert(a.isArray || a.isBoxed || a.hasObjectSort, s"This is not a valuetype and it's not something else, what is it? $a") + assert(a.isArray || a.isBoxed || a.isClass, s"This is not a valuetype and it's not something else, what is it? $a") // TODO For some reason, ICode thinks `REFERENCE(...).maxType(BOXED(whatever))` is `uncomparable`. Here, that has maxType AnyRefReference. // BTW, when swapping arguments, ICode says BOXED(whatever).maxType(REFERENCE(...)) == AnyRefReference, so I guess the above was an oversight in REFERENCE.maxType() - if (other.isRefOrArrayType) { AnyRefReference } - else { abort(s"Uncomparable BTypes: $a with $other") } + if (other.isRef) { AnyRefReference } + else { abort(s"Uncomparable BTypes: $a with $other") } } } @@ -609,7 +596,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { * can-multi-thread */ def isPartialFunctionType(t: BType): Boolean = { - (t.hasObjectSort) && exemplars.get(t).isSubtypeOf(PartialFunctionReference) + (t.isClass) && exemplars.get(t).isSubtypeOf(PartialFunctionReference) } /* @@ -618,7 +605,7 @@ abstract class BCodeTypes extends BCodeIdiomatic { * can-multi-thread */ def isFunctionType(t: BType): Boolean = { - if (!t.hasObjectSort) return false + if (!t.isClass) return false var idx = 0 val et: Tracked = exemplars.get(t) while (idx <= definitions.MaxFunctionArity) { @@ -724,14 +711,8 @@ abstract class BCodeTypes extends BCodeIdiomatic { } } - // now that we have all of `ics` , `csym` , and soon the inner-classes-chain, it's too tempting not to cache. - if (chain.isEmpty) { null } - else { - val arr = new Array[InnerClassEntry](chain.size) - (chain map toInnerClassEntry).copyToArray(arr) - - arr - } + if (chain.isEmpty) null + else chain.map(toInnerClassEntry).toArray } /* diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala new file mode 100644 index 0000000000..9aedc7f8b4 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -0,0 +1,416 @@ +package scala.tools.nsc +package backend.jvm + +import scala.collection.immutable +import scala.annotation.switch +import scala.tools.asm +import asm.Opcodes +import scala.collection.mutable.ListBuffer + +/** + * BTypes is a backend component that defines the class BType, a number of basic instances and + * some utilities. + * + * A BType is essentially an slice of the array `chrs` denoting the name of the type, and a field + * denoting the kind (object, array, method, or one of the primitive types). + * + * BTypes depends on Global just because it re-uses hash-consing of Name. It would be cleaner to + * create an interface for BTypeName and extend it in scala.reflect.internal.Names#Name, that + * would simplify testing BTypes (no Global needed). + */ +abstract class BTypes[G <: Global](val __global_dont_use: G) { + def chrs: Array[Char] + + /** + * Interface for names stored in `chrs` + */ + type BTypeName <: __global_dont_use.Name + + /** + * Create a new name in `chrs`. Names are assumed to be hash-consed. Equality on BType will use + * reference equality to compare the names. + */ + def createNewName(s: String): BTypeName + + /*sealed*/ trait BType { // Not sealed for now due to SI-8546 + final override def toString: String = this match { + case UNIT => "V" + case BOOL => "Z" + case CHAR => "C" + case BYTE => "B" + case SHORT => "S" + case INT => "I" + case FLOAT => "F" + case LONG => "J" + case DOUBLE => "D" + case c @ ClassBType(_, _) => "L" + c.internalName + ";" + case ArrayBType(component) => "[" + component + case MethodBType(args, res) => "(" + args.mkString + ")" + res + } + + /** + * @return The Java descriptor of this type. Examples: + * - int: I + * - java.lang.String: Ljava/lang/String; + * - int[]: [I + * - Object m(String s, double d): (Ljava/lang/String;D)Ljava/lang/Object; + */ + final def descriptor = toString + + /** + * @return 0 for void, 2 for long and double, 1 otherwise + */ + final def size: Int = this match { + case UNIT => 0 + case LONG | DOUBLE => 2 + case _ => 1 + } + + final def isPrimitive: Boolean = this.isInstanceOf[PrimitiveBType] + final def isRef: Boolean = this.isInstanceOf[RefBType] + final def isArray: Boolean = this.isInstanceOf[ArrayBType] + final def isClass: Boolean = this.isInstanceOf[ClassBType] + final def isMethod: Boolean = this.isInstanceOf[MethodBType] + + final def isNonVoidPrimitiveType = isPrimitive && this != UNIT + // TODO @lry should also include !isMethod in isNonSpecial? in this case it would be equivalent to isClass, so we could get rid of it. + final def isNonSpecial = !isPrimitive && !isArray && !isPhantomType + final def isNullType = this == RT_NULL || this == CT_NULL + final def isNothingType = this == RT_NOTHING || this == CT_NOTHING + final def isPhantomType = isNullType || isNothingType + + final def isBoxed = this match { + case BOXED_UNIT | BOXED_BOOLEAN | BOXED_CHAR | + BOXED_BYTE | BOXED_SHORT | BOXED_INT | + BOXED_FLOAT | BOXED_LONG | BOXED_DOUBLE => true + case _ => false + } + + final def isIntSizedType = this == BOOL || this == CHAR || this == BYTE || + this == SHORT || this == INT + final def isIntegralType = this == INT || this == BYTE || this == LONG || + this == CHAR || this == SHORT + final def isRealType = this == FLOAT || this == DOUBLE + final def isNumericType = isIntegralType || isRealType + final def isWideType = size == 2 + + /** + * See documentation of [[typedOpcode]]. + * The numbers are taken from asm.Type.VOID_TYPE ff., the values are those shifted by << 8. + */ + private def loadStoreOpcodeOffset: Int = this match { + case UNIT | INT => 0 + case BOOL | BYTE => 5 + case CHAR => 6 + case SHORT => 7 + case FLOAT => 2 + case LONG => 1 + case DOUBLE => 3 + case _ => 4 + } + + /** + * See documentation of [[typedOpcode]]. + * The numbers are taken from asm.Type.VOID_TYPE ff., the values are those shifted by << 16. + */ + private def typedOpcodeOffset: Int = this match { + case UNIT => 5 + case BOOL | CHAR | BYTE | SHORT | INT => 0 + case FLOAT => 2 + case LONG => 1 + case DOUBLE => 3 + case _ => 4 + } + + /** + * Some JVM opcodes have typed variants. This method returns the correct opcode according to + * the type. + * + * @param opcode A JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD, + * IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR + * IXOR and IRETURN. + * @return The opcode adapted to this java type. For example, if this type is `float` and + * `opcode` is `IRETURN`, this method returns `FRETURN`. + */ + final def typedOpcode(opcode: Int): Int = { + if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) + opcode + loadStoreOpcodeOffset + else + opcode + typedOpcodeOffset + } + + /** + * The asm.Type corresponding to this BType. + * + * Note about asm.Type.getObjectType (*): For class types, the method expects the internal + * name, i.e. without the surrounding 'L' and ';'. For array types on the other hand, the + * method expects a full descriptor, for example "[Ljava/lang/String;". + * + * See method asm.Type.getType that creates a asm.Type from a type descriptor + * - for an OBJECT type, the 'L' and ';' are not part of the range of the created Type + * - for an ARRAY type, the full descriptor is part of the range + */ + def toASMType: asm.Type = this match { + case UNIT => asm.Type.VOID_TYPE + case BOOL => asm.Type.BOOLEAN_TYPE + case CHAR => asm.Type.CHAR_TYPE + case BYTE => asm.Type.BYTE_TYPE + case SHORT => asm.Type.SHORT_TYPE + case INT => asm.Type.INT_TYPE + case FLOAT => asm.Type.FLOAT_TYPE + case LONG => asm.Type.LONG_TYPE + case DOUBLE => asm.Type.DOUBLE_TYPE + case c @ ClassBType(_, _) => asm.Type.getObjectType(c.internalName) // (*) + case a @ ArrayBType(_) => asm.Type.getObjectType(a.descriptor) + case m @ MethodBType(_, _) => asm.Type.getMethodType(m.descriptor) + } + + def asRefBType : RefBType = this.asInstanceOf[RefBType] + def asArrayBType: ArrayBType = this.asInstanceOf[ArrayBType] + def asClassBType: ClassBType = this.asInstanceOf[ClassBType] + } + + object BType { + /** + * @param chars The character array containing the descriptor + * @param start The position where the descriptor starts + * @return The BType and the index of the first character after the consumed descriptor + */ + private[BTypes] def fromNonMethodDescriptor(chars: Array[Char], start: Int): (BType, Int) = { + chars(start) match { + case 'L' => + var i = start + while (chars(i) != ';') { i += 1 } + // Example: chars = "IILpkg/Cls;I" + // ^ ^ + // start=2 i=10 + // `start + 1` to exclude the 'L', `i - start - 1` excludes the ';' + (new ClassBType(new String(chars, start + 1, i - start - 1)), i + 1) + case '[' => + val (res, next) = fromNonMethodDescriptor(chars, start + 1) + (ArrayBType(res), next) + case 'V' => (UNIT, start + 1) + case 'Z' => (BOOL, start + 1) + case 'C' => (CHAR, start + 1) + case 'B' => (BYTE, start + 1) + case 'S' => (SHORT, start + 1) + case 'I' => (INT, start + 1) + case 'F' => (FLOAT, start + 1) + case 'J' => (LONG, start + 1) + case 'D' => (DOUBLE, start + 1) + } + } + } + + sealed trait PrimitiveBType extends BType + + case object UNIT extends PrimitiveBType + case object BOOL extends PrimitiveBType + case object CHAR extends PrimitiveBType + case object BYTE extends PrimitiveBType + case object SHORT extends PrimitiveBType + case object INT extends PrimitiveBType + case object FLOAT extends PrimitiveBType + case object LONG extends PrimitiveBType + case object DOUBLE extends PrimitiveBType + + sealed trait RefBType extends BType { + /** + * The class or array type of this reference type. Used for ANEWARRAY, MULTIANEWARRAY, + * INSTANCEOF and CHECKCAST instructions. + * + * In contrast to the descriptor, this string does not contain the surrounding 'L' and ';' for + * class types, for example "java/lang/String". + * However, for array types, the full descriptor is used, for example "[Ljava/lang/String;". + * + * This can be verified for example using javap or ASMifier. + */ + def classOrArrayType: String = this match { + case c: ClassBType => c.internalName + case a: ArrayBType => a.descriptor + } + } + + /** + * Class or Interface type. + * + * Classes are represented using their name as a slice of the `chrs` array. This representation is + * efficient because the JVM class name is initially created using `classSymbol.javaBinaryName`. + * This already adds the necessary string to the `chrs` array, so it makes sense to reuse the same + * name table in the backend. + * + * Not a case class because that would expose the (Int, Int) constructor (didn't find a way to + * make it private, also the factory in the companion). + */ + class ClassBType private(val offset: Int, val length: Int) extends RefBType { + /** + * Construct a ClassBType for a given (intenred) class name. + * + * @param n The class name as a slice of the `chrs` array, without the surrounding 'L' and ';'. + * Note that `classSymbol.javaBinaryName` returns exactly such a name. + */ + def this(n: BTypeName) = this(n.start, n.length) + + /** + * Construct a ClassBType for a given java class name. + * + * @param s A class name of the form "java/lang/String", without the surrounding 'L' and ';'. + */ + def this(s: String) = this({ + assert(!(s.head == 'L' && s.last == ';'), s"Descriptor instead of internal name: $s") + createNewName(s) + }) + + /** + * The internal name of a class is the string returned by java.lang.Class.getName, with all '.' + * replaced by '/'. For example "java/lang/String". + */ + def internalName: String = new String(chrs, offset, length) + + /** + * @return The class name without the package prefix + */ + def simpleName: String = internalName.split("/").last + + /** + * Custom equals / hashCode are needed because this is not a case class. + */ + override def equals(o: Any): Boolean = (this eq o.asInstanceOf[Object]) || (o match { + case ClassBType(`offset`, `length`) => true + case _ => false + }) + + override def hashCode: Int = { + import scala.runtime.Statics + var acc: Int = -889275714 + acc = Statics.mix(acc, offset) + acc = Statics.mix(acc, length) + Statics.finalizeHash(acc, 2) + } + } + + object ClassBType { + def apply(n: BTypeName): ClassBType = new ClassBType(n) + def apply(s: String): ClassBType = new ClassBType(s) + + def unapply(c: ClassBType): Option[(Int, Int)] = + if (c == null) None + else Some((c.offset, c.length)) + } + + case class ArrayBType(componentType: BType) extends RefBType { + def dimension: Int = componentType match { + case a: ArrayBType => 1 + a.dimension + case _ => 1 + } + + def elementType: BType = componentType match { + case a: ArrayBType => a.elementType + case t => t + } + } + + case class MethodBType(argumentTypes: List[BType], returnType: BType) extends BType { + private def this(types: (List[BType], BType)) = this(types._1, types._2) + def this(descriptor: String) = this(MethodBType.decomposeMethodDescriptor(descriptor)) + } + + object MethodBType { + private def decomposeMethodDescriptor(descriptor: String): (List[BType], BType) = { + val chars = descriptor.toCharArray + assert(chars(0) == '(', s"Not a valid method descriptor: $descriptor") + var i = 1 + val argTypes = new ListBuffer[BType] + while (chars(i) != ')') { + val (argType, next) = BType.fromNonMethodDescriptor(chars, i) + argTypes += argType + i = next + } + val (resType, _) = BType.fromNonMethodDescriptor(chars, i + 1) // `i + 1` to skip the ')' + (argTypes.toList, resType) + } + def apply(descriptor: String) = { + val (argTypes, resType) = decomposeMethodDescriptor(descriptor) + new MethodBType(argTypes, resType) + } + } + + val BOXED_UNIT = ClassBType("java/lang/Void") + val BOXED_BOOLEAN = ClassBType("java/lang/Boolean") + val BOXED_BYTE = ClassBType("java/lang/Byte") + val BOXED_SHORT = ClassBType("java/lang/Short") + val BOXED_CHAR = ClassBType("java/lang/Character") + val BOXED_INT = ClassBType("java/lang/Integer") + val BOXED_LONG = ClassBType("java/lang/Long") + val BOXED_FLOAT = ClassBType("java/lang/Float") + val BOXED_DOUBLE = ClassBType("java/lang/Double") + + /* + * RT_NOTHING and RT_NULL exist at run-time only. They are the bytecode-level manifestation (in + * method signatures only) of what shows up as NothingClass resp. NullClass in Scala ASTs. + * + * Therefore, when RT_NOTHING or RT_NULL are to be emitted, a mapping is needed: the internal + * names of NothingClass and NullClass can't be emitted as-is. + */ + val RT_NOTHING = ClassBType("scala/runtime/Nothing$") + val RT_NULL = ClassBType("scala/runtime/Null$") + val CT_NOTHING = ClassBType("scala/Nothing") + val CT_NULL = ClassBType("scala/Null") + + val srBooleanRef = ClassBType("scala/runtime/BooleanRef") + val srByteRef = ClassBType("scala/runtime/ByteRef") + val srCharRef = ClassBType("scala/runtime/CharRef") + val srIntRef = ClassBType("scala/runtime/IntRef") + val srLongRef = ClassBType("scala/runtime/LongRef") + val srFloatRef = ClassBType("scala/runtime/FloatRef") + val srDoubleRef = ClassBType("scala/runtime/DoubleRef") + + /** + * Map from type kinds to the Java reference types. + * Useful when pushing class literals onto the operand stack (ldc instruction taking a class + * literal). + * @see Predef.classOf + * @see genConstant() + * + * TODO @lry rename to "boxedClassOfPrimitive" or so, check usages + */ + val classLiteral = immutable.Map[BType, ClassBType]( + UNIT -> BOXED_UNIT, + BOOL -> BOXED_BOOLEAN, + BYTE -> BOXED_BYTE, + SHORT -> BOXED_SHORT, + CHAR -> BOXED_CHAR, + INT -> BOXED_INT, + LONG -> BOXED_LONG, + FLOAT -> BOXED_FLOAT, + DOUBLE -> BOXED_DOUBLE + ) + + case class MethodNameAndType(name: String, descriptor: String) + + val asmBoxTo: immutable.Map[BType, MethodNameAndType] = { + Map( + BOOL -> MethodNameAndType("boxToBoolean", "(Z)Ljava/lang/Boolean;" ) , + BYTE -> MethodNameAndType("boxToByte", "(B)Ljava/lang/Byte;" ) , + CHAR -> MethodNameAndType("boxToCharacter", "(C)Ljava/lang/Character;") , + SHORT -> MethodNameAndType("boxToShort", "(S)Ljava/lang/Short;" ) , + INT -> MethodNameAndType("boxToInteger", "(I)Ljava/lang/Integer;" ) , + LONG -> MethodNameAndType("boxToLong", "(J)Ljava/lang/Long;" ) , + FLOAT -> MethodNameAndType("boxToFloat", "(F)Ljava/lang/Float;" ) , + DOUBLE -> MethodNameAndType("boxToDouble", "(D)Ljava/lang/Double;" ) + ) + } + + val asmUnboxTo: immutable.Map[BType, MethodNameAndType] = { + Map( + BOOL -> MethodNameAndType("unboxToBoolean", "(Ljava/lang/Object;)Z") , + BYTE -> MethodNameAndType("unboxToByte", "(Ljava/lang/Object;)B") , + CHAR -> MethodNameAndType("unboxToChar", "(Ljava/lang/Object;)C") , + SHORT -> MethodNameAndType("unboxToShort", "(Ljava/lang/Object;)S") , + INT -> MethodNameAndType("unboxToInt", "(Ljava/lang/Object;)I") , + LONG -> MethodNameAndType("unboxToLong", "(Ljava/lang/Object;)J") , + FLOAT -> MethodNameAndType("unboxToFloat", "(Ljava/lang/Object;)F") , + DOUBLE -> MethodNameAndType("unboxToDouble", "(Ljava/lang/Object;)D") + ) + } +} diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index 8e6c09213f..1d29fdee10 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -1,6 +1,6 @@ /* NSC -- new Scala compiler * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips + * @author Martin Odersky */ package scala.tools.nsc diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index b7f9b30e19..988c04f514 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -975,7 +975,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { index += jparamType.getSize() } - mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, javaType(m).getDescriptor) + mirrorMethod.visitMethodInsn(asm.Opcodes.INVOKEVIRTUAL, moduleName, mirrorMethodName, javaType(m).getDescriptor, false) mirrorMethod.visitInsn(jReturnType.getOpcode(asm.Opcodes.IRETURN)) mirrorMethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments @@ -1061,7 +1061,8 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { asm.Opcodes.INVOKEVIRTUAL, moduleName, androidFieldName.toString, - asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*) + asm.Type.getMethodDescriptor(creatorType, Array.empty[asm.Type]: _*), + false ) // PUTSTATIC `thisName`.CREATOR; @@ -1400,7 +1401,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // TODO param names: (m.params map (p => javaName(p.sym))) - // typestate: entering mode with valid call sequences: + // typestate: entering mode with valid call sequences: (see ASM Guide, 3.2.1) // [ visitAnnotationDefault ] ( visitAnnotation | visitParameterAnnotation | visitAttribute )* emitAnnotations(jmethod, others) @@ -1445,7 +1446,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { val hasStaticBitSet = ((flags & asm.Opcodes.ACC_STATIC) != 0) genCode(m, emitVars, hasStaticBitSet) - jmethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments + // visitMaxs needs to be called according to the protocol. The arguments will be ignored + // since maximums (and stack map frames) are computed. See ASM Guide, Section 3.2.1, + // section "ClassWriter options" + jmethod.visitMaxs(0, 0) } jmethod.visitEnd() @@ -1521,7 +1525,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { if (isStaticModule(clasz.symbol)) { clinit.visitTypeInsn(asm.Opcodes.NEW, thisName) clinit.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, - thisName, INSTANCE_CONSTRUCTOR_NAME, mdesc_arglessvoid) + thisName, INSTANCE_CONSTRUCTOR_NAME, mdesc_arglessvoid, false) } if (isParcelableClass) { legacyAddCreatorCode(clinit) } @@ -1665,16 +1669,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def rem(tk: TypeKind) { emitPrimitive(remOpcodes, tk) } def invokespecial(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false) } def invokestatic(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, false) } def invokeinterface(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc, true) } def invokevirtual(owner: String, name: String, desc: String) { - jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc) + jmethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false) } def goTo(label: asm.Label) { jmethod.visitJumpInsn(Opcodes.GOTO, label) } @@ -2924,7 +2928,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // invoke the superclass constructor, which will do the // necessary java reflection and create Method objects. - constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor) + constructor.visitMethodInsn(asm.Opcodes.INVOKESPECIAL, "scala/beans/ScalaBeanInfo", INSTANCE_CONSTRUCTOR_NAME, conJType.getDescriptor, false) constructor.visitInsn(asm.Opcodes.RETURN) constructor.visitMaxs(0, 0) // just to follow protocol, dummy arguments diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index 193100474c..9b292fee7f 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -243,6 +243,12 @@ abstract class GenBCode extends BCodeSyncAndTry { val plainC = SubItem3(plain.name, getByteArray(plain)) val beanC = if (bean == null) null else SubItem3(bean.name, getByteArray(bean)) + if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) { + if (mirrorC != null) AsmUtils.traceClass(mirrorC.jclassBytes) + AsmUtils.traceClass(plainC.jclassBytes) + if (beanC != null) AsmUtils.traceClass(beanC.jclassBytes) + } + q3 add Item3(arrivalPos, mirrorC, plainC, beanC, outFolder) } diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index c49f23852f..a866173a88 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -56,11 +56,8 @@ abstract class ClosureElimination extends SubComponent { case (BOX(t1), UNBOX(t2)) if (t1 == t2) => Some(Nil) - case (LOAD_FIELD(sym, isStatic), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) => - if (isStatic) - Some(Nil) - else - Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil) + case (LOAD_FIELD(sym, /* isStatic */false), DROP(_)) if !sym.hasAnnotation(definitions.VolatileAttr) && inliner.isClosureClass(sym.owner) => + Some(DROP(REFERENCE(definitions.ObjectClass)) :: Nil) case _ => None } diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 90c37ba0b3..4b419b210c 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -169,9 +169,14 @@ abstract class DeadCodeElimination extends SubComponent { case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) | THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) | - LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) => + LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() | CHECK_CAST(_) | CREATE_ARRAY(_, _) => moveToWorkList() + case LOAD_FIELD(sym, isStatic) if isStatic || !inliner.isClosureClass(sym.owner) => + // static load may trigger static initization. + // non-static load can throw NPE (but we know closure fields can't be accessed via a + // null reference. + moveToWorkList() case CALL_METHOD(m1, _) if isSideEffecting(m1) => moveToWorkList() @@ -193,6 +198,8 @@ abstract class DeadCodeElimination extends SubComponent { moveToWorkListIf(necessary) case LOAD_MODULE(sym) if isLoadNeeded(sym) => moveToWorkList() // SI-4859 Module initialization might side-effect. + case CALL_PRIMITIVE(Arithmetic(DIV | REM, INT | LONG) | ArrayLength(_)) => + moveToWorkList() // SI-8601 Might divide by zero case _ => () moveToWorkListIf(cond = false) } diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 1468680fe0..1f8ab7e887 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -132,7 +132,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre } val params = ((optionSymbol(thisProxy) map {proxy:Symbol => ValDef(proxy)}) ++ (target.paramss.flatten map ValDef.apply)).toList - val methSym = oldClass.newMethod(unit.freshTermName(nme.accessor.toString()), target.pos, FINAL | BRIDGE | SYNTHETIC | PROTECTED | STATIC) + val methSym = oldClass.newMethod(unit.freshTermName(nme.accessor.toString() + "$"), target.pos, FINAL | BRIDGE | SYNTHETIC | PROTECTED | STATIC) val paramSyms = params map {param => methSym.newSyntheticValueParam(param.symbol.tpe, param.name) } @@ -238,7 +238,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // - reinstate the assertion in `Erasure.resolveAnonymousBridgeClash` val suffix = "$lambda$" + ( if (funOwner.isPrimaryConstructor) "" - else "$" + funOwner.name + else "$" + funOwner.name + "$" ) val name = unit.freshTypeName(s"${oldClass.name.decode}$suffix") diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index e036035397..2f2142027f 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -1050,20 +1050,18 @@ abstract class Erasure extends AddInterfaces } } - def isAccessible(sym: Symbol) = localTyper.context.isAccessible(sym, sym.owner.thisType) - if (!isAccessible(owner) && qual.tpe != null) { + def isJvmAccessible(sym: Symbol) = (sym.isClass && !sym.isJavaDefined) || localTyper.context.isAccessible(sym, sym.owner.thisType) + if (!isJvmAccessible(owner) && qual.tpe != null) { qual match { case Super(_, _) => - // Insert a cast here at your peril -- see SI-5162. Bail out if the target method is defined in - // Java, otherwise, we'd get an IllegalAccessError at runtime. If the target method is defined in - // Scala, however, we should have access. - if (owner.isJavaDefined) unit.error(tree.pos, s"Unable to access ${tree.symbol.fullLocationString} with a super reference.") + // Insert a cast here at your peril -- see SI-5162. + unit.error(tree.pos, s"Unable to access ${tree.symbol.fullLocationString} with a super reference.") tree case _ => // Todo: Figure out how qual.tpe could be null in the check above (it does appear in build where SwingWorker.this // has a null type). val qualSym = qual.tpe.widen.typeSymbol - if (isAccessible(qualSym) && !qualSym.isPackageClass && !qualSym.isPackageObjectClass) { + if (isJvmAccessible(qualSym) && !qualSym.isPackageClass && !qualSym.isPackageObjectClass) { // insert cast to prevent illegal access error (see #4283) // util.trace("insert erasure cast ") (*/ treeCopy.Select(tree, gen.mkAttributedCast(qual, qual.tpe.widen), name) //) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 33e8e47c76..313d0a6e61 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -391,7 +391,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else { sourceModule setPos sym.pos if (sourceModule.flags != MODULE) { - log("!!! Directly setting sourceModule flags from %s to MODULE".format(sourceModule.flagString)) + log(s"!!! Directly setting sourceModule flags for $sourceModule from ${sourceModule.flagString} to MODULE") sourceModule.flags = MODULE } } diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 02e55241b3..908aa69310 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -538,6 +538,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { bytecodeClazz.info val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE) + sClass.setAnnotations(clazz.annotations) // SI-8574 important that the subclass picks up @SerialVersionUID, @strictfp, etc. def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long, newName: Name = null) = member.cloneSymbol(sClass, flagFn(member.flags | SPECIALIZED), newName) diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index d87090fa46..73c3e6f016 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1306,7 +1306,7 @@ trait Implicits { } def wrapResult(tree: Tree): SearchResult = - if (tree == EmptyTree) SearchFailure else new SearchResult(tree, EmptyTreeTypeSubstituter, Nil) + if (tree == EmptyTree) SearchFailure else new SearchResult(atPos(pos.focus)(tree), EmptyTreeTypeSubstituter, Nil) /** Materializes implicits of predefined types (currently, manifests and tags). * Will be replaced by implicit macros once we fix them. diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 4382a2c6f7..099031d536 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1040,10 +1040,10 @@ trait Namers extends MethodSynthesis { * so the resulting type is a valid external method type, it does not contain (references to) skolems. */ def thisMethodType(restpe: Type) = { - val checkDependencies = new DependentTypeChecker(context)(this) - checkDependencies check vparamSymss - // DEPMETTODO: check not needed when they become on by default - checkDependencies(restpe) + if (vparamSymss.lengthCompare(0) > 0) { // OPT fast path for methods of 0-1 parameter lists + val checkDependencies = new DependentTypeChecker(context)(this) + checkDependencies check vparamSymss + } val makeMethodType = (vparams: List[Symbol], restpe: Type) => { // TODODEPMET: check that we actually don't need to do anything here @@ -1177,7 +1177,13 @@ trait Namers extends MethodSynthesis { } } - addDefaultGetters(meth, ddef, vparamss, tparams, overriddenSymbol(methResTp)) + val overridden = { + val isConstr = meth.isConstructor + if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol(methResTp) + } + val hasDefaults = mexists(vparamss)(_.symbol.hasDefault) || mexists(overridden.paramss)(_.hasDefault) + if (hasDefaults) + addDefaultGetters(meth, ddef, vparamss, tparams, overridden) // fast track macros, i.e. macros defined inside the compiler, are hardcoded // hence we make use of that and let them have whatever right-hand side they need @@ -1219,7 +1225,7 @@ trait Namers extends MethodSynthesis { * typechecked, the corresponding param would not yet have the "defaultparam" * flag. */ - private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overriddenSymbol: => Symbol) { + private def addDefaultGetters(meth: Symbol, ddef: DefDef, vparamss: List[List[ValDef]], tparams: List[TypeDef], overridden: Symbol) { val DefDef(_, _, rtparams0, rvparamss0, _, _) = resetAttrs(ddef.duplicate) // having defs here is important to make sure that there's no sneaky tree sharing // in methods with multiple default parameters @@ -1227,7 +1233,6 @@ trait Namers extends MethodSynthesis { def rvparamss = rvparamss0.map(_.map(_.duplicate)) val methOwner = meth.owner val isConstr = meth.isConstructor - val overridden = if (isConstr || !methOwner.isClass) NoSymbol else overriddenSymbol val overrides = overridden != NoSymbol && !overridden.isOverloaded // value parameters of the base class (whose defaults might be overridden) var baseParamss = (vparamss, overridden.tpe.paramss) match { @@ -1745,7 +1750,6 @@ trait Namers extends MethodSynthesis { for (p <- vps) this(p.info) // can only refer to symbols in earlier parameter sections - // (if the extension is enabled) okParams ++= vps } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index bf98c0e3dc..66b1c2d87a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1687,7 +1687,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val sameSourceFile = context.unit.source.file == psym.sourceFile - if (psym.hasDeprecatedInheritanceAnnotation && !sameSourceFile) { + if (!isPastTyper && psym.hasDeprecatedInheritanceAnnotation && !sameSourceFile) { val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse "" val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix" unit.deprecationWarning(parent.pos, msg) @@ -3022,7 +3022,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper || (looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate) ) - def checkNoDoubleDefs(stats: List[Tree]): Unit = { + def checkNoDoubleDefs: Unit = { val scope = if (inBlock) context.scope else context.owner.info.decls var e = scope.elems while ((e ne null) && e.owner == scope) { @@ -3057,8 +3057,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // the corresponding synthetics to the package class, only to the package object class. def shouldAdd(sym: Symbol) = inBlock || !context.isInPackageObject(sym, context.owner) - for (sym <- scope if shouldAdd(sym)) - for (tree <- context.unit.synthetics get sym) { + for (sym <- scope) + for (tree <- context.unit.synthetics get sym if shouldAdd(sym)) { // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop newStats += typedStat(tree) // might add even more synthetics to the scope context.unit.synthetics -= sym } @@ -3104,7 +3104,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val stats1 = stats mapConserve typedStat if (phase.erasedTypes) stats1 else { - checkNoDoubleDefs(stats1) + // As packages are open, it doesn't make sense to check double definitions here. Furthermore, + // it is expensive if the package is large. Instead, such double defininitions are checked in `Namers.enterInScope` + if (!context.owner.isPackageClass) + checkNoDoubleDefs addSynthetics(stats1) } } diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala index cd928a2b61..c0468e8b02 100644 --- a/src/library/scala/StringContext.scala +++ b/src/library/scala/StringContext.scala @@ -8,6 +8,9 @@ package scala +import java.lang.{ StringBuilder => JLSBuilder } +import scala.annotation.tailrec + /** This class provides the basic mechanism to do String Interpolation. * String Interpolation allows users * to embed variable references directly in *processed* string literals. @@ -41,7 +44,7 @@ package scala * val x: JSONObject = json"{ a: $a }" * }}} * - * Here the `JsonHelper` extenion class implicitly adds the `json` method to + * Here the `JsonHelper` extension class implicitly adds the `json` method to * `StringContext` which can be used for `json` string literals. * * @since 2.10.0 @@ -118,7 +121,7 @@ case class StringContext(parts: String*) { checkLengths(args) val pi = parts.iterator val ai = args.iterator - val bldr = new java.lang.StringBuilder(process(pi.next())) + val bldr = new JLSBuilder(process(pi.next())) while (ai.hasNext) { bldr append ai.next bldr append process(pi.next()) @@ -186,60 +189,60 @@ object StringContext { */ def treatEscapes(str: String): String = treatEscapes0(str, strict = false) + /** Treats escapes, but disallows octal escape sequences. */ def processEscapes(str: String): String = treatEscapes0(str, strict = true) private def treatEscapes0(str: String, strict: Boolean): String = { - lazy val bldr = new java.lang.StringBuilder val len = str.length - var start = 0 - var cur = 0 - var idx = 0 - def output(ch: Char) = { - bldr.append(str, start, cur) - bldr append ch - start = idx - } - while (idx < len) { - cur = idx - if (str(idx) == '\\') { - idx += 1 - if (idx >= len) throw new InvalidEscapeException(str, cur) - if ('0' <= str(idx) && str(idx) <= '7') { - if (strict) throw new InvalidEscapeException(str, cur) - val leadch = str(idx) - var oct = leadch - '0' - idx += 1 - if (idx < len && '0' <= str(idx) && str(idx) <= '7') { - oct = oct * 8 + str(idx) - '0' - idx += 1 - if (idx < len && leadch <= '3' && '0' <= str(idx) && str(idx) <= '7') { - oct = oct * 8 + str(idx) - '0' + // replace escapes with given first escape + def replace(first: Int): String = { + val b = new JLSBuilder + // append replacement starting at index `i`, with `next` backslash + @tailrec def loop(i: Int, next: Int): String = { + if (next >= 0) { + //require(str(next) == '\\') + if (next > i) b.append(str, i, next) + var idx = next + 1 + if (idx >= len) throw new InvalidEscapeException(str, next) + val c = str(idx) match { + case 'b' => '\b' + case 't' => '\t' + case 'n' => '\n' + case 'f' => '\f' + case 'r' => '\r' + case '"' => '"' + case '\'' => '\'' + case '\\' => '\\' + case o if '0' <= o && o <= '7' => + if (strict) throw new InvalidEscapeException(str, next) + val leadch = str(idx) + var oct = leadch - '0' idx += 1 - } + if (idx < len && '0' <= str(idx) && str(idx) <= '7') { + oct = oct * 8 + str(idx) - '0' + idx += 1 + if (idx < len && leadch <= '3' && '0' <= str(idx) && str(idx) <= '7') { + oct = oct * 8 + str(idx) - '0' + idx += 1 + } + } + idx -= 1 // retreat + oct.toChar + case _ => throw new InvalidEscapeException(str, next) } - output(oct.toChar) + idx += 1 // advance + b append c + loop(idx, str.indexOf('\\', idx)) } else { - val ch = str(idx) - idx += 1 - output { - ch match { - case 'b' => '\b' - case 't' => '\t' - case 'n' => '\n' - case 'f' => '\f' - case 'r' => '\r' - case '\"' => '\"' - case '\'' => '\'' - case '\\' => '\\' - case _ => throw new InvalidEscapeException(str, cur) - } - } + if (i < len) b.append(str, i, len) + b.toString } - } else { - idx += 1 } + loop(0, first) + } + str indexOf '\\' match { + case -1 => str + case i => replace(i) } - if (start == 0) str - else bldr.append(str, start, idx).toString } } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index 1b496383a3..f6f46e158f 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -922,11 +922,16 @@ trait Iterator[+A] extends TraversableOnce[A] { /** For reasons which remain to be determined, calling * self.take(n).toSeq cause an infinite loop, so we have * a slight variation on take for local usage. + * NB: self.take.toSeq is slice.toStream, lazily built on self, + * so a subsequent self.hasNext would not test self after the + * group was consumed. */ private def takeDestructively(size: Int): Seq[A] = { val buf = new ArrayBuffer[A] var i = 0 - while (self.hasNext && i < size) { + // The order of terms in the following condition is important + // here as self.hasNext could be blocking + while (i < size && self.hasNext) { buf += self.next i += 1 } @@ -943,12 +948,10 @@ trait Iterator[+A] extends TraversableOnce[A] { // so the rest of the code can be oblivious val xs: Seq[B] = { val res = takeDestructively(count) - // extra checks so we don't calculate length unless there's reason - if (pad.isDefined && !self.hasNext) { - val shortBy = count - res.length - if (shortBy > 0) res ++ padding(shortBy) else res - } - else res + // was: extra checks so we don't calculate length unless there's reason + // but since we took the group eagerly, just use the fast length + val shortBy = count - res.length + if (shortBy > 0 && pad.isDefined) res ++ padding(shortBy) else res } lazy val len = xs.length lazy val incomplete = len < count diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index b60ea86ab0..d3a7db6968 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -345,8 +345,8 @@ trait TraversableLike[+A, +Repr] extends Any * $mayNotTerminateInf * * @param p the predicate used to test elements. - * @return `true` if the given predicate `p` holds for all elements - * of this $coll, otherwise `false`. + * @return `true` if this $coll is empty, otherwise `true` if the given predicate `p` + * holds for all elements of this $coll, otherwise `false`. */ def forall(p: A => Boolean): Boolean = { var result = true @@ -362,8 +362,8 @@ trait TraversableLike[+A, +Repr] extends Any * $mayNotTerminateInf * * @param p the predicate used to test elements. - * @return `true` if the given predicate `p` holds for some of the - * elements of this $coll, otherwise `false`. + * @return `false` if this $coll is empty, otherwise `true` if the given predicate `p` + * holds for some of the elements of this $coll, otherwise `false` */ def exists(p: A => Boolean): Boolean = { var result = false diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala index 072fd3da44..a8c4e047ab 100644 --- a/src/library/scala/collection/TraversableOnce.scala +++ b/src/library/scala/collection/TraversableOnce.scala @@ -85,10 +85,9 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] { */ def seq: TraversableOnce[A] - /** Presently these are abstract because the Traversable versions use - * breakable/break, and I wasn't sure enough of how that's supposed to - * function to consolidate them with the Iterator versions. - */ + // Presently these are abstract because the Traversable versions use + // breakable/break, and I wasn't sure enough of how that's supposed to + // function to consolidate them with the Iterator versions. def forall(p: A => Boolean): Boolean def exists(p: A => Boolean): Boolean def find(p: A => Boolean): Option[A] diff --git a/src/library/scala/collection/immutable/HashSet.scala b/src/library/scala/collection/immutable/HashSet.scala index 726937efd9..e4b7371ed4 100644 --- a/src/library/scala/collection/immutable/HashSet.scala +++ b/src/library/scala/collection/immutable/HashSet.scala @@ -162,6 +162,13 @@ class HashSet[A] extends AbstractSet[A] def - (e: A): HashSet[A] = nullToEmpty(removed0(e, computeHash(e), 0)) + /** Returns this $coll as an immutable set. + * + * A new set will not be built; lazy collections will stay lazy. + */ + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] + override def filter(p: A => Boolean) = { val buffer = new Array[HashSet[A]](bufferSize(size)) nullToEmpty(filter0(p, false, 0, buffer, 0)) diff --git a/src/library/scala/collection/immutable/ListSet.scala b/src/library/scala/collection/immutable/ListSet.scala index 1bb07eb02d..89d1a9640e 100644 --- a/src/library/scala/collection/immutable/ListSet.scala +++ b/src/library/scala/collection/immutable/ListSet.scala @@ -138,6 +138,13 @@ class ListSet[A] extends AbstractSet[A] override def stringPrefix = "ListSet" + /** Returns this $coll as an immutable set. + * + * A new set will not be built; lazy collections will stay lazy. + */ + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] + /** Represents an entry in the `ListSet`. */ protected class Node(override val head: A) extends ListSet[A] with Serializable { diff --git a/src/library/scala/collection/immutable/Set.scala b/src/library/scala/collection/immutable/Set.scala index 0fbf7942d4..7725ad9ee3 100644 --- a/src/library/scala/collection/immutable/Set.scala +++ b/src/library/scala/collection/immutable/Set.scala @@ -35,12 +35,7 @@ trait Set[A] extends Iterable[A] override def companion: GenericCompanion[Set] = Set - /** Returns this $coll as an immutable map. - * - * A new map will not be built; lazy collections will stay lazy. - */ - @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") - override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] + override def toSet[B >: A]: Set[B] = to[({type l[a] = immutable.Set[B]})#l] // for bincompat; remove in dev override def seq: Set[A] = this protected override def parCombiner = ParSet.newCombiner[A] // if `immutable.SetLike` gets introduced, please move this there! @@ -62,6 +57,7 @@ object Set extends ImmutableSetFactory[Set] { def - (elem: Any): Set[Any] = this def iterator: Iterator[Any] = Iterator.empty override def foreach[U](f: Any => U): Unit = {} + override def toSet[B >: Any]: Set[B] = this.asInstanceOf[Set[B]] } private[collection] def emptyInstance: Set[Any] = EmptySet @@ -92,6 +88,8 @@ object Set extends ImmutableSetFactory[Set] { if (f(elem1)) Some(elem1) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } /** An optimized representation for immutable sets of size 2 */ @@ -123,6 +121,8 @@ object Set extends ImmutableSetFactory[Set] { else if (f(elem2)) Some(elem2) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } /** An optimized representation for immutable sets of size 3 */ @@ -156,6 +156,8 @@ object Set extends ImmutableSetFactory[Set] { else if (f(elem3)) Some(elem3) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } /** An optimized representation for immutable sets of size 4 */ @@ -191,6 +193,8 @@ object Set extends ImmutableSetFactory[Set] { else if (f(elem4)) Some(elem4) else None } + @deprecatedOverriding("Immutable sets should do nothing on toSet but return themselves cast as a Set.", "2.11.0") + override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]] } } diff --git a/src/library/scala/collection/mutable/AVLTree.scala b/src/library/scala/collection/mutable/AVLTree.scala index de09bb2040..cc2acb74d4 100644 --- a/src/library/scala/collection/mutable/AVLTree.scala +++ b/src/library/scala/collection/mutable/AVLTree.scala @@ -14,8 +14,8 @@ package mutable * An immutable AVL Tree implementation formerly used by mutable.TreeSet * * @author Lucien Pereira - * @deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.0") */ +@deprecated("AVLTree and its related classes are being removed from the standard library since they're not different enough from RedBlackTree to justify keeping them.", "2.11.2") private[mutable] sealed trait AVLTree[+A] extends Serializable { def balance: Int diff --git a/src/library/scala/concurrent/Lock.scala b/src/library/scala/concurrent/Lock.scala index 1c00c0e91f..8d18da2d38 100644 --- a/src/library/scala/concurrent/Lock.scala +++ b/src/library/scala/concurrent/Lock.scala @@ -14,8 +14,8 @@ package scala.concurrent * * @author Martin Odersky * @version 1.0, 10/03/2003 - * @deprecated("Use java.util.concurrent.locks.Lock", "2.11.0") */ +@deprecated("Use java.util.concurrent.locks.Lock", "2.11.2") class Lock { var available = true diff --git a/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java b/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java index b1b100fbb0..d97756c171 100644 --- a/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java +++ b/src/partest-javaagent/scala/tools/partest/javaagent/ProfilerVisitor.java @@ -50,7 +50,7 @@ public class ProfilerVisitor extends ClassVisitor implements Opcodes { mv.visitLdcInsn(name); mv.visitLdcInsn(desc); mv.visitMethodInsn(INVOKESTATIC, profilerClass, "methodCalled", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", false); } } return mv; diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index ae9f2da4e5..b50f324074 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -40,7 +40,10 @@ trait Names extends api.Names { /** Hashtable for finding type names quickly. */ private val typeHashtable = new Array[TypeName](HASH_SIZE) - /** The hashcode of a name. */ + /** + * The hashcode of a name depends on the first, the last and the middle character, + * and the length of the name. + */ private def hashValue(cs: Array[Char], offset: Int, len: Int): Int = if (len > 0) (len * (41 * 41 * 41) + @@ -104,10 +107,21 @@ trait Names extends api.Names { // The logic order here is future-proofing against the possibility // that name.toString will become an eager val, in which case the call // to enterChars cannot follow the construction of the TermName. - val ncStart = nc - enterChars(cs, offset, len) - if (cachedString ne null) new TermName_S(ncStart, len, h, cachedString) - else new TermName_R(ncStart, len, h) + var startIndex = 0 + if (cs == chrs) { + // Optimize for subName, the new name is already stored in chrs + startIndex = offset + } else { + startIndex = nc + enterChars(cs, offset, len) + } + val next = termHashtable(h) + val termName = + if (cachedString ne null) new TermName_S(startIndex, len, next, cachedString) + else new TermName_R(startIndex, len, next) + // Add the new termName to the hashtable only after it's been fully constructed + termHashtable(h) = termName + termName } } if (synchronizeNames) nameLock.synchronized(body) else body @@ -145,40 +159,20 @@ trait Names extends api.Names { newTermName(bs, offset, len).toTypeName /** - * Used only by the GenBCode backend, to represent bytecode-level types in a way that makes equals() and hashCode() efficient. - * For bytecode-level types of OBJECT sort, its internal name (not its descriptor) is stored. - * For those of ARRAY sort, its descriptor is stored ie has a leading '[' - * For those of METHOD sort, its descriptor is stored ie has a leading '(' + * Used by the GenBCode backend to lookup type names that are known to already exist. This method + * might be invoked in a multi-threaded setting. Invoking newTypeName instead might be unsafe. * - * can-multi-thread - * TODO SI-6240 !!! JZ Really? the constructors TermName and TypeName publish unconstructed `this` references - * into the hash tables; we could observe them here before the subclass constructor completes. + * can-multi-thread: names are added to the hash tables only after they are fully constructed. */ - final def lookupTypeName(cs: Array[Char]): TypeName = { lookupTypeNameIfExisting(cs, true) } - - final def lookupTypeNameIfExisting(cs: Array[Char], failOnNotFound: Boolean): TypeName = { - - val hterm = hashValue(cs, 0, cs.size) & HASH_MASK - var nterm = termHashtable(hterm) - while ((nterm ne null) && (nterm.length != cs.size || !equals(nterm.start, cs, 0, cs.size))) { - nterm = nterm.next - } - if (nterm eq null) { - if (failOnNotFound) { assert(false, "TermName not yet created: " + new String(cs)) } - return null - } + final def lookupTypeName(cs: Array[Char]): TypeName = { + val hash = hashValue(cs, 0, cs.length) & HASH_MASK + var typeName = typeHashtable(hash) - val htype = hashValue(chrs, nterm.start, nterm.length) & HASH_MASK - var ntype = typeHashtable(htype) - while ((ntype ne null) && ntype.start != nterm.start) { - ntype = ntype.next + while ((typeName ne null) && (typeName.length != cs.length || !equals(typeName.start, cs, 0, cs.length))) { + typeName = typeName.next } - if (ntype eq null) { - if (failOnNotFound) { assert(false, "TypeName not yet created: " + new String(cs)) } - return null - } - - ntype + assert(typeName != null, s"TypeName ${new String(cs)} not yet created.") + typeName } // Classes ---------------------------------------------------------------------- @@ -515,43 +509,47 @@ trait Names extends api.Names { /** TermName_S and TypeName_S have fields containing the string version of the name. * TermName_R and TypeName_R recreate it each time toString is called. */ - private final class TermName_S(index0: Int, len0: Int, hash: Int, override val toString: String) extends TermName(index0, len0, hash) { - protected def createCompanionName(h: Int): TypeName = new TypeName_S(index, len, h, toString) + private final class TermName_S(index0: Int, len0: Int, next0: TermName, override val toString: String) extends TermName(index0, len0, next0) { + protected def createCompanionName(next: TypeName): TypeName = new TypeName_S(index, len, next, toString) override def newName(str: String): TermName = newTermNameCached(str) } - private final class TypeName_S(index0: Int, len0: Int, hash: Int, override val toString: String) extends TypeName(index0, len0, hash) { - protected def createCompanionName(h: Int): TermName = new TermName_S(index, len, h, toString) + private final class TypeName_S(index0: Int, len0: Int, next0: TypeName, override val toString: String) extends TypeName(index0, len0, next0) { override def newName(str: String): TypeName = newTypeNameCached(str) } - private final class TermName_R(index0: Int, len0: Int, hash: Int) extends TermName(index0, len0, hash) { - protected def createCompanionName(h: Int): TypeName = new TypeName_R(index, len, h) + private final class TermName_R(index0: Int, len0: Int, next0: TermName) extends TermName(index0, len0, next0) { + protected def createCompanionName(next: TypeName): TypeName = new TypeName_R(index, len, next) override def toString = new String(chrs, index, len) } - private final class TypeName_R(index0: Int, len0: Int, hash: Int) extends TypeName(index0, len0, hash) { - protected def createCompanionName(h: Int): TermName = new TermName_R(index, len, h) + private final class TypeName_R(index0: Int, len0: Int, next0: TypeName) extends TypeName(index0, len0, next0) { override def toString = new String(chrs, index, len) } // SYNCNOTE: caller to constructor must synchronize if `synchronizeNames` is enabled - sealed abstract class TermName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) with TermNameApi { + sealed abstract class TermName(index0: Int, len0: Int, val next: TermName) extends Name(index0, len0) with TermNameApi { type ThisNameType = TermName protected[this] def thisName: TermName = this - val next: TermName = termHashtable(hash) - termHashtable(hash) = this + def isTermName: Boolean = true def isTypeName: Boolean = false def toTermName: TermName = this def toTypeName: TypeName = { def body = { + // Re-computing the hash saves a field for storing it in the TermName val h = hashValue(chrs, index, len) & HASH_MASK var n = typeHashtable(h) while ((n ne null) && n.start != index) n = n.next if (n ne null) n - else createCompanionName(h) + else { + val next = typeHashtable(h) + val typeName = createCompanionName(next) + // Add the new typeName to the hashtable only after it's been fully constructed + typeHashtable(h) = typeName + typeName + } } if (synchronizeNames) nameLock.synchronized(body) else body } @@ -562,7 +560,7 @@ trait Names extends api.Names { def nameKind = "term" /** SYNCNOTE: caller must synchronize if `synchronizeNames` is enabled */ - protected def createCompanionName(h: Int): TypeName + protected def createCompanionName(next: TypeName): TypeName } implicit val TermNameTag = ClassTag[TermName](classOf[TermName]) @@ -572,24 +570,22 @@ trait Names extends api.Names { def unapply(name: TermName): Option[String] = Some(name.toString) } - sealed abstract class TypeName(index0: Int, len0: Int, hash: Int) extends Name(index0, len0) with TypeNameApi { + sealed abstract class TypeName(index0: Int, len0: Int, val next: TypeName) extends Name(index0, len0) with TypeNameApi { type ThisNameType = TypeName protected[this] def thisName: TypeName = this - val next: TypeName = typeHashtable(hash) - typeHashtable(hash) = this - def isTermName: Boolean = false def isTypeName: Boolean = true def toTermName: TermName = { def body = { + // Re-computing the hash saves a field for storing it in the TypeName val h = hashValue(chrs, index, len) & HASH_MASK var n = termHashtable(h) while ((n ne null) && n.start != index) n = n.next - if (n ne null) n - else createCompanionName(h) + assert (n ne null, s"TypeName $this is missing its correspondent") + n } if (synchronizeNames) nameLock.synchronized(body) else body } @@ -601,8 +597,6 @@ trait Names extends api.Names { def nameKind = "type" override def decode = if (nameDebug) super.decode + "!" else super.decode - /** SYNCNOTE: caller must synchronize if `synchronizeNames` is enabled */ - protected def createCompanionName(h: Int): TermName } implicit val TypeNameTag = ClassTag[TypeName](classOf[TypeName]) diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index cf3f356daa..103f885ad4 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -48,22 +48,17 @@ trait Scopes extends api.Scopes { self: SymbolTable => * This is necessary because when run from reflection every scope needs to have a * SynchronizedScope as mixin. */ - class Scope protected[Scopes] (initElems: ScopeEntry = null, initFingerPrints: Long = 0L) extends ScopeApi with MemberScopeApi { + class Scope protected[Scopes]() extends ScopeApi with MemberScopeApi { - protected[Scopes] def this(base: Scope) = { - this(base.elems) - nestinglevel = base.nestinglevel + 1 - } - - private[scala] var elems: ScopeEntry = initElems + private[scala] var elems: ScopeEntry = _ /** The number of times this scope is nested in another */ - private var nestinglevel = 0 + private[Scopes] var nestinglevel = 0 /** the hash table */ - private var hashtable: Array[ScopeEntry] = null + private[Scopes] var hashtable: Array[ScopeEntry] = null /** a cache for all elements, to be used by symbol iterator. */ @@ -84,8 +79,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => */ private val MIN_HASH = 8 - if (size >= MIN_HASH) createHash() - /** Returns a new scope with the same content as this one. */ def cloneScope: Scope = newScopeWith(this.toList: _*) @@ -435,7 +428,14 @@ trait Scopes extends api.Scopes { self: SymbolTable => } /** Create a new scope nested in another one with which it shares its elements */ - def newNestedScope(outer: Scope): Scope = new Scope(outer) + final def newNestedScope(outer: Scope): Scope = { + val nested = newScope // not `new Scope`, we must allow the runtime reflection universe to mixin SynchronizedScopes! + nested.elems = outer.elems + nested.nestinglevel = outer.nestinglevel + 1 + if (outer.hashtable ne null) + nested.hashtable = java.util.Arrays.copyOf(outer.hashtable, outer.hashtable.length) + nested + } /** Create a new scope with given initial elements */ def newScopeWith(elems: Symbol*): Scope = { diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index 4fccad74ac..a2642628a4 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -40,7 +40,7 @@ abstract class SourceFile { def lineToString(index: Int): String = { val start = lineToOffset(index) var end = start - while (!isEndOfLine(end) && end <= length) end += 1 + while (end < length && !isEndOfLine(end)) end += 1 new String(content, start, end - start) } diff --git a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala index c56bc28d90..7ba68b8733 100644 --- a/src/reflect/scala/reflect/runtime/SymbolLoaders.scala +++ b/src/reflect/scala/reflect/runtime/SymbolLoaders.scala @@ -91,7 +91,7 @@ private[reflect] trait SymbolLoaders { self: SymbolTable => // // Short of significantly changing SymbolLoaders I see no other way than just // to slap a global lock on materialization in runtime reflection. - class PackageScope(pkgClass: Symbol) extends Scope(initFingerPrints = -1L) // disable fingerprinting as we do not know entries beforehand + class PackageScope(pkgClass: Symbol) extends Scope with SynchronizedScope { assert(pkgClass.isType) diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index c90901410a..4a8585d616 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -37,8 +37,7 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable // Scopes - override def newScope = new Scope() with SynchronizedScope - override def newNestedScope(outer: Scope): Scope = new Scope(outer) with SynchronizedScope + override def newScope = new Scope with SynchronizedScope trait SynchronizedScope extends Scope { // we can keep this lock fine-grained, because methods of Scope don't do anything extraordinary, which makes deadlocks impossible diff --git a/src/reflect/scala/reflect/runtime/package.scala b/src/reflect/scala/reflect/runtime/package.scala index 3c9bbccba3..e240bed0a7 100644 --- a/src/reflect/scala/reflect/runtime/package.scala +++ b/src/reflect/scala/reflect/runtime/package.scala @@ -30,7 +30,8 @@ package runtime { import c.universe._ val runtimeClass = c.reifyEnclosingRuntimeClass if (runtimeClass.isEmpty) c.abort(c.enclosingPosition, "call site does not have an enclosing class") - val runtimeUniverse = Select(Select(Select(Ident(newTermName("scala")), newTermName("reflect")), newTermName("runtime")), newTermName("universe")) + val scalaPackage = Select(Ident(newTermName("_root_")), newTermName("scala")) + val runtimeUniverse = Select(Select(Select(scalaPackage, newTermName("reflect")), newTermName("runtime")), newTermName("universe")) val currentMirror = Apply(Select(runtimeUniverse, newTermName("runtimeMirror")), List(Select(runtimeClass, newTermName("getClassLoader")))) c.Expr[Nothing](currentMirror)(c.WeakTypeTag.Nothing) } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css index 55fb370a41..3e352a95b3 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css @@ -310,6 +310,7 @@ h1 { position: fixed; margin-left: 300px; display: block; + -webkit-overflow-scrolling: touch; } #content > iframe { diff --git a/test/files/jvm/beanInfo.check b/test/files/jvm/beanInfo.check new file mode 100644 index 0000000000..d74e127711 --- /dev/null +++ b/test/files/jvm/beanInfo.check @@ -0,0 +1,6 @@ +property descriptors +x -- int -- public int p.C.x() -- null +y -- class java.lang.String -- public java.lang.String p.C.y() -- public void p.C.y_$eq(java.lang.String) +z -- class scala.collection.immutable.List -- public scala.collection.immutable.List p.C.z() -- public void p.C.z_$eq(scala.collection.immutable.List) +method descriptors +f -- public p.C p.C.f() diff --git a/test/files/jvm/beanInfo/C_1.scala b/test/files/jvm/beanInfo/C_1.scala new file mode 100644 index 0000000000..a338abea1d --- /dev/null +++ b/test/files/jvm/beanInfo/C_1.scala @@ -0,0 +1,9 @@ +package p + +@scala.beans.BeanInfo +class C { + val x: Int = 0 + var y: String = "" + var z: List[_] = Nil + def f: C = ??? +} diff --git a/test/files/jvm/beanInfo/Test_2.scala b/test/files/jvm/beanInfo/Test_2.scala new file mode 100644 index 0000000000..fa9b6e1391 --- /dev/null +++ b/test/files/jvm/beanInfo/Test_2.scala @@ -0,0 +1,17 @@ +object Test extends App { + val info = java.beans.Introspector.getBeanInfo(classOf[p.C]) + + println("property descriptors") + + val pds = info.getPropertyDescriptors + for (pd <- pds) { + println(s"${pd.getName} -- ${pd.getPropertyType} -- ${pd.getReadMethod} -- ${pd.getWriteMethod}") + } + + println("method descriptors") + + val mds = info.getMethodDescriptors + for (md <- mds) { + println(s"${md.getName} -- ${md.getMethod}") + } +} diff --git a/test/files/jvm/t8582.check b/test/files/jvm/t8582.check new file mode 100644 index 0000000000..564f482ff8 --- /dev/null +++ b/test/files/jvm/t8582.check @@ -0,0 +1,44 @@ +getClass on module gives module class + class p1.p2.Singleton$Singleton$ + +Nested module classes are found through reflection + p1.p2.Singleton$Singleton$: List(class p1.p2.Singleton$Singleton$Singleton$) + +Reflection can find direct nested classes (A1-B1-C1) + A1: List(class A1$B1) + A1$B1: List(class A1$B1$C1) + A1$B1$C1: List() + +Reflection can find direct nested classes (A2-B2-C2) + A2: List(class A2$B2) + A2$B2: List(class A2$B2$C2) + A2$B2$C2: List() + +Mirror classes have the same InnerClass attributes as the corresponding module class: + className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] +Module class + className[p1/p2/Singleton$Singleton$] outerClassName[p1/p2/Singleton] innerName[Singleton$] access[9] + +An outer class has a InnerClass attribute for direct nested classes + className[A1$B1] outerClassName[A1] innerName[B1] access[1] +A nested class has an InnerClass attribute for itself (and also for its nested classes) + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] +C1 is a nested class, so it has an InnerClass attribute for itself. +Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1. + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] + +The BeanInfo class has the same InnerClass attributes as the corresponding bean + className[A1$B1] outerClassName[A1] innerName[B1] access[1] + className[A1$B1$C1] outerClassName[A1$B1] innerName[C1] access[1] + +Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] +B2 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] +C2 + className[A2$B2] outerClassName[A2] innerName[B2] access[1] + className[A2$B2$C2] outerClassName[A2$B2] innerName[C2] access[1] diff --git a/test/files/jvm/t8582.scala b/test/files/jvm/t8582.scala new file mode 100644 index 0000000000..8a57ef7952 --- /dev/null +++ b/test/files/jvm/t8582.scala @@ -0,0 +1,81 @@ +import scala.tools.partest.BytecodeTest +import scala.collection.JavaConverters._ + +package p1 { + package p2 { + object Singleton { + object Singleton { + object Singleton + } + } + } +} + +class A1 { + class B1 { + @scala.beans.BeanInfo + class C1 + } +} + +class A2 { + class B2 { + class C2 + } + def f: B2#C2 = null +} + + +object Test extends BytecodeTest { + import p1.p2._ + + def nested(c: Class[_]) = s" ${c.getName}: ${c.getDeclaredClasses.toList}" + + def nprintln(s: String) = println("\n"+s) + def printInner(cname: String): Unit = { + val cnode = loadClassNode(cname) + println(cnode.innerClasses.asScala.toList.map(i => s"className[${i.name}] outerClassName[${i.outerName}] innerName[${i.innerName}] access[${i.access}]").mkString(" ", "\n ", "")) + } + + def show() { + + println("getClass on module gives module class") + println(" " + Singleton.Singleton.getClass) + + nprintln("Nested module classes are found through reflection") + println(nested(Singleton.Singleton.getClass)) + + nprintln("Reflection can find direct nested classes (A1-B1-C1)") + println(nested(classOf[A1])) + println(nested(classOf[A1#B1])) + println(nested(classOf[A1#B1#C1])) + + nprintln("Reflection can find direct nested classes (A2-B2-C2)") + println(nested(classOf[A2])) + println(nested(classOf[A2#B2])) + println(nested(classOf[A2#B2#C2])) + + nprintln("Mirror classes have the same InnerClass attributes as the corresponding module class:") + printInner("p1.p2.Singleton") // mirror class + println("Module class") + printInner("p1.p2.Singleton$") + + nprintln("An outer class has a InnerClass attribute for direct nested classes") + printInner("A1") + println("A nested class has an InnerClass attribute for itself (and also for its nested classes)") + printInner("A1$B1") + println("C1 is a nested class, so it has an InnerClass attribute for itself.\n"+ + "Because that attribute leads to an entry for B1 in the constant pool, C1 needs an InnerClass attribute for B1.") + printInner("A1$B1$C1") + + nprintln("The BeanInfo class has the same InnerClass attributes as the corresponding bean") + printInner("A1$B1$C1BeanInfo") + + nprintln("Class A2 mentions class C2 in the constant pool (due to method f), therefore it needs an InnerClass attribute for C1") + printInner("A2") + println("B2") + printInner("A2$B2") + println("C2") + printInner("A2$B2$C2") + } +} diff --git a/test/files/neg/double-def-top-level.check b/test/files/neg/double-def-top-level.check new file mode 100644 index 0000000000..85b16e81e5 --- /dev/null +++ b/test/files/neg/double-def-top-level.check @@ -0,0 +1,7 @@ +D_3.scala:1: error: C is already defined as class C +class C + ^ +D_3.scala:2: error: O is already defined as object O +object O + ^ +two errors found diff --git a/test/files/neg/double-def-top-level/A_1.scala b/test/files/neg/double-def-top-level/A_1.scala new file mode 100644 index 0000000000..c3d68d9d05 --- /dev/null +++ b/test/files/neg/double-def-top-level/A_1.scala @@ -0,0 +1,4 @@ +package p + +class C +object O diff --git a/test/files/neg/double-def-top-level/B_2.scala b/test/files/neg/double-def-top-level/B_2.scala new file mode 100644 index 0000000000..c328e8c964 --- /dev/null +++ b/test/files/neg/double-def-top-level/B_2.scala @@ -0,0 +1,2 @@ +class C /* noerror */ +object O /* noerror */
\ No newline at end of file diff --git a/test/files/neg/double-def-top-level/C_3.scala b/test/files/neg/double-def-top-level/C_3.scala new file mode 100644 index 0000000000..e1c327c15a --- /dev/null +++ b/test/files/neg/double-def-top-level/C_3.scala @@ -0,0 +1,2 @@ +class C +object O
\ No newline at end of file diff --git a/test/files/neg/double-def-top-level/D_3.scala b/test/files/neg/double-def-top-level/D_3.scala new file mode 100644 index 0000000000..518e0d1c54 --- /dev/null +++ b/test/files/neg/double-def-top-level/D_3.scala @@ -0,0 +1,2 @@ +class C +object O diff --git a/test/files/neg/t6162-inheritance.check b/test/files/neg/t6162-inheritance.check index 13c78030d9..c9f4ddaec1 100644 --- a/test/files/neg/t6162-inheritance.check +++ b/test/files/neg/t6162-inheritance.check @@ -7,12 +7,6 @@ object SubT extends T usage.scala:8: warning: inheritance from trait S in package t6126 is deprecated new S { ^ -usage.scala:3: warning: inheritance from class Foo in package t6126 is deprecated: `Foo` will be made final in a future version. -class SubFoo extends Foo - ^ -usage.scala:5: warning: inheritance from trait T in package t6126 is deprecated -object SubT extends T - ^ error: No warnings can be incurred under -Xfatal-warnings. -5 warnings found +three warnings found one error found diff --git a/test/files/neg/t8630.check b/test/files/neg/t8630.check new file mode 100644 index 0000000000..98b084b153 --- /dev/null +++ b/test/files/neg/t8630.check @@ -0,0 +1,7 @@ +t8630.scala:1: error: '{' expected but 'abstract' found. +package bobsdelights abstract class Fruit( val name: String, val color: String ) object Fruits { object Apple extends Fruit("apple", "red") object Orange extends Fruit("orange", "orange") object Pear extends Fruit("pear", "yellowish") val menu = List(Apple, Orange, Pear) } + ^ +t8630.scala:1: error: '}' expected but eof found. +package bobsdelights abstract class Fruit( val name: String, val color: String ) object Fruits { object Apple extends Fruit("apple", "red") object Orange extends Fruit("orange", "orange") object Pear extends Fruit("pear", "yellowish") val menu = List(Apple, Orange, Pear) } + ^ +two errors found diff --git a/test/files/neg/t8630.scala b/test/files/neg/t8630.scala new file mode 100644 index 0000000000..ea25227452 --- /dev/null +++ b/test/files/neg/t8630.scala @@ -0,0 +1 @@ +package bobsdelights abstract class Fruit( val name: String, val color: String ) object Fruits { object Apple extends Fruit("apple", "red") object Orange extends Fruit("orange", "orange") object Pear extends Fruit("pear", "yellowish") val menu = List(Apple, Orange, Pear) }
\ No newline at end of file diff --git a/test/files/pos/t8578.flags b/test/files/pos/t8578.flags new file mode 100644 index 0000000000..48b438ddf8 --- /dev/null +++ b/test/files/pos/t8578.flags @@ -0,0 +1 @@ +-Ydelambdafy:method diff --git a/test/files/pos/t8578.scala b/test/files/pos/t8578.scala new file mode 100644 index 0000000000..879b5f5550 --- /dev/null +++ b/test/files/pos/t8578.scala @@ -0,0 +1,18 @@ +class DuplicateClassName { + () => { + {() => ()} + {() => ()} + {() => ()} + {() => ()} + {() => ()} + {() => ()} + {() => ()} + {() => ()} + {() => ()} + {() => () => ()} + {() => ()} + } +} +// Was: +// Different class symbols have the same bytecode-level internal name: +// name: DuplicateClassName$lambda$$$anonfun$111
\ No newline at end of file diff --git a/test/files/pos/t8617.flags b/test/files/pos/t8617.flags new file mode 100644 index 0000000000..281f0a10cd --- /dev/null +++ b/test/files/pos/t8617.flags @@ -0,0 +1 @@ +-Yrangepos diff --git a/test/files/pos/t8617.scala b/test/files/pos/t8617.scala new file mode 100644 index 0000000000..fc825bbcba --- /dev/null +++ b/test/files/pos/t8617.scala @@ -0,0 +1,10 @@ +object Test { + def foo[A] = implicitly[OptManifest[A]] // was "unpositioned tree" under -Yrangepos + + // These did not crash, but testing for good measure. + implicitly[OptManifest[String]] + implicitly[Manifest[String]] + + implicitly[reflect.ClassTag[String]] + implicitly[reflect.runtime.universe.TypeTag[String]] +} diff --git a/test/files/pos/t8625.scala b/test/files/pos/t8625.scala new file mode 100644 index 0000000000..95c4b0dbcd --- /dev/null +++ b/test/files/pos/t8625.scala @@ -0,0 +1,5 @@ +object Test { + def f1(a: Boolean, b: Boolean) = (a || ???) && (b || ???) + def f2(a: Boolean, b: Boolean) = (a || ???) && b + def f3(a: Boolean, b: Boolean) = (a && ???) || b +} diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check index e3ab554d4c..9803465ddc 100644 --- a/test/files/run/analyzerPlugins.check +++ b/test/files/run/analyzerPlugins.check @@ -19,7 +19,7 @@ canAdaptAnnotations(Trees$Typed, Any) [1] canAdaptAnnotations(Trees$Typed, Int) [1] lub(List(Int @testAnn, Int)) [1] pluginsPt(?, Trees$Annotated) [7] -pluginsPt(?, Trees$Apply) [9] +pluginsPt(?, Trees$Apply) [8] pluginsPt(?, Trees$ApplyImplicitView) [2] pluginsPt(?, Trees$Assign) [7] pluginsPt(?, Trees$Block) [4] @@ -31,13 +31,13 @@ pluginsPt(?, Trees$Literal) [16] pluginsPt(?, Trees$New) [5] pluginsPt(?, Trees$PackageDef) [1] pluginsPt(?, Trees$Return) [1] -pluginsPt(?, Trees$Select) [48] +pluginsPt(?, Trees$Select) [47] pluginsPt(?, Trees$Super) [2] pluginsPt(?, Trees$This) [20] -pluginsPt(?, Trees$TypeApply) [4] +pluginsPt(?, Trees$TypeApply) [3] pluginsPt(?, Trees$TypeBoundsTree) [2] pluginsPt(?, Trees$TypeDef) [1] -pluginsPt(?, Trees$TypeTree) [39] +pluginsPt(?, Trees$TypeTree) [38] pluginsPt(?, Trees$Typed) [1] pluginsPt(?, Trees$ValDef) [21] pluginsPt(Any, Trees$Literal) [2] @@ -98,7 +98,6 @@ pluginsTyped(()String, Trees$Ident) [1] pluginsTyped(()String, Trees$TypeApply) [1] pluginsTyped(()scala.annotation.Annotation, Trees$Select) [1] pluginsTyped(()testAnn, Trees$Select) [10] -pluginsTyped(()type, Trees$TypeApply) [1] pluginsTyped((str: String)A <and> (param: Double)A, Trees$Select) [1] pluginsTyped((x$1: Any)Boolean <and> (x: Double)Boolean <and> (x: Float)Boolean <and> (x: Long)Boolean <and> (x: Int)Boolean <and> (x: Char)Boolean <and> (x: Short)Boolean <and> (x: Byte)Boolean, Trees$Select) [1] pluginsTyped((x$1: Int)Unit, Trees$Select) [1] @@ -173,7 +172,7 @@ pluginsTyped(Unit, Trees$Literal) [5] pluginsTyped(Unit, Trees$TypeTree) [1] pluginsTyped([A](xs: A*)List[A], Trees$Select) [1] pluginsTyped([T <: Int]=> Int, Trees$Select) [1] -pluginsTyped([T0]()T0, Trees$Select) [2] +pluginsTyped([T0]()T0, Trees$Select) [1] pluginsTyped([T](xs: Array[T])scala.collection.mutable.WrappedArray[T], Trees$Select) [1] pluginsTyped(annotation.type, Trees$Select) [4] pluginsTyped(math.type, Trees$Select) [9] @@ -190,7 +189,5 @@ pluginsTyped(testAnn, Trees$New) [5] pluginsTyped(testAnn, Trees$This) [1] pluginsTyped(testAnn, Trees$TypeTree) [2] pluginsTyped(testAnn.super.type, Trees$Super) [1] -pluginsTyped(type, Trees$Apply) [1] pluginsTyped(type, Trees$Select) [1] -pluginsTyped(type, Trees$TypeTree) [1] pluginsTypedReturn(return f, String) [1] diff --git a/test/files/run/classfile-format-51.scala b/test/files/run/classfile-format-51.scala index f92382d89b..24b1ee8397 100644 --- a/test/files/run/classfile-format-51.scala +++ b/test/files/run/classfile-format-51.scala @@ -32,7 +32,7 @@ object Test extends DirectTest { val constructor = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null) constructor.visitCode() constructor.visitVarInsn(ALOAD, 0) - constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V") + constructor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false) constructor.visitInsn(RETURN) constructor.visitMaxs(1, 1) constructor.visitEnd() @@ -47,19 +47,19 @@ object Test extends DirectTest { val bootstrap = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, bootstrapMethodName, bootStrapMethodType, null, null) bootstrap.visitCode() // val lookup = MethodHandles.lookup(); - bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;") + bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false) bootstrap.visitVarInsn(ASTORE, 3) // lookup // val clazz = lookup.lookupClass(); bootstrap.visitVarInsn(ALOAD, 3) // lookup - bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;") + bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;", false) bootstrap.visitVarInsn(ASTORE, 4) // clazz // val methodType = MethodType.fromMethodDescriptorString("()Ljava/lang/String, clazz.getClassLoader()") bootstrap.visitLdcInsn("()Ljava/lang/String;") bootstrap.visitVarInsn(ALOAD, 4) // CLAZZ - bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;") - bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;") + bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) + bootstrap.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodType", "fromMethodDescriptorString", "(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;", false) bootstrap.visitVarInsn(ASTORE, 5) // methodType // val methodHandle = lookup.findStatic(thisClass, "target", methodType) @@ -67,14 +67,14 @@ object Test extends DirectTest { bootstrap.visitVarInsn(ALOAD, 4) // clazz bootstrap.visitLdcInsn("target") bootstrap.visitVarInsn(ALOAD, 5) // methodType - bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;") + bootstrap.visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandles$Lookup", "findStatic", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", false) bootstrap.visitVarInsn(ASTORE, 6) // methodHandle // new ConstantCallSite(methodHandle) bootstrap.visitTypeInsn(NEW, "java/lang/invoke/ConstantCallSite") bootstrap.visitInsn(DUP) bootstrap.visitVarInsn(ALOAD, 6) // methodHandle - bootstrap.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V") + bootstrap.visitMethodInsn(INVOKESPECIAL, "java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false) bootstrap.visitInsn(ARETURN) bootstrap.visitMaxs(4,7) bootstrap.visitEnd() diff --git a/test/files/run/large_class.check b/test/files/run/large_class.check new file mode 100644 index 0000000000..0585c267ac --- /dev/null +++ b/test/files/run/large_class.check @@ -0,0 +1,3 @@ +newSource1.scala:1: error: Could not write class BigEnoughToFail because it exceeds JVM code size limits. Class file too large! +class BigEnoughToFail { + ^ diff --git a/test/files/run/large_class.scala b/test/files/run/large_class.scala new file mode 100644 index 0000000000..aa486ef8f7 --- /dev/null +++ b/test/files/run/large_class.scala @@ -0,0 +1,27 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +// a cold run of partest takes about 15s for this test on my laptop +object Test extends DirectTest { + override def extraSettings: String = "-usejavacp -d " + testOutput.path + + def s(n: Int) = "\""+n+"\"" + + override def code + = s""" + |class BigEnoughToFail { + | def m(a: String, b: String, c: String, d: String, e: String, f: String) = null + | ${(1 to 5500) map (n => "def f"+n+" = m("+ s(n+10000)+","+ + s(n+20000)+","+ + s(n+30000)+","+ + s(n+40000)+","+ + s(n+50000)+","+ + s(n+60000)+")") mkString ";"} + |}""".stripMargin.trim + + override def show(): Unit = { + Console.withErr(System.out) { + compile() + } + } +} diff --git a/test/files/run/t8346.check b/test/files/run/t8346.check new file mode 100644 index 0000000000..1ba5c31abe --- /dev/null +++ b/test/files/run/t8346.check @@ -0,0 +1,6 @@ +BitSet: List(invariant, invariant, invariant, invariant) +HashSet: List(covariant (true), covariant (true), covariant (true), covariant (true)) +ListSet: List(covariant (true), covariant (true), covariant (true), covariant (true)) +SortedSet: List(invariant, invariant, invariant, invariant) +TreeSet: List(invariant, invariant, invariant, invariant) +ValueSet: invariant diff --git a/test/files/run/t8346.scala b/test/files/run/t8346.scala new file mode 100644 index 0000000000..5f3df84174 --- /dev/null +++ b/test/files/run/t8346.scala @@ -0,0 +1,34 @@ +object Test extends App { + import reflect.ClassTag + + object SomeEnum extends Enumeration { + val one, two, three, four = Value + } + + def sctor[A <: Set[Int]](f: Int => A)(implicit A: ClassTag[A]) + : (String, Int => Set[Int]) = + (A.runtimeClass.getSimpleName, f) + + val inits: Seq[(String, Int => Set[Int])] = { + import collection.immutable.{Seq => _, _} + Seq(sctor(BitSet(_)), + sctor(HashSet(_)), + sctor(ListSet(_)), + sctor(SortedSet(_)), + sctor(TreeSet(_))) + } + + def sVarInfo[A](sa: Set[A]): String = { + val saa = sa.toSet[Any] + if (sa eq saa) s"""covariant (${(saa + "hi") contains "hi"})""" + else "invariant" + } + + inits foreach {case (name, singleton) => + print(s"${name}: ") + val one = singleton(1) + println(Seq(2,3,4).scanLeft(one)(_ + _) map sVarInfo toList) + } + + println(s"ValueSet: ${sVarInfo(SomeEnum.values)}") +} diff --git a/test/files/run/t8549.scala b/test/files/run/t8549.scala index 1b402d2d22..d3355208fa 100644 --- a/test/files/run/t8549.scala +++ b/test/files/run/t8549.scala @@ -85,13 +85,9 @@ object Test extends App { check(List(1, 2, 3))( "rO0ABXNyADJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0JFNlcmlhbGl6YXRpb25Qcm94eQAAAAAAAAABAwAAeHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJzcQB+AAIAAAADc3IALHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3RTZXJpYWxpemVFbmQkilxjW/dTC20CAAB4cHg=") check(Nil)( "rO0ABXNyADJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0JFNlcmlhbGl6YXRpb25Qcm94eQAAAAAAAAABAwAAeHBzcgAsc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuTGlzdFNlcmlhbGl6ZUVuZCSKXGNb91MLbQIAAHhweA==") - check(Vector(1))( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5WZWN0b3Lkd3dcHq6PXAIAC0kABWRlcHRoWgAFZGlydHlJAAhlbmRJbmRleEkABWZvY3VzSQAKc3RhcnRJbmRleFsACGRpc3BsYXkwdAATW0xqYXZhL2xhbmcvT2JqZWN0O1sACGRpc3BsYXkxcQB+AAFbAAhkaXNwbGF5MnEAfgABWwAIZGlzcGxheTNxAH4AAVsACGRpc3BsYXk0cQB+AAFbAAhkaXNwbGF5NXEAfgABeHAAAAABAAAAAAEAAAAAAAAAAHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAACBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcA==") - check(Vector())( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5WZWN0b3Lkd3dcHq6PXAIAC0kABWRlcHRoWgAFZGlydHlJAAhlbmRJbmRleEkABWZvY3VzSQAKc3RhcnRJbmRleFsACGRpc3BsYXkwdAATW0xqYXZhL2xhbmcvT2JqZWN0O1sACGRpc3BsYXkxcQB+AAFbAAhkaXNwbGF5MnEAfgABWwAIZGlzcGxheTNxAH4AAVsACGRpc3BsYXk0cQB+AAFbAAhkaXNwbGF5NXEAfgABeHAAAAAAAAAAAAAAAAAAAAAAAHBwcHBwcA==") - - object Enum extends Enumeration { - val V1 = new Value { def id = 42 } - val V2 = new Val(42) - } + // TODO SI-8576 unstable under -Xcheckinit + // check(Vector(1))( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5WZWN0b3Lkd3dcHq6PXAIAC0kABWRlcHRoWgAFZGlydHlJAAhlbmRJbmRleEkABWZvY3VzSQAKc3RhcnRJbmRleFsACGRpc3BsYXkwdAATW0xqYXZhL2xhbmcvT2JqZWN0O1sACGRpc3BsYXkxcQB+AAFbAAhkaXNwbGF5MnEAfgABWwAIZGlzcGxheTNxAH4AAVsACGRpc3BsYXk0cQB+AAFbAAhkaXNwbGF5NXEAfgABeHAAAAABAAAAAAEAAAAAAAAAAHVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAACBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcA==") + // check(Vector())( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5WZWN0b3Lkd3dcHq6PXAIAC0kABWRlcHRoWgAFZGlydHlJAAhlbmRJbmRleEkABWZvY3VzSQAKc3RhcnRJbmRleFsACGRpc3BsYXkwdAATW0xqYXZhL2xhbmcvT2JqZWN0O1sACGRpc3BsYXkxcQB+AAFbAAhkaXNwbGF5MnEAfgABWwAIZGlzcGxheTNxAH4AAVsACGRpc3BsYXk0cQB+AAFbAAhkaXNwbGF5NXEAfgABeHAAAAAAAAAAAAAAAAAAAAAAAHBwcHBwcA==") import collection.{ mutable, immutable } @@ -101,16 +97,20 @@ object Test extends App { check(reflect.classTag[String])("rO0ABXNyAB5zY2FsYS5yZWZsZWN0LkNsYXNzVGFnJCRhbm9uJDG7ePPrmQBkhgIAAUwAD3J1bnRpbWVDbGFzczEkMXQAEUxqYXZhL2xhbmcvQ2xhc3M7eHB2cgAQamF2YS5sYW5nLlN0cmluZ6DwpDh6O7NCAgAAeHA=") check(reflect.classTag[Object])("rO0ABXNyACVzY2FsYS5yZWZsZWN0Lk1hbmlmZXN0RmFjdG9yeSQkYW5vbiQymPrtq/Ci1gsCAAB4cgAtc2NhbGEucmVmbGVjdC5NYW5pZmVzdEZhY3RvcnkkUGhhbnRvbU1hbmlmZXN0rzigP7KRh/kCAAFMAAh0b1N0cmluZ3QAEkxqYXZhL2xhbmcvU3RyaW5nO3hyAC9zY2FsYS5yZWZsZWN0Lk1hbmlmZXN0RmFjdG9yeSRDbGFzc1R5cGVNYW5pZmVzdFq6NWvfTgYFAgADTAAGcHJlZml4dAAOTHNjYWxhL09wdGlvbjtMAAxydW50aW1lQ2xhc3N0ABFMamF2YS9sYW5nL0NsYXNzO0wADXR5cGVBcmd1bWVudHN0ACFMc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvTGlzdDt4cHNyAAtzY2FsYS5Ob25lJEZQJPZTypSsAgAAeHIADHNjYWxhLk9wdGlvbv5pN/3bDmZ0AgAAeHB2cgAQamF2YS5sYW5nLk9iamVjdAAAAAAAAAAAAAAAeHBzcgAyc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuTGlzdCRTZXJpYWxpemF0aW9uUHJveHkAAAAAAAAAAQMAAHhwc3IALHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3RTZXJpYWxpemVFbmQkilxjW/dTC20CAAB4cHh0AAZPYmplY3Q=") - check(Enum)( "rO0ABXNyAApUZXN0JEVudW0ketCIyQ8C23MCAAJMAAJWMXQAGUxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZTtMAAJWMnQAF0xzY2FsYS9FbnVtZXJhdGlvbiRWYWw7eHIAEXNjYWxhLkVudW1lcmF0aW9udaDN3ZgOWY4CAAhJAAZuZXh0SWRJABtzY2FsYSRFbnVtZXJhdGlvbiQkYm90dG9tSWRJABhzY2FsYSRFbnVtZXJhdGlvbiQkdG9wSWRMABRWYWx1ZU9yZGVyaW5nJG1vZHVsZXQAIkxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZU9yZGVyaW5nJDtMAA9WYWx1ZVNldCRtb2R1bGV0AB1Mc2NhbGEvRW51bWVyYXRpb24kVmFsdWVTZXQkO0wACG5leHROYW1ldAAbTHNjYWxhL2NvbGxlY3Rpb24vSXRlcmF0b3I7TAAXc2NhbGEkRW51bWVyYXRpb24kJG5tYXB0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL01hcDtMABdzY2FsYSRFbnVtZXJhdGlvbiQkdm1hcHEAfgAHeHAAAAArAAAAAAAAACtwcHBzcgAgc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkhhc2hNYXAAAAAAAAAAAQMAAHhwdw0AAALuAAAAAAAAAAQAeHNxAH4ACXcNAAAC7gAAAAEAAAAEAHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAqc3IAFXNjYWxhLkVudW1lcmF0aW9uJFZhbM9pZ6/J/O1PAgACSQAYc2NhbGEkRW51bWVyYXRpb24kVmFsJCRpTAAEbmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO3hyABdzY2FsYS5FbnVtZXJhdGlvbiRWYWx1ZWJpfC/tIR1RAgACTAAGJG91dGVydAATTHNjYWxhL0VudW1lcmF0aW9uO0wAHHNjYWxhJEVudW1lcmF0aW9uJCRvdXRlckVudW1xAH4AEnhwcQB+AAhxAH4ACAAAACpweHNyABFUZXN0JEVudW0kJGFub24kMVlIjlmE1sXaAgAAeHEAfgARcQB+AAhxAH4ACHEAfgAT") - check(Enum.V1)( "rO0ABXNyABFUZXN0JEVudW0kJGFub24kMVlIjlmE1sXaAgAAeHIAF3NjYWxhLkVudW1lcmF0aW9uJFZhbHVlYml8L+0hHVECAAJMAAYkb3V0ZXJ0ABNMc2NhbGEvRW51bWVyYXRpb247TAAcc2NhbGEkRW51bWVyYXRpb24kJG91dGVyRW51bXEAfgACeHBzcgAKVGVzdCRFbnVtJHrQiMkPAttzAgACTAACVjF0ABlMc2NhbGEvRW51bWVyYXRpb24kVmFsdWU7TAACVjJ0ABdMc2NhbGEvRW51bWVyYXRpb24kVmFsO3hyABFzY2FsYS5FbnVtZXJhdGlvbnWgzd2YDlmOAgAISQAGbmV4dElkSQAbc2NhbGEkRW51bWVyYXRpb24kJGJvdHRvbUlkSQAYc2NhbGEkRW51bWVyYXRpb24kJHRvcElkTAAUVmFsdWVPcmRlcmluZyRtb2R1bGV0ACJMc2NhbGEvRW51bWVyYXRpb24kVmFsdWVPcmRlcmluZyQ7TAAPVmFsdWVTZXQkbW9kdWxldAAdTHNjYWxhL0VudW1lcmF0aW9uJFZhbHVlU2V0JDtMAAhuZXh0TmFtZXQAG0xzY2FsYS9jb2xsZWN0aW9uL0l0ZXJhdG9yO0wAF3NjYWxhJEVudW1lcmF0aW9uJCRubWFwdAAeTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9NYXA7TAAXc2NhbGEkRW51bWVyYXRpb24kJHZtYXBxAH4AC3hwAAAAKwAAAAAAAAArcHBwc3IAIHNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5IYXNoTWFwAAAAAAAAAAEDAAB4cHcNAAAC7gAAAAAAAAAEAHhzcQB+AA13DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAKnNyABVzY2FsYS5FbnVtZXJhdGlvbiRWYWzPaWevyfztTwIAAkkAGHNjYWxhJEVudW1lcmF0aW9uJFZhbCQkaUwABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cQB+AAFxAH4ADHEAfgAMAAAAKnB4cQB+AANxAH4AFXEAfgAM") - check(Enum.V2)( "rO0ABXNyABVzY2FsYS5FbnVtZXJhdGlvbiRWYWzPaWevyfztTwIAAkkAGHNjYWxhJEVudW1lcmF0aW9uJFZhbCQkaUwABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cgAXc2NhbGEuRW51bWVyYXRpb24kVmFsdWViaXwv7SEdUQIAAkwABiRvdXRlcnQAE0xzY2FsYS9FbnVtZXJhdGlvbjtMABxzY2FsYSRFbnVtZXJhdGlvbiQkb3V0ZXJFbnVtcQB+AAN4cHNyAApUZXN0JEVudW0ketCIyQ8C23MCAAJMAAJWMXQAGUxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZTtMAAJWMnQAF0xzY2FsYS9FbnVtZXJhdGlvbiRWYWw7eHIAEXNjYWxhLkVudW1lcmF0aW9udaDN3ZgOWY4CAAhJAAZuZXh0SWRJABtzY2FsYSRFbnVtZXJhdGlvbiQkYm90dG9tSWRJABhzY2FsYSRFbnVtZXJhdGlvbiQkdG9wSWRMABRWYWx1ZU9yZGVyaW5nJG1vZHVsZXQAIkxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZU9yZGVyaW5nJDtMAA9WYWx1ZVNldCRtb2R1bGV0AB1Mc2NhbGEvRW51bWVyYXRpb24kVmFsdWVTZXQkO0wACG5leHROYW1ldAAbTHNjYWxhL2NvbGxlY3Rpb24vSXRlcmF0b3I7TAAXc2NhbGEkRW51bWVyYXRpb24kJG5tYXB0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL01hcDtMABdzY2FsYSRFbnVtZXJhdGlvbiQkdm1hcHEAfgAMeHAAAAArAAAAAAAAACtwcHBzcgAgc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkhhc2hNYXAAAAAAAAAAAQMAAHhwdw0AAALuAAAAAAAAAAQAeHNxAH4ADncNAAAC7gAAAAEAAAAEAHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAqcQB+AAR4c3IAEVRlc3QkRW51bSQkYW5vbiQxWUiOWYTWxdoCAAB4cQB+AAJxAH4ADXEAfgANcQB+AARxAH4ADQAAACpw") + // TODO SI-8576 unstable under -Xcheckinit + // check(Enum)( "rO0ABXNyAApUZXN0JEVudW0ketCIyQ8C23MCAAJMAAJWMXQAGUxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZTtMAAJWMnQAF0xzY2FsYS9FbnVtZXJhdGlvbiRWYWw7eHIAEXNjYWxhLkVudW1lcmF0aW9udaDN3ZgOWY4CAAhJAAZuZXh0SWRJABtzY2FsYSRFbnVtZXJhdGlvbiQkYm90dG9tSWRJABhzY2FsYSRFbnVtZXJhdGlvbiQkdG9wSWRMABRWYWx1ZU9yZGVyaW5nJG1vZHVsZXQAIkxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZU9yZGVyaW5nJDtMAA9WYWx1ZVNldCRtb2R1bGV0AB1Mc2NhbGEvRW51bWVyYXRpb24kVmFsdWVTZXQkO0wACG5leHROYW1ldAAbTHNjYWxhL2NvbGxlY3Rpb24vSXRlcmF0b3I7TAAXc2NhbGEkRW51bWVyYXRpb24kJG5tYXB0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL01hcDtMABdzY2FsYSRFbnVtZXJhdGlvbiQkdm1hcHEAfgAHeHAAAAArAAAAAAAAACtwcHBzcgAgc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkhhc2hNYXAAAAAAAAAAAQMAAHhwdw0AAALuAAAAAAAAAAQAeHNxAH4ACXcNAAAC7gAAAAEAAAAEAHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAqc3IAFXNjYWxhLkVudW1lcmF0aW9uJFZhbM9pZ6/J/O1PAgACSQAYc2NhbGEkRW51bWVyYXRpb24kVmFsJCRpTAAEbmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO3hyABdzY2FsYS5FbnVtZXJhdGlvbiRWYWx1ZWJpfC/tIR1RAgACTAAGJG91dGVydAATTHNjYWxhL0VudW1lcmF0aW9uO0wAHHNjYWxhJEVudW1lcmF0aW9uJCRvdXRlckVudW1xAH4AEnhwcQB+AAhxAH4ACAAAACpweHNyABFUZXN0JEVudW0kJGFub24kMVlIjlmE1sXaAgAAeHEAfgARcQB+AAhxAH4ACHEAfgAT") + // check(Enum.V1)( "rO0ABXNyABFUZXN0JEVudW0kJGFub24kMVlIjlmE1sXaAgAAeHIAF3NjYWxhLkVudW1lcmF0aW9uJFZhbHVlYml8L+0hHVECAAJMAAYkb3V0ZXJ0ABNMc2NhbGEvRW51bWVyYXRpb247TAAcc2NhbGEkRW51bWVyYXRpb24kJG91dGVyRW51bXEAfgACeHBzcgAKVGVzdCRFbnVtJHrQiMkPAttzAgACTAACVjF0ABlMc2NhbGEvRW51bWVyYXRpb24kVmFsdWU7TAACVjJ0ABdMc2NhbGEvRW51bWVyYXRpb24kVmFsO3hyABFzY2FsYS5FbnVtZXJhdGlvbnWgzd2YDlmOAgAISQAGbmV4dElkSQAbc2NhbGEkRW51bWVyYXRpb24kJGJvdHRvbUlkSQAYc2NhbGEkRW51bWVyYXRpb24kJHRvcElkTAAUVmFsdWVPcmRlcmluZyRtb2R1bGV0ACJMc2NhbGEvRW51bWVyYXRpb24kVmFsdWVPcmRlcmluZyQ7TAAPVmFsdWVTZXQkbW9kdWxldAAdTHNjYWxhL0VudW1lcmF0aW9uJFZhbHVlU2V0JDtMAAhuZXh0TmFtZXQAG0xzY2FsYS9jb2xsZWN0aW9uL0l0ZXJhdG9yO0wAF3NjYWxhJEVudW1lcmF0aW9uJCRubWFwdAAeTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9NYXA7TAAXc2NhbGEkRW51bWVyYXRpb24kJHZtYXBxAH4AC3hwAAAAKwAAAAAAAAArcHBwc3IAIHNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5IYXNoTWFwAAAAAAAAAAEDAAB4cHcNAAAC7gAAAAAAAAAEAHhzcQB+AA13DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAKnNyABVzY2FsYS5FbnVtZXJhdGlvbiRWYWzPaWevyfztTwIAAkkAGHNjYWxhJEVudW1lcmF0aW9uJFZhbCQkaUwABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cQB+AAFxAH4ADHEAfgAMAAAAKnB4cQB+AANxAH4AFXEAfgAM") + // check(Enum.V2)( "rO0ABXNyABVzY2FsYS5FbnVtZXJhdGlvbiRWYWzPaWevyfztTwIAAkkAGHNjYWxhJEVudW1lcmF0aW9uJFZhbCQkaUwABG5hbWV0ABJMamF2YS9sYW5nL1N0cmluZzt4cgAXc2NhbGEuRW51bWVyYXRpb24kVmFsdWViaXwv7SEdUQIAAkwABiRvdXRlcnQAE0xzY2FsYS9FbnVtZXJhdGlvbjtMABxzY2FsYSRFbnVtZXJhdGlvbiQkb3V0ZXJFbnVtcQB+AAN4cHNyAApUZXN0JEVudW0ketCIyQ8C23MCAAJMAAJWMXQAGUxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZTtMAAJWMnQAF0xzY2FsYS9FbnVtZXJhdGlvbiRWYWw7eHIAEXNjYWxhLkVudW1lcmF0aW9udaDN3ZgOWY4CAAhJAAZuZXh0SWRJABtzY2FsYSRFbnVtZXJhdGlvbiQkYm90dG9tSWRJABhzY2FsYSRFbnVtZXJhdGlvbiQkdG9wSWRMABRWYWx1ZU9yZGVyaW5nJG1vZHVsZXQAIkxzY2FsYS9FbnVtZXJhdGlvbiRWYWx1ZU9yZGVyaW5nJDtMAA9WYWx1ZVNldCRtb2R1bGV0AB1Mc2NhbGEvRW51bWVyYXRpb24kVmFsdWVTZXQkO0wACG5leHROYW1ldAAbTHNjYWxhL2NvbGxlY3Rpb24vSXRlcmF0b3I7TAAXc2NhbGEkRW51bWVyYXRpb24kJG5tYXB0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL01hcDtMABdzY2FsYSRFbnVtZXJhdGlvbiQkdm1hcHEAfgAMeHAAAAArAAAAAAAAACtwcHBzcgAgc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkhhc2hNYXAAAAAAAAAAAQMAAHhwdw0AAALuAAAAAAAAAAQAeHNxAH4ADncNAAAC7gAAAAEAAAAEAHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAqcQB+AAR4c3IAEVRlc3QkRW51bSQkYW5vbiQxWUiOWYTWxdoCAAB4cQB+AAJxAH4ADXEAfgANcQB+AARxAH4ADQAAACpw") // IndexedSeqLike#Elements - check(new immutable.Range(0, 1, 1).iterator)("rO0ABXNyAChzY2FsYS5jb2xsZWN0aW9uLkluZGV4ZWRTZXFMaWtlJEVsZW1lbnRzGF+1cBwmcx0CAANJAANlbmRJAAVpbmRleEwABiRvdXRlcnQAIUxzY2FsYS9jb2xsZWN0aW9uL0luZGV4ZWRTZXFMaWtlO3hwAAAAAQAAAABzcgAgc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuUmFuZ2Vpu6NUqxUyDQIAB0kAA2VuZFoAB2lzRW1wdHlJAAtsYXN0RWxlbWVudEkAEG51bVJhbmdlRWxlbWVudHNJAAVzdGFydEkABHN0ZXBJAA90ZXJtaW5hbEVsZW1lbnR4cAAAAAEAAAAAAAAAAAEAAAAAAAAAAQAAAAE=" - , _.toList) + // TODO SI-8576 throws scala.UnitializedFieldError under -Xcheckinit + // check(new immutable.Range(0, 1, 1).iterator)("rO0ABXNyAChzY2FsYS5jb2xsZWN0aW9uLkluZGV4ZWRTZXFMaWtlJEVsZW1lbnRzGF+1cBwmcx0CAANJAANlbmRJAAVpbmRleEwABiRvdXRlcnQAIUxzY2FsYS9jb2xsZWN0aW9uL0luZGV4ZWRTZXFMaWtlO3hwAAAAAQAAAABzcgAgc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuUmFuZ2Vpu6NUqxUyDQIAB0kAA2VuZFoAB2lzRW1wdHlJAAtsYXN0RWxlbWVudEkAEG51bVJhbmdlRWxlbWVudHNJAAVzdGFydEkABHN0ZXBJAA90ZXJtaW5hbEVsZW1lbnR4cAAAAAEAAAAAAAAAAAEAAAAAAAAAAQAAAAE=" + // , _.toList) - check(new collection.concurrent.TrieMap[Any, Any]())( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLmNvbmN1cnJlbnQuVHJpZU1hcKckxpgOIYHPAwAETAALZXF1YWxpdHlvYmp0ABJMc2NhbGEvbWF0aC9FcXVpdjtMAApoYXNoaW5nb2JqdAAcTHNjYWxhL3V0aWwvaGFzaGluZy9IYXNoaW5nO0wABHJvb3R0ABJMamF2YS9sYW5nL09iamVjdDtMAAtyb290dXBkYXRlcnQAOUxqYXZhL3V0aWwvY29uY3VycmVudC9hdG9taWMvQXRvbWljUmVmZXJlbmNlRmllbGRVcGRhdGVyO3hwc3IAMnNjYWxhLmNvbGxlY3Rpb24uY29uY3VycmVudC5UcmllTWFwJE1hbmdsZWRIYXNoaW5nhTBoJQ/mgb0CAAB4cHNyABhzY2FsYS5tYXRoLkVxdWl2JCRhbm9uJDLBbyx4dy/qGwIAAHhwc3IANHNjYWxhLmNvbGxlY3Rpb24uY29uY3VycmVudC5UcmllTWFwU2VyaWFsaXphdGlvbkVuZCSbjdgbbGCt2gIAAHhweA==") + // check(new collection.concurrent.TrieMap[Any, Any]())( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLmNvbmN1cnJlbnQuVHJpZU1hcKckxpgOIYHPAwAETAALZXF1YWxpdHlvYmp0ABJMc2NhbGEvbWF0aC9FcXVpdjtMAApoYXNoaW5nb2JqdAAcTHNjYWxhL3V0aWwvaGFzaGluZy9IYXNoaW5nO0wABHJvb3R0ABJMamF2YS9sYW5nL09iamVjdDtMAAtyb290dXBkYXRlcnQAOUxqYXZhL3V0aWwvY29uY3VycmVudC9hdG9taWMvQXRvbWljUmVmZXJlbmNlRmllbGRVcGRhdGVyO3hwc3IAMnNjYWxhLmNvbGxlY3Rpb24uY29uY3VycmVudC5UcmllTWFwJE1hbmdsZWRIYXNoaW5nhTBoJQ/mgb0CAAB4cHNyABhzY2FsYS5tYXRoLkVxdWl2JCRhbm9uJDLBbyx4dy/qGwIAAHhwc3IANHNjYWxhLmNvbGxlY3Rpb24uY29uY3VycmVudC5UcmllTWFwU2VyaWFsaXphdGlvbkVuZCSbjdgbbGCt2gIAAHhweA==") // not sure why this one needs stable serialization. + + // TODO SI-8576 unstable under -Xcheckinit check(collection.convert.Wrappers)( "rO0ABXNyACJzY2FsYS5jb2xsZWN0aW9uLmNvbnZlcnQuV3JhcHBlcnMkrrSziizavIECABJMABhEaWN0aW9uYXJ5V3JhcHBlciRtb2R1bGV0ADZMc2NhbGEvY29sbGVjdGlvbi9jb252ZXJ0L1dyYXBwZXJzJERpY3Rpb25hcnlXcmFwcGVyJDtMABZJdGVyYWJsZVdyYXBwZXIkbW9kdWxldAA0THNjYWxhL2NvbGxlY3Rpb24vY29udmVydC9XcmFwcGVycyRJdGVyYWJsZVdyYXBwZXIkO0wAFkl0ZXJhdG9yV3JhcHBlciRtb2R1bGV0ADRMc2NhbGEvY29sbGVjdGlvbi9jb252ZXJ0L1dyYXBwZXJzJEl0ZXJhdG9yV3JhcHBlciQ7TAAZSkNvbGxlY3Rpb25XcmFwcGVyJG1vZHVsZXQAN0xzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkSkNvbGxlY3Rpb25XcmFwcGVyJDtMABxKQ29uY3VycmVudE1hcFdyYXBwZXIkbW9kdWxldAA6THNjYWxhL2NvbGxlY3Rpb24vY29udmVydC9XcmFwcGVycyRKQ29uY3VycmVudE1hcFdyYXBwZXIkO0wAGUpEaWN0aW9uYXJ5V3JhcHBlciRtb2R1bGV0ADdMc2NhbGEvY29sbGVjdGlvbi9jb252ZXJ0L1dyYXBwZXJzJEpEaWN0aW9uYXJ5V3JhcHBlciQ7TAAaSkVudW1lcmF0aW9uV3JhcHBlciRtb2R1bGV0ADhMc2NhbGEvY29sbGVjdGlvbi9jb252ZXJ0L1dyYXBwZXJzJEpFbnVtZXJhdGlvbldyYXBwZXIkO0wAF0pJdGVyYWJsZVdyYXBwZXIkbW9kdWxldAA1THNjYWxhL2NvbGxlY3Rpb24vY29udmVydC9XcmFwcGVycyRKSXRlcmFibGVXcmFwcGVyJDtMABdKSXRlcmF0b3JXcmFwcGVyJG1vZHVsZXQANUxzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkSkl0ZXJhdG9yV3JhcHBlciQ7TAATSkxpc3RXcmFwcGVyJG1vZHVsZXQAMUxzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkSkxpc3RXcmFwcGVyJDtMABJKTWFwV3JhcHBlciRtb2R1bGV0ADBMc2NhbGEvY29sbGVjdGlvbi9jb252ZXJ0L1dyYXBwZXJzJEpNYXBXcmFwcGVyJDtMABlKUHJvcGVydGllc1dyYXBwZXIkbW9kdWxldAA3THNjYWxhL2NvbGxlY3Rpb24vY29udmVydC9XcmFwcGVycyRKUHJvcGVydGllc1dyYXBwZXIkO0wAEkpTZXRXcmFwcGVyJG1vZHVsZXQAMExzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkSlNldFdyYXBwZXIkO0wAG011dGFibGVCdWZmZXJXcmFwcGVyJG1vZHVsZXQAOUxzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkTXV0YWJsZUJ1ZmZlcldyYXBwZXIkO0wAGE11dGFibGVNYXBXcmFwcGVyJG1vZHVsZXQANkxzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkTXV0YWJsZU1hcFdyYXBwZXIkO0wAGE11dGFibGVTZXFXcmFwcGVyJG1vZHVsZXQANkxzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkTXV0YWJsZVNlcVdyYXBwZXIkO0wAGE11dGFibGVTZXRXcmFwcGVyJG1vZHVsZXQANkxzY2FsYS9jb2xsZWN0aW9uL2NvbnZlcnQvV3JhcHBlcnMkTXV0YWJsZVNldFdyYXBwZXIkO0wAEVNlcVdyYXBwZXIkbW9kdWxldAAvTHNjYWxhL2NvbGxlY3Rpb24vY29udmVydC9XcmFwcGVycyRTZXFXcmFwcGVyJDt4cHBwcHBwcHBwcHBwcHBwcHBwcA==") check(immutable.BitSet(1, 2, 3))( "rO0ABXNyAClzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5CaXRTZXQkQml0U2V0MR9dg8JGRI8UAgABSgAFZWxlbXN4cgAhc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuQml0U2V0Flz5Ms3qxsoCAAB4cAAAAAAAAAAO") @@ -130,7 +130,8 @@ object Test extends App { check(immutable.Queue())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5RdWV1ZZY146W3qSuhAgACTAACaW50ACFMc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvTGlzdDtMAANvdXRxAH4AAXhwc3IAMnNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3QkU2VyaWFsaXphdGlvblByb3h5AAAAAAAAAAEDAAB4cHNyACxzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0U2VyaWFsaXplRW5kJIpcY1v3UwttAgAAeHB4cQB+AAQ=") check(immutable.Queue(1, 2, 3))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5RdWV1ZZY146W3qSuhAgACTAACaW50ACFMc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvTGlzdDtMAANvdXRxAH4AAXhwc3IAMnNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3QkU2VyaWFsaXphdGlvblByb3h5AAAAAAAAAAEDAAB4cHNyACxzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0U2VyaWFsaXplRW5kJIpcY1v3UwttAgAAeHB4c3EAfgADc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcQB+AAgAAAACc3EAfgAIAAAAA3EAfgAGeA==") - check(new immutable.Range(0, 1, 1))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5SYW5nZWm7o1SrFTINAgAHSQADZW5kWgAHaXNFbXB0eUkAC2xhc3RFbGVtZW50SQAQbnVtUmFuZ2VFbGVtZW50c0kABXN0YXJ0SQAEc3RlcEkAD3Rlcm1pbmFsRWxlbWVudHhwAAAAAQAAAAAAAAAAAQAAAAAAAAABAAAAAQ==") + // TODO SI-8576 throws scala.UnitializedFieldError under -Xcheckinit + // check(new immutable.Range(0, 1, 1))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5SYW5nZWm7o1SrFTINAgAHSQADZW5kWgAHaXNFbXB0eUkAC2xhc3RFbGVtZW50SQAQbnVtUmFuZ2VFbGVtZW50c0kABXN0YXJ0SQAEc3RlcEkAD3Rlcm1pbmFsRWxlbWVudHhwAAAAAQAAAAAAAAAAAQAAAAAAAAABAAAAAQ==") check(immutable.Set())( "rO0ABXNyAChzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5TZXQkRW1wdHlTZXQk8Hk3TFN0uDYCAAB4cA==") check(immutable.Set(1))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5TZXQkU2V0MREd3c4yqtWTAgABTAAFZWxlbTF0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAAB") @@ -141,32 +142,42 @@ object Test extends App { check(immutable.Stack(1, 2, 3))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5TdGFjaxtt3qEbMvq+AgABTAAFZWxlbXN0ACFMc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvTGlzdDt4cHNyADJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5MaXN0JFNlcmlhbGl6YXRpb25Qcm94eQAAAAAAAAABAwAAeHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABQAAAAJzcQB+AAUAAAADc3IALHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3RTZXJpYWxpemVFbmQkilxjW/dTC20CAAB4cHg=") - check(immutable.Stream(1, 2, 3))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5TdHJlYW0kQ29uc/ekjBXM3TlFAgADTAACaGR0ABJMamF2YS9sYW5nL09iamVjdDtMAAV0bEdlbnQAEUxzY2FsYS9GdW5jdGlvbjA7TAAFdGxWYWx0ACNMc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvU3RyZWFtO3hyACFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5TdHJlYW0552RDntM42gIAAHhwc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcgAtc2NhbGEuY29sbGVjdGlvbi5JdGVyYXRvciQkYW5vbmZ1biR0b1N0cmVhbSQxRWR4We0SX0UCAAFMAAYkb3V0ZXJ0ABtMc2NhbGEvY29sbGVjdGlvbi9JdGVyYXRvcjt4cHNyAChzY2FsYS5jb2xsZWN0aW9uLkluZGV4ZWRTZXFMaWtlJEVsZW1lbnRzGF+1cBwmcx0CAANJAANlbmRJAAVpbmRleEwABiRvdXRlcnQAIUxzY2FsYS9jb2xsZWN0aW9uL0luZGV4ZWRTZXFMaWtlO3hwAAAAAwAAAAFzcgArc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLldyYXBwZWRBcnJheSRvZkludMmRLBcI15VjAgABWwAFYXJyYXl0AAJbSXhwdXIAAltJTbpgJnbqsqUCAAB4cAAAAAMAAAABAAAAAgAAAANw") + // TODO SI-8576 Uninitialized field: IndexedSeqLike.scala: 56 + // check(immutable.Stream(1, 2, 3))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5TdHJlYW0kQ29uc/ekjBXM3TlFAgADTAACaGR0ABJMamF2YS9sYW5nL09iamVjdDtMAAV0bEdlbnQAEUxzY2FsYS9GdW5jdGlvbjA7TAAFdGxWYWx0ACNMc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvU3RyZWFtO3hyACFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5TdHJlYW0552RDntM42gIAAHhwc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcgAtc2NhbGEuY29sbGVjdGlvbi5JdGVyYXRvciQkYW5vbmZ1biR0b1N0cmVhbSQxRWR4We0SX0UCAAFMAAYkb3V0ZXJ0ABtMc2NhbGEvY29sbGVjdGlvbi9JdGVyYXRvcjt4cHNyAChzY2FsYS5jb2xsZWN0aW9uLkluZGV4ZWRTZXFMaWtlJEVsZW1lbnRzGF+1cBwmcx0CAANJAANlbmRJAAVpbmRleEwABiRvdXRlcnQAIUxzY2FsYS9jb2xsZWN0aW9uL0luZGV4ZWRTZXFMaWtlO3hwAAAAAwAAAAFzcgArc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLldyYXBwZWRBcnJheSRvZkludMmRLBcI15VjAgABWwAFYXJyYXl0AAJbSXhwdXIAAltJTbpgJnbqsqUCAAB4cAAAAAMAAAABAAAAAgAAAANw") check(immutable.TreeSet[Int]())( "rO0ABXNyACJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5UcmVlU2V0sRdVIDjbWAsCAAJMAAhvcmRlcmluZ3QAFUxzY2FsYS9tYXRoL09yZGVyaW5nO0wABHRyZWV0AC5Mc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvUmVkQmxhY2tUcmVlJFRyZWU7eHBzcgAYc2NhbGEubWF0aC5PcmRlcmluZyRJbnQkC4BMdr1Z51wCAAB4cHA=") - check(immutable.TreeSet(1, 2, 3))( "rO0ABXNyACJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5UcmVlU2V0sRdVIDjbWAsCAAJMAAhvcmRlcmluZ3QAFUxzY2FsYS9tYXRoL09yZGVyaW5nO0wABHRyZWV0AC5Mc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvUmVkQmxhY2tUcmVlJFRyZWU7eHBzcgAYc2NhbGEubWF0aC5PcmRlcmluZyRJbnQkC4BMdr1Z51wCAAB4cHNyADFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5SZWRCbGFja1RyZWUkQmxhY2tUcmVlzRxnCKenVAECAAB4cgAsc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuUmVkQmxhY2tUcmVlJFRyZWVrqCSyHJbsMgIABUkABWNvdW50TAADa2V5dAASTGphdmEvbGFuZy9PYmplY3Q7TAAEbGVmdHEAfgACTAAFcmlnaHRxAH4AAkwABXZhbHVlcQB+AAh4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAnNxAH4ABgAAAAFzcQB+AAoAAAABcHBzcgAXc2NhbGEucnVudGltZS5Cb3hlZFVuaXR0pn1HHezLmgIAAHhwc3EAfgAGAAAAAXNxAH4ACgAAAANwcHEAfgAQcQB+ABA=") - - check(mutable.ArrayBuffer(1, 2, 3))( "rO0ABXNyACRzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlCdWZmZXIVOLBTg4KOcwIAA0kAC2luaXRpYWxTaXplSQAFc2l6ZTBbAAVhcnJheXQAE1tMamF2YS9sYW5nL09iamVjdDt4cAAAABAAAAADdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAEHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAFAAAAAnNxAH4ABQAAAANwcHBwcHBwcHBwcHBw") - check(mutable.ArraySeq(1, 2, 3))( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTZXEVPD3SKEkOcwIAAkkABmxlbmd0aFsABWFycmF5dAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAA3VyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABQAAAAJzcQB+AAUAAAAD") + + // TODO SI-8576 unstable under -Xcheckinit + // check(immutable.TreeSet(1, 2, 3))( "rO0ABXNyACJzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5UcmVlU2V0sRdVIDjbWAsCAAJMAAhvcmRlcmluZ3QAFUxzY2FsYS9tYXRoL09yZGVyaW5nO0wABHRyZWV0AC5Mc2NhbGEvY29sbGVjdGlvbi9pbW11dGFibGUvUmVkQmxhY2tUcmVlJFRyZWU7eHBzcgAYc2NhbGEubWF0aC5PcmRlcmluZyRJbnQkC4BMdr1Z51wCAAB4cHNyADFzY2FsYS5jb2xsZWN0aW9uLmltbXV0YWJsZS5SZWRCbGFja1RyZWUkQmxhY2tUcmVlzRxnCKenVAECAAB4cgAsc2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuUmVkQmxhY2tUcmVlJFRyZWVrqCSyHJbsMgIABUkABWNvdW50TAADa2V5dAASTGphdmEvbGFuZy9PYmplY3Q7TAAEbGVmdHEAfgACTAAFcmlnaHRxAH4AAkwABXZhbHVlcQB+AAh4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAnNxAH4ABgAAAAFzcQB+AAoAAAABcHBzcgAXc2NhbGEucnVudGltZS5Cb3hlZFVuaXR0pn1HHezLmgIAAHhwc3EAfgAGAAAAAXNxAH4ACgAAAANwcHEAfgAQcQB+ABA=") + + // TODO SI-8576 Uninitialized field under -Xcheckinit + // check(mutable.ArrayBuffer(1, 2, 3))( "rO0ABXNyACRzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlCdWZmZXIVOLBTg4KOcwIAA0kAC2luaXRpYWxTaXplSQAFc2l6ZTBbAAVhcnJheXQAE1tMamF2YS9sYW5nL09iamVjdDt4cAAAABAAAAADdXIAE1tMamF2YS5sYW5nLk9iamVjdDuQzlifEHMpbAIAAHhwAAAAEHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAFAAAAAnNxAH4ABQAAAANwcHBwcHBwcHBwcHBw") + // TODO SI-8576 Uninitialized field under -Xcheckinit + // check(mutable.ArraySeq(1, 2, 3))( "rO0ABXNyACFzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTZXEVPD3SKEkOcwIAAkkABmxlbmd0aFsABWFycmF5dAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAA3VyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABQAAAAJzcQB+AAUAAAAD") check(mutable.ArrayStack(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTdGFja3bdxXbcnLBeAgACSQAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJEFycmF5U3RhY2skJGluZGV4WwAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJEFycmF5U3RhY2skJHRhYmxldAATW0xqYXZhL2xhbmcvT2JqZWN0O3hwAAAAA3VyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAANzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAA3NxAH4ABQAAAAJzcQB+AAUAAAAB") check(mutable.DoubleLinkedList(1, 2, 3))( "rO0ABXNyAClzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuRG91YmxlTGlua2VkTGlzdI73LKsKRr1RAgADTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL1NlcTtMAARwcmV2cQB+AAJ4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAAc3EAfgAEAAAAAnNxAH4AAHNxAH4ABAAAAANzcQB+AABwcQB+AAtxAH4ACXEAfgAHcQB+AANw") check(mutable.HashMap())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAAAAAAABAB4") check(mutable.HashMap(1 -> 1))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXEAfgAEeA==") check(mutable.HashSet(1, 2, 3))( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGFzaFNldAAAAAAAAAABAwAAeHB3DQAAAcIAAAADAAAABQBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJzcQB+AAIAAAADeA==") - check(new mutable.History())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGlzdG9yeUhuXxDIFJrsAgACSQAKbWF4SGlzdG9yeUwAA2xvZ3QAIExzY2FsYS9jb2xsZWN0aW9uL211dGFibGUvUXVldWU7eHAAAAPoc3IAHnNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5RdWV1ZbjMURVfOuHHAgAAeHIAJHNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5NdXRhYmxlTGlzdFJpnjJ+gFbAAgADSQADbGVuTAAGZmlyc3QwdAAlTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9MaW5rZWRMaXN0O0wABWxhc3QwcQB+AAV4cAAAAABzcgAjc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkxpbmtlZExpc3Sak+nGCZHaUQIAAkwABGVsZW10ABJMamF2YS9sYW5nL09iamVjdDtMAARuZXh0dAAeTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9TZXE7eHBwcQB+AApxAH4ACg==") + // TODO SI-8576 Uninitialized field under -Xcheckinit + // check(new mutable.History())( "rO0ABXNyACBzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuSGlzdG9yeUhuXxDIFJrsAgACSQAKbWF4SGlzdG9yeUwAA2xvZ3QAIExzY2FsYS9jb2xsZWN0aW9uL211dGFibGUvUXVldWU7eHAAAAPoc3IAHnNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5RdWV1ZbjMURVfOuHHAgAAeHIAJHNjYWxhLmNvbGxlY3Rpb24ubXV0YWJsZS5NdXRhYmxlTGlzdFJpnjJ+gFbAAgADSQADbGVuTAAGZmlyc3QwdAAlTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9MaW5rZWRMaXN0O0wABWxhc3QwcQB+AAV4cAAAAABzcgAjc2NhbGEuY29sbGVjdGlvbi5tdXRhYmxlLkxpbmtlZExpc3Sak+nGCZHaUQIAAkwABGVsZW10ABJMamF2YS9sYW5nL09iamVjdDtMAARuZXh0dAAeTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9TZXE7eHBwcQB+AApxAH4ACg==") check(mutable.LinkedHashMap(1 -> 2))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkSGFzaE1hcAAAAAAAAAABAwAAeHB3DQAAAu4AAAABAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJ4") check(mutable.LinkedHashSet(1, 2, 3))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkSGFzaFNldAAAAAAAAAABAwAAeHB3DQAAAu4AAAADAAAABABzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4AAgAAAAJzcQB+AAIAAAADeA==") check(mutable.LinkedList(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlua2VkTGlzdJqT6cYJkdpRAgACTAAEZWxlbXQAEkxqYXZhL2xhbmcvT2JqZWN0O0wABG5leHR0AB5Mc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL1NlcTt4cHNyABFqYXZhLmxhbmcuSW50ZWdlchLioKT3gYc4AgABSQAFdmFsdWV4cgAQamF2YS5sYW5nLk51bWJlcoaslR0LlOCLAgAAeHAAAAABc3EAfgAAc3EAfgAEAAAAAnNxAH4AAHNxAH4ABAAAAANzcQB+AABwcQB+AAs=") - check(mutable.ListBuffer(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlzdEJ1ZmZlci9y9I7QyWzGAwAEWgAIZXhwb3J0ZWRJAANsZW5MAAVsYXN0MHQAKUxzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS8kY29sb24kY29sb247TAAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJExpc3RCdWZmZXIkJHN0YXJ0dAAhTHNjYWxhL2NvbGxlY3Rpb24vaW1tdXRhYmxlL0xpc3Q7eHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABAAAAAJzcQB+AAQAAAADc3IALHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3RTZXJpYWxpemVFbmQkilxjW/dTC20CAAB4cHcFAAAAAAN4") + + // TODO SI-8576 unstable under -Xcheckinit + // check(mutable.ListBuffer(1, 2, 3))( "rO0ABXNyACNzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuTGlzdEJ1ZmZlci9y9I7QyWzGAwAEWgAIZXhwb3J0ZWRJAANsZW5MAAVsYXN0MHQAKUxzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS8kY29sb24kY29sb247TAAqc2NhbGEkY29sbGVjdGlvbiRtdXRhYmxlJExpc3RCdWZmZXIkJHN0YXJ0dAAhTHNjYWxhL2NvbGxlY3Rpb24vaW1tdXRhYmxlL0xpc3Q7eHBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABAAAAAJzcQB+AAQAAAADc3IALHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLkxpc3RTZXJpYWxpemVFbmQkilxjW/dTC20CAAB4cHcFAAAAAAN4") check(new mutable.StringBuilder(new java.lang.StringBuilder("123")))( "rO0ABXNyACZzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuU3RyaW5nQnVpbGRlcomvqgGv1tTxAgABTAAKdW5kZXJseWluZ3QAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjt4cHNyABdqYXZhLmxhbmcuU3RyaW5nQnVpbGRlcjzV+xRaTGrLAwAAeHB3BAAAAAN1cgACW0OwJmaw4l2ErAIAAHhwAAAAEwAxADIAMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeA==") check(mutable.UnrolledBuffer[Int]())( "rO0ABXNyACdzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuVW5yb2xsZWRCdWZmZXIAAAAAAAAAAQMAAUwAA3RhZ3QAGExzY2FsYS9yZWZsZWN0L0NsYXNzVGFnO3hwc3IAJXNjYWxhLnJlZmxlY3QuTWFuaWZlc3RGYWN0b3J5JCRhbm9uJDnN+aJJU2O1UgIAAHhyABxzY2FsYS5yZWZsZWN0LkFueVZhbE1hbmlmZXN0AAAAAAAAAAECAAFMAAh0b1N0cmluZ3QAEkxqYXZhL2xhbmcvU3RyaW5nO3hwdAADSW50dwQAAAAAeA==") import collection.parallel check(parallel.immutable.ParHashMap(1 -> 2))( "rO0ABXNyAC5zY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLmltbXV0YWJsZS5QYXJIYXNoTWFwAAAAAAAAAAECAANMAA9TY2FuTGVhZiRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2NhbkxlYWYkO0wAD1NjYW5Ob2RlJG1vZHVsZXQANUxzY2FsYS9jb2xsZWN0aW9uL3BhcmFsbGVsL1Bhckl0ZXJhYmxlTGlrZSRTY2FuTm9kZSQ7TAAEdHJpZXQAJExzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS9IYXNoTWFwO3hwcHBzcgA1c2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuSGFzaE1hcCRTZXJpYWxpemF0aW9uUHJveHkAAAAAAAAAAgMAAHhwdwQAAAABc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcQB+AAcAAAACeA==") check(parallel.immutable.ParHashSet(1, 2, 3))( "rO0ABXNyAC5zY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLmltbXV0YWJsZS5QYXJIYXNoU2V0AAAAAAAAAAECAANMAA9TY2FuTGVhZiRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2NhbkxlYWYkO0wAD1NjYW5Ob2RlJG1vZHVsZXQANUxzY2FsYS9jb2xsZWN0aW9uL3BhcmFsbGVsL1Bhckl0ZXJhYmxlTGlrZSRTY2FuTm9kZSQ7TAAEdHJpZXQAJExzY2FsYS9jb2xsZWN0aW9uL2ltbXV0YWJsZS9IYXNoU2V0O3hwcHBzcgA1c2NhbGEuY29sbGVjdGlvbi5pbW11dGFibGUuSGFzaFNldCRTZXJpYWxpemF0aW9uUHJveHkAAAAAAAAAAgMAAHhwdwQAAAADc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAFzcQB+AAcAAAACc3EAfgAHAAAAA3g=") - check(new parallel.immutable.ParRange(new Range(0, 1, 2)))( "rO0ABXNyACxzY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLmltbXV0YWJsZS5QYXJSYW5nZQAAAAAAAAABAgAETAAXUGFyUmFuZ2VJdGVyYXRvciRtb2R1bGV0AEBMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9pbW11dGFibGUvUGFyUmFuZ2UkUGFyUmFuZ2VJdGVyYXRvciQ7TAAPU2NhbkxlYWYkbW9kdWxldAA1THNjYWxhL2NvbGxlY3Rpb24vcGFyYWxsZWwvUGFySXRlcmFibGVMaWtlJFNjYW5MZWFmJDtMAA9TY2FuTm9kZSRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2Nhbk5vZGUkO0wABXJhbmdldAAiTHNjYWxhL2NvbGxlY3Rpb24vaW1tdXRhYmxlL1JhbmdlO3hwcHBwc3IAIHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLlJhbmdlabujVKsVMg0CAAdJAANlbmRaAAdpc0VtcHR5SQALbGFzdEVsZW1lbnRJABBudW1SYW5nZUVsZW1lbnRzSQAFc3RhcnRJAARzdGVwSQAPdGVybWluYWxFbGVtZW50eHAAAAABAAAAAAAAAAABAAAAAAAAAAIAAAAC") - check(parallel.mutable.ParArray(1, 2, 3))( "rO0ABXNyACpzY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLm11dGFibGUuUGFyQXJyYXkAAAAAAAAAAQMABEwAF1BhckFycmF5SXRlcmF0b3IkbW9kdWxldAA+THNjYWxhL2NvbGxlY3Rpb24vcGFyYWxsZWwvbXV0YWJsZS9QYXJBcnJheSRQYXJBcnJheUl0ZXJhdG9yJDtMAA9TY2FuTGVhZiRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2NhbkxlYWYkO0wAD1NjYW5Ob2RlJG1vZHVsZXQANUxzY2FsYS9jb2xsZWN0aW9uL3BhcmFsbGVsL1Bhckl0ZXJhYmxlTGlrZSRTY2FuTm9kZSQ7TAAIYXJyYXlzZXF0ACNMc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL0FycmF5U2VxO3hwcHBwc3IAMXNjYWxhLmNvbGxlY3Rpb24ucGFyYWxsZWwubXV0YWJsZS5FeHBvc2VkQXJyYXlTZXGx2OTefAodSQIAAkkABmxlbmd0aFsABWFycmF5dAATW0xqYXZhL2xhbmcvT2JqZWN0O3hyACFzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTZXEVPD3SKEkOcwIAAkkABmxlbmd0aFsABWFycmF5cQB+AAd4cAAAAAN1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAADcHBwAAAAA3VxAH4ACgAAABBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ADQAAAAJzcQB+AA0AAAADcHBwcHBwcHBwcHBwcHg=") + // TODO SI-8576 Uninitialized field under -Xcheckinit + // check(new parallel.immutable.ParRange(new Range(0, 1, 2)))( "rO0ABXNyACxzY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLmltbXV0YWJsZS5QYXJSYW5nZQAAAAAAAAABAgAETAAXUGFyUmFuZ2VJdGVyYXRvciRtb2R1bGV0AEBMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9pbW11dGFibGUvUGFyUmFuZ2UkUGFyUmFuZ2VJdGVyYXRvciQ7TAAPU2NhbkxlYWYkbW9kdWxldAA1THNjYWxhL2NvbGxlY3Rpb24vcGFyYWxsZWwvUGFySXRlcmFibGVMaWtlJFNjYW5MZWFmJDtMAA9TY2FuTm9kZSRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2Nhbk5vZGUkO0wABXJhbmdldAAiTHNjYWxhL2NvbGxlY3Rpb24vaW1tdXRhYmxlL1JhbmdlO3hwcHBwc3IAIHNjYWxhLmNvbGxlY3Rpb24uaW1tdXRhYmxlLlJhbmdlabujVKsVMg0CAAdJAANlbmRaAAdpc0VtcHR5SQALbGFzdEVsZW1lbnRJABBudW1SYW5nZUVsZW1lbnRzSQAFc3RhcnRJAARzdGVwSQAPdGVybWluYWxFbGVtZW50eHAAAAABAAAAAAAAAAABAAAAAAAAAAIAAAAC") + // TODO SI-8576 unstable under -Xcheckinit + // check(parallel.mutable.ParArray(1, 2, 3))( "rO0ABXNyACpzY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLm11dGFibGUuUGFyQXJyYXkAAAAAAAAAAQMABEwAF1BhckFycmF5SXRlcmF0b3IkbW9kdWxldAA+THNjYWxhL2NvbGxlY3Rpb24vcGFyYWxsZWwvbXV0YWJsZS9QYXJBcnJheSRQYXJBcnJheUl0ZXJhdG9yJDtMAA9TY2FuTGVhZiRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2NhbkxlYWYkO0wAD1NjYW5Ob2RlJG1vZHVsZXQANUxzY2FsYS9jb2xsZWN0aW9uL3BhcmFsbGVsL1Bhckl0ZXJhYmxlTGlrZSRTY2FuTm9kZSQ7TAAIYXJyYXlzZXF0ACNMc2NhbGEvY29sbGVjdGlvbi9tdXRhYmxlL0FycmF5U2VxO3hwcHBwc3IAMXNjYWxhLmNvbGxlY3Rpb24ucGFyYWxsZWwubXV0YWJsZS5FeHBvc2VkQXJyYXlTZXGx2OTefAodSQIAAkkABmxlbmd0aFsABWFycmF5dAATW0xqYXZhL2xhbmcvT2JqZWN0O3hyACFzY2FsYS5jb2xsZWN0aW9uLm11dGFibGUuQXJyYXlTZXEVPD3SKEkOcwIAAkkABmxlbmd0aFsABWFycmF5cQB+AAd4cAAAAAN1cgATW0xqYXZhLmxhbmcuT2JqZWN0O5DOWJ8QcylsAgAAeHAAAAADcHBwAAAAA3VxAH4ACgAAABBzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ADQAAAAJzcQB+AA0AAAADcHBwcHBwcHBwcHBwcHg=") check(parallel.mutable.ParHashMap(1 -> 2))( "rO0ABXNyACxzY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLm11dGFibGUuUGFySGFzaE1hcAAAAAAAAAABAwACTAAPU2NhbkxlYWYkbW9kdWxldAA1THNjYWxhL2NvbGxlY3Rpb24vcGFyYWxsZWwvUGFySXRlcmFibGVMaWtlJFNjYW5MZWFmJDtMAA9TY2FuTm9kZSRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2Nhbk5vZGUkO3hwcHB3DQAAAu4AAAABAAAABAFzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABAAAAAJ4") check(parallel.mutable.ParHashSet(1, 2, 3))( "rO0ABXNyACxzY2FsYS5jb2xsZWN0aW9uLnBhcmFsbGVsLm11dGFibGUuUGFySGFzaFNldAAAAAAAAAABAwACTAAPU2NhbkxlYWYkbW9kdWxldAA1THNjYWxhL2NvbGxlY3Rpb24vcGFyYWxsZWwvUGFySXRlcmFibGVMaWtlJFNjYW5MZWFmJDtMAA9TY2FuTm9kZSRtb2R1bGV0ADVMc2NhbGEvY29sbGVjdGlvbi9wYXJhbGxlbC9QYXJJdGVyYWJsZUxpa2UkU2Nhbk5vZGUkO3hwcHB3DQAAAcIAAAADAAAAGwFzcgARamF2YS5sYW5nLkludGVnZXIS4qCk94GHOAIAAUkABXZhbHVleHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhwAAAAAXNxAH4ABAAAAAJzcQB+AAQAAAADeA==") diff --git a/test/files/run/t8574.scala b/test/files/run/t8574.scala new file mode 100644 index 0000000000..8c23ada482 --- /dev/null +++ b/test/files/run/t8574.scala @@ -0,0 +1,27 @@ +import annotation._ + +@SerialVersionUID(42) @strictfp class Foo[@specialized(Int) T] extends Serializable { + def foo(t: T) = t +} + +object Test extends App { + def checkUID(cls: Class[_], expected: Long) = { + val actual = java.io.ObjectStreamClass.lookup(cls).getSerialVersionUID + assert(actual == expected, s"$actual != expected for ${cls}") + } + def checkStrictFp(cls: Class[_]) = { + import java.lang.reflect._ + for (m <- cls.getDeclaredMethods) { + val isStrict = Modifier.isStrict(m.getModifiers) + assert(isStrict, cls) + } + } + def check(x: AnyRef) { + checkUID(x.getClass, 42) + checkStrictFp(x.getClass) + } + + check(new Foo[String]) + check(new Foo[Int]) +} + diff --git a/test/files/run/t8601-closure-elim.flags b/test/files/run/t8601-closure-elim.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/run/t8601-closure-elim.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/run/t8601-closure-elim.scala b/test/files/run/t8601-closure-elim.scala new file mode 100644 index 0000000000..2c5b03af77 --- /dev/null +++ b/test/files/run/t8601-closure-elim.scala @@ -0,0 +1,26 @@ +import scala.tools.partest.BytecodeTest +import scala.tools.asm +import scala.tools.asm.util._ +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + val nullChecks = Set(asm.Opcodes.NEW) + + def show: Unit = { + def test(methodName: String) { + val classNode = loadClassNode("Foo") + val methodNode = getMethod(classNode, "b") + val ops = methodNode.instructions.iterator.asScala.map(_.getOpcode).toList + assert(!ops.contains(asm.Opcodes.NEW), ops)// should be allocation free if the closure is eliminiated + } + test("b") + } +} + +class Foo { + @inline final def a(x: Int => Int) = x(1) + final def b { + val delta = 0 + a(x => delta + 1) + } +} diff --git a/test/files/run/t8601.flags b/test/files/run/t8601.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601.flags @@ -0,0 +1 @@ +-optimize
\ No newline at end of file diff --git a/test/files/run/t8601.scala b/test/files/run/t8601.scala new file mode 100644 index 0000000000..e1afc23cc4 --- /dev/null +++ b/test/files/run/t8601.scala @@ -0,0 +1,15 @@ +object Test { + def idiv(x: Int): Unit = x / 0 + def ldiv(x: Long): Unit = x / 0 + def irem(x: Int): Unit = x % 0 + def lrem(x: Long): Unit = x % 0 + + def check(x: => Any) = try { x; sys.error("failed to throw divide by zero!") } catch { case _: ArithmeticException => } + + def main(args: Array[String]) { + check(idiv(1)) + check(ldiv(1L)) + check(irem(1)) + check(lrem(1L)) + } +} diff --git a/test/files/run/t8601b.flags b/test/files/run/t8601b.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601b.flags @@ -0,0 +1 @@ +-optimize
\ No newline at end of file diff --git a/test/files/run/t8601b.scala b/test/files/run/t8601b.scala new file mode 100644 index 0000000000..9c37ce33d6 --- /dev/null +++ b/test/files/run/t8601b.scala @@ -0,0 +1,14 @@ +object Test { + def len(x: Array[String]): Unit = x.length + def load(x: Array[String]): Unit = x(0) + def newarray(i: Int): Unit = new Array[Int](i) + + def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + def checkNegSize(x: => Any) = try { x; sys.error("failed to throw NegativeArraySizeException!") } catch { case _: NegativeArraySizeException => } + + def main(args: Array[String]) { + check(len(null)) // bug: did not NPE + check(load(null)) + checkNegSize(newarray(-1)) + } +} diff --git a/test/files/run/t8601c.flags b/test/files/run/t8601c.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601c.flags @@ -0,0 +1 @@ +-optimize
\ No newline at end of file diff --git a/test/files/run/t8601c.scala b/test/files/run/t8601c.scala new file mode 100644 index 0000000000..c487d6825e --- /dev/null +++ b/test/files/run/t8601c.scala @@ -0,0 +1,12 @@ +object Test { + def loadField(x: scala.runtime.IntRef): Unit = x.elem + def storeField(x: scala.runtime.IntRef): Unit = x.elem = 42 + + def check(x: => Any) = try { x; sys.error("failed to throw NPE!") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(loadField(null)) // bug: did not NPE under -Ydead-code + check(storeField(null)) + + } +} diff --git a/test/files/run/t8601d.flags b/test/files/run/t8601d.flags new file mode 100644 index 0000000000..1182725e86 --- /dev/null +++ b/test/files/run/t8601d.flags @@ -0,0 +1 @@ +-optimize
\ No newline at end of file diff --git a/test/files/run/t8601d.scala b/test/files/run/t8601d.scala new file mode 100644 index 0000000000..ac89963d67 --- /dev/null +++ b/test/files/run/t8601d.scala @@ -0,0 +1,8 @@ +object Test { + def monitor(x: AnyRef): Unit = {x.synchronized(()); ()} + def check(x: => Any) = try { x; sys.error("failed to throw NPE") } catch { case _: NullPointerException => } + + def main(args: Array[String]) { + check(monitor(null)) + } +} diff --git a/test/files/run/t8601e.flags b/test/files/run/t8601e.flags new file mode 100644 index 0000000000..49d036a887 --- /dev/null +++ b/test/files/run/t8601e.flags @@ -0,0 +1 @@ +-optimize diff --git a/test/files/run/t8601e/StaticInit.class b/test/files/run/t8601e/StaticInit.class Binary files differnew file mode 100644 index 0000000000..99a0e2a643 --- /dev/null +++ b/test/files/run/t8601e/StaticInit.class diff --git a/test/files/run/t8601e/StaticInit.java b/test/files/run/t8601e/StaticInit.java new file mode 100644 index 0000000000..7543ed98b8 --- /dev/null +++ b/test/files/run/t8601e/StaticInit.java @@ -0,0 +1,8 @@ +public class StaticInit { + static { + if ("".isEmpty()) { + throw new RuntimeException(); + } + } + public static int fld = 42; +} diff --git a/test/files/run/t8601e/Test.scala b/test/files/run/t8601e/Test.scala new file mode 100644 index 0000000000..838114f6a7 --- /dev/null +++ b/test/files/run/t8601e/Test.scala @@ -0,0 +1,12 @@ +class C { + def foo: Unit = {StaticInit.fld} +} + +object Test extends App { + try { + new C().foo + sys.error("StaticInit.<clinit> was not run!") + } catch { + case t: ExceptionInInitializerError => + } +} diff --git a/test/files/run/t8607.scala b/test/files/run/t8607.scala new file mode 100644 index 0000000000..1b8ef9bbd0 --- /dev/null +++ b/test/files/run/t8607.scala @@ -0,0 +1,36 @@ +package p1 { + private[p1] trait B extends Any { + def a: Any = "" + } + + class C(val value: Int) extends AnyVal with B { + // def b = "" + } +} + +object Test { + def main(args: Array[String]) { + val c = new p1.C(42) + c.a + /* + new p1.C.<init>( + c.$asInstanceOf[scala.this.Int]() + ).a(); + + + new p1.C.<init>( + new p1.C.<init>( + c.$asInstanceOf[scala.this.Int]() + ).$asInstanceOf[ErasedValueType(class C, scala.this.Int)]() + .$asInstanceOf[scala.this.Int]() + ).a(); + + new p1.C.<init>( + new p1.C.<init>(c) + .$asInstanceOf[scala.this.Int]() + .$asInstanceOf[scala.this.Int]() + ).a(); + + */ + } +} diff --git a/test/junit/scala/StringContextTest.scala b/test/junit/scala/StringContextTest.scala new file mode 100644 index 0000000000..5abfe90cd1 --- /dev/null +++ b/test/junit/scala/StringContextTest.scala @@ -0,0 +1,51 @@ + +package scala + +import org.junit.Test +import org.junit.Assert._ +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.AssertUtil._ + +@RunWith(classOf[JUnit4]) +class StringContextTest { + + import StringContext._ + + @Test def noEscape() = { + val s = "string" + val res = processEscapes(s) + assertEquals(s, res) + } + @Test def tabbed() = { + val s = """a\tb""" + val res = processEscapes(s) + assertEquals("a\tb", res) + } + @Test def quoted() = { + val s = """hello, \"world\"""" + val res = processEscapes(s) + assertEquals("""hello, "world"""", res) + } + @Test def octal() = { + val s = """\123cala""" + val res = treatEscapes(s) + assertEquals("Scala", res) + } + @Test def doubled() = { + val s = """\123cala\123yntax""" + val res = treatEscapes(s) + assertEquals("ScalaSyntax", res) + } + @Test def badly() = assertThrows[InvalidEscapeException] { + val s = """Scala\""" + val res = treatEscapes(s) + assertEquals("Scala", res) + } + @Test def noOctal() = assertThrows[InvalidEscapeException] { + val s = """\123cala""" + val res = processEscapes(s) + assertEquals("Scala", res) + } +} diff --git a/test/junit/scala/collection/IteratorTest.scala b/test/junit/scala/collection/IteratorTest.scala new file mode 100644 index 0000000000..b7a9805c9f --- /dev/null +++ b/test/junit/scala/collection/IteratorTest.scala @@ -0,0 +1,28 @@ + +package scala.collection + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(classOf[JUnit4]) +class IteratorTest { + + @Test + def groupedIteratorShouldNotAskForUnneededElement(): Unit = { + var counter = 0 + val it = new Iterator[Int] { var i = 0 ; def hasNext = { counter = i; true } ; def next = { i += 1; i } } + val slidingIt = it sliding 2 + slidingIt.next + assertEquals("Counter should be one, that means we didn't look further than needed", 1, counter) + } + + @Test def groupedIteratorIsLazyWhenPadded(): Unit = { + var counter = 0 + def it = new Iterator[Int] { var i = 0 ; def hasNext = { counter = i; true } ; def next = { i += 1; i } } + val slidingIt = it sliding 2 withPadding -1 + slidingIt.next + assertEquals("Counter should be one, that means we didn't look further than needed", 1, counter) + } +} diff --git a/test/junit/scala/reflect/internal/NamesTest.scala b/test/junit/scala/reflect/internal/NamesTest.scala new file mode 100644 index 0000000000..549c10abed --- /dev/null +++ b/test/junit/scala/reflect/internal/NamesTest.scala @@ -0,0 +1,95 @@ +package scala.reflect.internal + +import scala.tools.testing.AssertUtil._ +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import org.junit.Assert._ +import scala.tools.nsc.symtab.SymbolTableForUnitTesting + +@RunWith(classOf[JUnit4]) +class NamesTest { + object symbolTable extends SymbolTableForUnitTesting + import symbolTable._ + + val h1 = newTermName("hai") + val h2 = newTermName("hai") + val f = newTermName("fisch") + + val h1y = h1.toTypeName + val h2y = newTypeName("hai") + val fy = newTypeName("fisch") + + val uy = newTypeName("uhu") + val u = uy.toTermName // calling toTermName after constructing a typeName. This tests the fact + // that creating a typeName always also first creates a termName. There is + // an assertion for that in toTermName. + + @Test + def termNamesAreHashConsed() { + assertTrue(h1 eq h2) + assertEquals(h1, h2) + assertTrue(h1 ne f) + assertTrue(h1 != f) + } + + @Test + def termNamesNotEqualsTypeNames() { + assert(h1 ne h1y) + assert(h1 != h1y) + assert(h2 ne h2y) + assert(h2 != h2y) + } + + @Test + def termNamesTypeNamesSameRange() { + assert(h1.start == h1y.start && h1.length == h1y.length) + assert(h2.start == h2y.start && h2.length == h2y.length) + assert(u.start == uy.start && u.length == uy.length) + } + + @Test + def testLookupTypeName() { + assert(lookupTypeName("hai".toCharArray) eq h1y) + assert(lookupTypeName("fisch".toCharArray) eq fy) + assert(lookupTypeName("uhu".toCharArray) eq uy) + + assertThrows[AssertionError](lookupTypeName("dog".toCharArray), _ contains "not yet created") + val d = newTermName("dog") + assertThrows[AssertionError](lookupTypeName("dog".toCharArray), _ contains "not yet created") + val dy = d.toTypeName + assert(lookupTypeName("dog".toCharArray) eq dy) + } + + @Test + def emptyName() { + val z = newTermName("") + val zy = z.toTypeName + assertEquals(z.toString, "") + assertEquals(zy.toString, "") + assert(z eq newTermName("")) + assert(zy eq newTypeName("")) + } + + @Test + def subNameTest() { + val i = f.subName(1, f.length) + assert(i.start == (f.start + 1) && i.length == (f.length - 1)) + assert(f.subName(0, f.length) eq f) + + val iy = fy.subName(1, fy.length) + assert(iy.start == (fy.start + 1) && iy.length == (fy.length - 1)) + assert(fy.subName(0, fy.length) eq fy) + + assert(f.subName(1,1) eq newTermName("")) + assert(f.subName(1, 0) eq newTermName("")) + + assertThrows[IllegalArgumentException](f.subName(0 - f.start - 1, 1)) + } + + @Test + def stringEqualsTest() { + assert(h1 string_== h2) + assert(h1 string_== h1y) + } +} diff --git a/test/junit/scala/reflect/internal/ScopeTest.scala b/test/junit/scala/reflect/internal/ScopeTest.scala new file mode 100644 index 0000000000..5166620189 --- /dev/null +++ b/test/junit/scala/reflect/internal/ScopeTest.scala @@ -0,0 +1,54 @@ +package symtab + +import scala.tools.nsc.symtab + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.testing.AssertUtil.assertThrows +import scala.tools.nsc.symtab.SymbolTableForUnitTesting + +@RunWith(classOf[JUnit4]) +class ScopeTest { + object symbolTable extends SymbolTableForUnitTesting + + import symbolTable._ + + @Test + def testNestedScopeSmall(): Unit = testNestedScope(0) + @Test + def testNestedScopeLarge(): Unit = testNestedScope(64) // exceeding MIN_HASH + + private def testNestedScope(initSize: Int) { + def sym(termName: String): Symbol = NoSymbol.newValue(TermName(termName)) + val foo = sym("foo") + val bar = sym("bar") + + val outerElems = List.tabulate(initSize)(i => sym(i.toString)) + val outer = newScopeWith(outerElems ++ List(foo, bar) : _*) + assertTrue(outer.containsName(foo.name)) + assertTrue(outer.containsName(bar.name)) + + val baz = sym("baz") + val nested = newNestedScope(outer) + + // Entries from the outer scope are entered in the nested. + assertTrue(outer.containsName(foo.name)) + assertTrue(outer.containsName(bar.name)) + + // Nested scopes structurally share ScopeEntry-s with the outer. + assertSame(outer.lookupEntry(foo.name), nested.lookupEntry(foo.name)) + nested.enter(baz) + + // Symbols entered in the nested scope aren't visible in the outer. + assertTrue(nested.containsName(baz.name)) + assertTrue(!outer.containsName(baz.name)) + + // Unlinking a symbol in the inner scope doesn't modify the outer + nested.unlink(bar) + assert(!nested.containsName(bar.name)) + assert(outer.containsName(bar.name)) + } +} diff --git a/test/junit/scala/reflect/internal/util/SourceFileTest.scala b/test/junit/scala/reflect/internal/util/SourceFileTest.scala index 903e705ba2..cad23eba14 100644 --- a/test/junit/scala/reflect/internal/util/SourceFileTest.scala +++ b/test/junit/scala/reflect/internal/util/SourceFileTest.scala @@ -17,6 +17,11 @@ class SourceFileTest { assertFalse(file.isEndOfLine(Int.MaxValue)) } + @Test def si8630_lineToString(): Unit = { + val code = "abc " + assertEquals(code, new BatchSourceFile("", code).lineToString(0)) + } + @Test def si8205_lineToString(): Unit = { assertEquals("", lineContentOf("", 0)) diff --git a/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala new file mode 100644 index 0000000000..b592d06501 --- /dev/null +++ b/test/junit/scala/tools/nsc/backend/jvm/BTypesTest.scala @@ -0,0 +1,104 @@ +package scala.tools.nsc +package backend.jvm + +import scala.tools.testing.AssertUtil._ +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Test +import scala.tools.asm.Opcodes +import org.junit.Assert._ + +@RunWith(classOf[JUnit4]) +class BTypesTest { + val g: Global = new Global(new Settings()) + + val btypes = new BTypes[g.type](g) { + def chrs = g.chrs + override type BTypeName = g.TypeName + override def createNewName(s: String) = g.newTypeName(s) + } + + import btypes._ + + val jls = "java/lang/String" + val jlo = "java/lang/Object" + + val o = ClassBType(jlo) + val s = ClassBType(jls) + val oArr = ArrayBType(o) + val method = MethodBType(List(oArr, INT, DOUBLE, s), UNIT) + + @Test + def classBTypesEquality() { + val s1 = ClassBType(jls) + val s2 = ClassBType(jls) + val o = ClassBType(jlo) + assertEquals(s1, s2) + assertEquals(s1.hashCode, s2.hashCode) + assert(s1 != o) + assert(s2 != o) + } + + @Test + def classBTypeRequiresInternalName() { + assertThrows[AssertionError](ClassBType(s"L$jls;"), _ contains "Descriptor instead of internal name") + } + + @Test + def typedOpcodes() { + assert(UNIT.typedOpcode(Opcodes.IALOAD) == Opcodes.IALOAD) + assert(INT.typedOpcode(Opcodes.IALOAD) == Opcodes.IALOAD) + assert(BOOL.typedOpcode(Opcodes.IALOAD) == Opcodes.BALOAD) + assert(BYTE.typedOpcode(Opcodes.IALOAD) == Opcodes.BALOAD) + assert(CHAR.typedOpcode(Opcodes.IALOAD) == Opcodes.CALOAD) + assert(SHORT.typedOpcode(Opcodes.IALOAD) == Opcodes.SALOAD) + assert(FLOAT.typedOpcode(Opcodes.IALOAD) == Opcodes.FALOAD) + assert(LONG.typedOpcode(Opcodes.IALOAD) == Opcodes.LALOAD) + assert(DOUBLE.typedOpcode(Opcodes.IALOAD) == Opcodes.DALOAD) + assert(ClassBType(jls).typedOpcode(Opcodes.IALOAD) == Opcodes.AALOAD) + + assert(UNIT.typedOpcode(Opcodes.IRETURN) == Opcodes.RETURN) + assert(BOOL.typedOpcode(Opcodes.IRETURN) == Opcodes.IRETURN) + assert(CHAR.typedOpcode(Opcodes.IRETURN) == Opcodes.IRETURN) + assert(BYTE.typedOpcode(Opcodes.IRETURN) == Opcodes.IRETURN) + assert(SHORT.typedOpcode(Opcodes.IRETURN) == Opcodes.IRETURN) + assert(INT.typedOpcode(Opcodes.IRETURN) == Opcodes.IRETURN) + assert(FLOAT.typedOpcode(Opcodes.IRETURN) == Opcodes.FRETURN) + assert(LONG.typedOpcode(Opcodes.IRETURN) == Opcodes.LRETURN) + assert(DOUBLE.typedOpcode(Opcodes.IRETURN) == Opcodes.DRETURN) + assert(ClassBType(jls).typedOpcode(Opcodes.IRETURN) == Opcodes.ARETURN) + } + + @Test + def descriptors() { + assert(o.descriptor == "Ljava/lang/Object;") + assert(s.descriptor == "Ljava/lang/String;") + assert(oArr.descriptor == "[Ljava/lang/Object;") + assert(method.descriptor == "([Ljava/lang/Object;IDLjava/lang/String;)V") + } + + @Test + def toAsmTypeTest() { + for (t <- List(o, s, oArr, method, INT, UNIT, DOUBLE)) { + assertEquals(o.descriptor, o.toASMType.getDescriptor) + } + } + + @Test + def parseMethodDescriptorTest() { + val descriptors = List( + "()V", + "(ID)I", + "([[I[D)[D", + s"(L$jls;[L$jlo;)[[L$jls;", + s"(IL$jlo;)L$jls;" + ) + for (d <- descriptors) { + assertEquals(d, MethodBType(d).descriptor) + } + + // class types in method descriptor need surrounding 'L' and ';' + assertThrows[MatchError](MethodBType("(java/lang/String)V"), _ == "j (of class java.lang.Character)") + assertThrows[AssertionError](MethodBType("I"), _ contains "Not a valid method descriptor") + } +} diff --git a/test/junit/scala/tools/testing/AssertUtil.scala b/test/junit/scala/tools/testing/AssertUtil.scala index 9efac64a97..9a97c5114f 100644 --- a/test/junit/scala/tools/testing/AssertUtil.scala +++ b/test/junit/scala/tools/testing/AssertUtil.scala @@ -5,15 +5,20 @@ package testing * that are ultimately based on junit.Assert primitives. */ object AssertUtil { - /** Check if exception T (or a subclass) was thrown during evaluation of f. - * If any other exception or throwable is found instead it will be re-thrown. + /** + * Check if throwable T (or a subclass) was thrown during evaluation of f, and that its message + * satisfies the `checkMessage` predicate. + * If any other exception will be re-thrown. */ - def assertThrows[T <: Exception](f: => Any)(implicit manifest: Manifest[T]): Unit = + def assertThrows[T <: Throwable](f: => Any, + checkMessage: String => Boolean = s => true) + (implicit manifest: Manifest[T]): Unit = { try f catch { - case e: Exception => - val clazz = manifest.erasure.asInstanceOf[Class[T]] + case e: Throwable if checkMessage(e.getMessage) => + val clazz = manifest.runtimeClass if (!clazz.isAssignableFrom(e.getClass)) throw e } -}
\ No newline at end of file + } +} diff --git a/versions.properties b/versions.properties index 0e56f731ed..1212734d22 100644 --- a/versions.properties +++ b/versions.properties @@ -1,10 +1,10 @@ -#Wed, 16 Apr 2014 11:13:08 +0200 +#Tue, 20 May 2014 10:01:37 +0200 # NOTE: this file determines the content of the scala-distribution # via scala-dist-pom.xml and scala-library-all-pom.xml # when adding new properties that influence a release, # also add them to the update.versions mechanism in build.xml, # which is used by scala-release-2.11.x in scala/jenkins-scripts -starr.version=2.11.0 +starr.version=2.11.1 starr.use.released=1 # These are the versions of the modules that go with this release. @@ -14,15 +14,15 @@ starr.use.released=1 scala.binary.version=2.11 # e.g. 2.11.0-RC1, 2.11.0, 2.11.1-RC1, 2.11.1 # this defines the dependency on scala-continuations-plugin in scala-dist's pom -scala.full.version=2.11.0 +scala.full.version=2.11.1 # external modules shipped with distribution, as specified by scala-library-all's pom -scala-xml.version.number=1.0.1 +scala-xml.version.number=1.0.2 scala-parser-combinators.version.number=1.0.1 -scala-continuations-plugin.version.number=1.0.1 -scala-continuations-library.version.number=1.0.1 +scala-continuations-plugin.version.number=1.0.2 +scala-continuations-library.version.number=1.0.2 scala-swing.version.number=1.0.1 -akka-actor.version.number=2.3.2 +akka-actor.version.number=2.3.3 actors-migration.version.number=1.1.0 # external modules, used internally (not shipped) |