diff options
408 files changed, 7931 insertions, 2648 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..0b90cf4c8b 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -230,6 +230,47 @@ filter { { matchName="scala.reflect.runtime.JavaMirrors#JavaMirror.scala$reflect$runtime$JavaMirrors$JavaMirror$$followStatic" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.SynchronizedOps.newNestedScope" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.JavaUniverse" + problemName=MissingTypesProblem + }, + { + matchName="scala.reflect.runtime.JavaUniverse.reporter" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.JavaUniverse$PerRunReporting" + problemName=MissingClassProblem + }, + { + matchName="scala.reflect.runtime.JavaUniverse.currentRun" + problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.JavaUniverse.PerRunReporting" + problemName=MissingMethodProblem + }, + // see SI-5919 + { + matchName="scala.reflect.api.TypeTags$PredefTypeCreator" + problemName=MissingTypesProblem + }, + { + matchName="scala.reflect.api.TreeCreator" + problemName=MissingTypesProblem + }, + { + matchName="scala.reflect.api.TypeCreator" + problemName=MissingTypesProblem + }, + { + matchName="scala.reflect.api.PredefTypeCreator" + problemName=MissingClassProblem } ] } 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 @@ -187,8 +187,6 @@ TODO: <property name="copyright.string" value="Copyright 2002-2013, LAMP/EPFL"/> - <property name="jline.version" value="2.11"/> - <!-- These are NOT the flags used to run SuperSabbus, but the ones written into the script runners created with scala.tools.ant.ScalaTool --> <property name="java.flags" value="-Xmx256M -Xms32M"/> @@ -1357,20 +1355,24 @@ TODO: </target> <target name="test.osgi" depends="test.osgi.comp"> - <stopwatch name="test.osgi.timer"/> - <mkdir dir="${test.osgi.classes}"/> - - <echo message="Running OSGi JUnit tests. Output in ${build-osgi.dir}"/> - <junit fork="yes" haltonfailure="yes"> - <classpath refid="test.osgi.compiler.build.path"/> - <batchtest fork="yes" todir="${build-osgi.dir}"> - <fileset dir="${test.osgi.classes}"> - <include name="**/*Test.class"/> - </fileset> - </batchtest> - <formatter type="xml" /> <!-- silenced by having it use a file; I tried for an hour to use other formatters but classpath issues drove me to this usefile="false" --> - </junit> - <stopwatch name="test.osgi.timer" action="total"/> + <if><isset property="has.java8"/><then> + <echo message="Skipping OSGi JUnit tests on Java 8. See SI-8642"/> + </then><else> + <echo message="Running OSGi JUnit tests. Output in ${build-osgi.dir}"/> + <stopwatch name="test.osgi.timer"/> + <mkdir dir="${test.osgi.classes}"/> + + <junit fork="yes" haltonfailure="yes"> + <classpath refid="test.osgi.compiler.build.path"/> + <batchtest fork="yes" todir="${build-osgi.dir}"> + <fileset dir="${test.osgi.classes}"> + <include name="**/*Test.class"/> + </fileset> + </batchtest> + <formatter type="xml" /> <!-- silenced by having it use a file; I tried for an hour to use other formatters but classpath issues drove me to this usefile="false" --> + </junit> + <stopwatch name="test.osgi.timer" action="total"/> + </else></if> </target> 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/reflect/macros/contexts/Parsers.scala b/src/compiler/scala/reflect/macros/contexts/Parsers.scala index 88cfea8157..f4584f3627 100644 --- a/src/compiler/scala/reflect/macros/contexts/Parsers.scala +++ b/src/compiler/scala/reflect/macros/contexts/Parsers.scala @@ -9,12 +9,15 @@ trait Parsers { def parse(code: String) = { val sreporter = new StoreReporter() - val unit = new CompilationUnit(newSourceFile(code, "<macro>")) { override def reporter = sreporter } - val parser = newUnitParser(unit) - val tree = gen.mkTreeOrBlock(parser.parseStatsOrPackages()) - sreporter.infos.foreach { - case sreporter.Info(pos, msg, sreporter.ERROR) => throw ParseException(pos, msg) - } - tree + val oldReporter = global.reporter + try { + global.reporter = sreporter + val parser = newUnitParser(new CompilationUnit(newSourceFile(code, "<macro>"))) + val tree = gen.mkTreeOrBlock(parser.parseStatsOrPackages()) + sreporter.infos.foreach { + case sreporter.Info(pos, msg, sreporter.ERROR) => throw ParseException(pos, msg) + } + tree + } finally global.reporter = oldReporter } }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/CompilationUnits.scala b/src/compiler/scala/tools/nsc/CompilationUnits.scala index c2caed70a0..f23bca77cd 100644 --- a/src/compiler/scala/tools/nsc/CompilationUnits.scala +++ b/src/compiler/scala/tools/nsc/CompilationUnits.scala @@ -123,31 +123,23 @@ trait CompilationUnits { global: Global => */ val icode: LinkedHashSet[icodes.IClass] = new LinkedHashSet - def reporter = global.reporter + @deprecated("Call global.reporter.echo directly instead.", "2.11.2") + final def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg) + @deprecated("Call global.reporter.error (or typer.context.error) directly instead.", "2.11.2") + final def error(pos: Position, msg: String): Unit = reporter.error(pos, msg) + @deprecated("Call global.reporter.warning (or typer.context.warning) directly instead.", "2.11.2") + final def warning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) + + @deprecated("Call global.currentRun.reporting.deprecationWarning directly instead.", "2.11.2") + final def deprecationWarning(pos: Position, msg: String): Unit = currentRun.reporting.deprecationWarning(pos, msg) + @deprecated("Call global.currentRun.reporting.uncheckedWarning directly instead.", "2.11.2") + final def uncheckedWarning(pos: Position, msg: String): Unit = currentRun.reporting.uncheckedWarning(pos, msg) + + // called by ScalaDocAnalyzer, overridden by the IDE (in Reporter) + // TODO: don't use reporter to communicate comments from parser to IDE! + @deprecated("This method will be removed.", "2.11.2") + final def comment(pos: Position, msg: String): Unit = reporter.comment(pos, msg) - def echo(pos: Position, msg: String) = - reporter.echo(pos, msg) - - def error(pos: Position, msg: String) = - reporter.error(pos, msg) - - def warning(pos: Position, msg: String) = - reporter.warning(pos, msg) - - def deprecationWarning(pos: Position, msg: String) = - currentRun.deprecationWarnings0.warn(pos, msg) - - def uncheckedWarning(pos: Position, msg: String) = - currentRun.uncheckedWarnings0.warn(pos, msg) - - def inlinerWarning(pos: Position, msg: String) = - currentRun.inlinerWarnings.warn(pos, msg) - - def incompleteInputError(pos: Position, msg:String) = - reporter.incompleteInputError(pos, msg) - - def comment(pos: Position, msg: String) = - reporter.comment(pos, msg) /** Is this about a .java source file? */ lazy val isJava = source.file.name.endsWith(".java") diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index 6f068e179c..1f3a4237eb 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -7,7 +7,7 @@ package scala.tools.nsc import java.io.PrintStream import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} -import scala.reflect.internal.util.FakePos //Position +import scala.reflect.internal.util.{FakePos, Position} import scala.tools.util.SocketServer import settings.FscSettings @@ -37,7 +37,7 @@ class StandardCompileServer extends SocketServer { /** Create a new compiler instance */ def newGlobal(settings: Settings, reporter: Reporter) = new Global(settings, reporter) { - override def inform(msg: String) = out.println(msg) + override def inform(pos: Position, msg: String) = out.println(msg) } override def timeout() { diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index bab0768ca9..a1d0d52dcf 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -20,9 +20,12 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { def ok = processArgumentsResult._1 def files = processArgumentsResult._2 - /** The name of the command */ + /** The name of the command. */ def cmdName = "scalac" + /** A descriptive alias for version and help messages. */ + def cmdDesc = "compiler" + private def explainAdvanced = "\n" + """ |-- Notes on option parsing -- |Boolean settings are always false unless set. @@ -85,7 +88,11 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { def getInfoMessage(global: Global): String = { import settings._ - if (help) usageMsg + global.pluginOptionsHelp + import Properties.{ versionString, copyrightString } //versionFor + def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + if (version) versionFor(cmdDesc) + else if (help) usageMsg + global.pluginOptionsHelp else if (Xhelp) xusageMsg else if (Yhelp) yusageMsg else if (showPlugins) global.pluginDescriptions diff --git a/src/compiler/scala/tools/nsc/Driver.scala b/src/compiler/scala/tools/nsc/Driver.scala index 3ac27a42e8..6befa76b3f 100644 --- a/src/compiler/scala/tools/nsc/Driver.scala +++ b/src/compiler/scala/tools/nsc/Driver.scala @@ -2,26 +2,24 @@ package scala package tools.nsc import scala.tools.nsc.reporters.ConsoleReporter -import Properties.{ versionString, copyrightString, residentPromptString } +import Properties.{ versionMsg, residentPromptString } import scala.reflect.internal.util.FakePos abstract class Driver { val prompt = residentPromptString - val versionMsg = "Scala compiler " + - versionString + " -- " + - copyrightString - var reporter: ConsoleReporter = _ protected var command: CompilerCommand = _ protected var settings: Settings = _ - protected def scalacError(msg: String) { + protected def scalacError(msg: String): Unit = { reporter.error(FakePos("scalac"), msg + "\n scalac -help gives more information") } - protected def processSettingsHook(): Boolean = true + protected def processSettingsHook(): Boolean = { + if (settings.version) { reporter echo versionMsg ; false } else true + } protected def newCompiler(): Global @@ -37,14 +35,12 @@ abstract class Driver { } def process(args: Array[String]) { - val ss = new Settings(scalacError) - reporter = new ConsoleReporter(ss) + val ss = new Settings(scalacError) + reporter = new ConsoleReporter(ss) command = new CompilerCommand(args.toList, ss) settings = command.settings - if (settings.version) { - reporter.echo(versionMsg) - } else if (processSettingsHook()) { + if (processSettingsHook()) { val compiler = newCompiler() try { if (reporter.hasErrors) @@ -68,5 +64,4 @@ abstract class Driver { process(args) sys.exit(if (reporter.hasErrors) 1 else 0) } - } diff --git a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala index e710222285..dbdeec809f 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerCommand.scala @@ -19,9 +19,10 @@ extends CompilerCommand(args, settings) { def this(args: List[String]) = this(args, str => Console.println("Error: " + str)) - /** name of the associated compiler command */ override def cmdName = "scala" - def compCmdName = "scalac" + override def cmdDesc = "code runner" + + def compCmdName = "scalac" // super.cmdName // change CompilerCommand behavior override def shouldProcessArguments: Boolean = false @@ -50,17 +51,16 @@ extends CompilerCommand(args, settings) { case Nil => AsRepl case hd :: _ => waysToRun find (_.name == settings.howtorun.value) getOrElse guessHowToRun(hd) } - private def interpolate(s: String) = s.trim.replaceAll("@cmd@", cmdName).replaceAll("@compileCmd@", compCmdName) + "\n" - - def shortUsageMsg = interpolate(""" -Usage: @cmd@ <options> [<script|class|object|jar> <arguments>] - or @cmd@ -help -All options to @compileCmd@ (see @compileCmd@ -help) are also allowed. -""") + def shortUsageMsg = +s"""|Usage: $cmdName <options> [<script|class|object|jar> <arguments>] + | or $cmdName -help + | + |All options to $compCmdName (see $compCmdName -help) are also allowed. +""".stripMargin - override def usageMsg = shortUsageMsg + interpolate(""" -The first given argument other than options to @cmd@ designates + override def usageMsg = f"""$shortUsageMsg +The first given argument other than options to $cmdName designates what to run. Runnable targets are: - a file containing scala source @@ -68,7 +68,7 @@ what to run. Runnable targets are: - a runnable jar file with a valid Main-Class attribute - or if no argument is given, the repl (interactive shell) is started -Options to @cmd@ which reach the java runtime: +Options to $cmdName which reach the java runtime: -Dname=prop passed directly to java to set system properties -J<arg> -J is stripped and <arg> passed to java as-is @@ -86,8 +86,7 @@ A file argument will be run as a scala script unless it contains only self-contained compilation units (classes and objects) and exactly one runnable main method. In that case the file will be compiled and the main method invoked. This provides a bridge between scripts and standard -scala source. - """) + "\n" +scala source.%n""" } object GenericRunnerCommand { diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 35eab94333..572e579aca 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -44,7 +44,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) with Trees with Printers with DocComments - with Positions { self => + with Positions + with Reporting { self => // the mirror -------------------------------------------------- @@ -227,20 +228,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) */ def registerTopLevelSym(sym: Symbol) {} -// ------------------ Reporting ------------------------------------- - - // not deprecated yet, but a method called "error" imported into - // nearly every trait really must go. For now using globalError. - def error(msg: String) = globalError(msg) - - override def inform(msg: String) = inform(NoPosition, msg) - override def globalError(msg: String) = globalError(NoPosition, msg) - override def warning(msg: String) = warning(NoPosition, msg) - override def deprecationWarning(pos: Position, msg: String) = currentUnit.deprecationWarning(pos, msg) - - def globalError(pos: Position, msg: String) = reporter.error(pos, msg) - def warning(pos: Position, msg: String) = if (settings.fatalWarnings) globalError(pos, msg) else reporter.warning(pos, msg) - def inform(pos: Position, msg: String) = reporter.echo(pos, msg) +// ------------------ Debugging ------------------------------------- // Getting in front of Predef's asserts to supplement with more info. // This has the happy side effect of masking the one argument forms @@ -263,12 +251,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) require(requirement, "") } - // Needs to call error to make sure the compile fails. - override def abort(msg: String): Nothing = { - error(msg) - super.abort(msg) - } - @inline final def ifDebug(body: => Unit) { if (settings.debug) body @@ -291,8 +273,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) log(s"!!!$pos_s $msg") // such warnings always at least logged } - def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg)) - def logError(msg: String, t: Throwable): Unit = () override def shouldLogAtThisPhase = settings.log.isSetByUser && ( @@ -351,9 +331,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } if (settings.verbose || settings.Ylogcp) { - // Uses the "do not truncate" inform - informComplete("[search path for source files: " + classPath.sourcepaths.mkString(",") + "]") - informComplete("[search path for class files: " + classPath.asClasspathString + "]") + reporter.echo( + s"[search path for source files: ${classPath.sourcepaths.mkString(",")}]\n"+ + s"[search path for class files: ${classPath.asClasspathString}") } // The current division between scala.reflect.* and scala.tools.nsc.* is pretty @@ -1112,45 +1092,41 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** Don't want to introduce new errors trying to report errors, * so swallow exceptions. */ - override def supplementErrorMessage(errorMessage: String): String = { - if (currentRun.supplementedError) errorMessage - else try { - currentRun.supplementedError = true - val tree = analyzer.lastTreeToTyper - val sym = tree.symbol - val tpe = tree.tpe - val site = lastSeenContext.enclClassOrMethod.owner - val pos_s = if (tree.pos.isDefined) s"line ${tree.pos.line} of ${tree.pos.source.file}" else "<unknown>" - val context_s = try { - // Taking 3 before, 3 after the fingered line. - val start = 0 max (tree.pos.line - 3) - val xs = scala.reflect.io.File(tree.pos.source.file.file).lines drop start take 7 - val strs = xs.zipWithIndex map { case (line, idx) => f"${start + idx}%6d $line" } - strs.mkString("== Source file context for tree position ==\n\n", "\n", "") - } - catch { case t: Exception => devWarning("" + t) ; "<Cannot read source file>" } - - val info1 = formatExplain( - "while compiling" -> currentSource.path, - "during phase" -> ( if (globalPhase eq phase) phase else "globalPhase=%s, enteringPhase=%s".format(globalPhase, phase) ), - "library version" -> scala.util.Properties.versionString, - "compiler version" -> Properties.versionString, - "reconstructed args" -> settings.recreateArgs.mkString(" ") - ) - val info2 = formatExplain( - "last tree to typer" -> tree.summaryString, - "tree position" -> pos_s, - "tree tpe" -> tpe, - "symbol" -> Option(sym).fold("null")(_.debugLocationString), - "symbol definition" -> Option(sym).fold("null")(s => s.defString + s" (a ${s.shortSymbolClass})"), - "symbol package" -> sym.enclosingPackage.fullName, - "symbol owners" -> ownerChainString(sym), - "call site" -> (site.fullLocationString + " in " + site.enclosingPackage) - ) - ("\n " + errorMessage + "\n" + info1) :: info2 :: context_s :: Nil mkString "\n\n" + override def supplementTyperState(errorMessage: String): String = try { + val tree = analyzer.lastTreeToTyper + val sym = tree.symbol + val tpe = tree.tpe + val site = lastSeenContext.enclClassOrMethod.owner + val pos_s = if (tree.pos.isDefined) s"line ${tree.pos.line} of ${tree.pos.source.file}" else "<unknown>" + val context_s = try { + // Taking 3 before, 3 after the fingered line. + val start = 0 max (tree.pos.line - 3) + val xs = scala.reflect.io.File(tree.pos.source.file.file).lines drop start take 7 + val strs = xs.zipWithIndex map { case (line, idx) => f"${start + idx}%6d $line" } + strs.mkString("== Source file context for tree position ==\n\n", "\n", "") } - catch { case _: Exception | _: TypeError => errorMessage } - } + catch { case t: Exception => devWarning("" + t) ; "<Cannot read source file>" } + + val info1 = formatExplain( + "while compiling" -> currentSource.path, + "during phase" -> ( if (globalPhase eq phase) phase else "globalPhase=%s, enteringPhase=%s".format(globalPhase, phase) ), + "library version" -> scala.util.Properties.versionString, + "compiler version" -> Properties.versionString, + "reconstructed args" -> settings.recreateArgs.mkString(" ") + ) + val info2 = formatExplain( + "last tree to typer" -> tree.summaryString, + "tree position" -> pos_s, + "tree tpe" -> tpe, + "symbol" -> Option(sym).fold("null")(_.debugLocationString), + "symbol definition" -> Option(sym).fold("null")(s => s.defString + s" (a ${s.shortSymbolClass})"), + "symbol package" -> sym.enclosingPackage.fullName, + "symbol owners" -> ownerChainString(sym), + "call site" -> (site.fullLocationString + " in " + site.enclosingPackage) + ) + ("\n " + errorMessage + "\n" + info1) :: info2 :: context_s :: Nil mkString "\n\n" + } catch { case _: Exception | _: TypeError => errorMessage } + /** The id of the currently active run */ @@ -1162,17 +1138,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) inform("[running phase " + ph.name + " on " + currentRun.size + " compilation units]") } - /** Collects for certain classes of warnings during this run. */ - class ConditionalWarning(what: String, option: Settings#BooleanSetting) { - val warnings = mutable.LinkedHashMap[Position, String]() - def warn(pos: Position, msg: String) = - if (option) reporter.warning(pos, msg) - else if (!(warnings contains pos)) warnings += ((pos, msg)) - def summarize() = - if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings)) - warning("there were %d %s warning(s); re-run with %s for details".format(warnings.size, what, option.name)) - } - def newSourceFile(code: String, filename: String = "<console>") = new BatchSourceFile(filename, code) @@ -1190,7 +1155,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** A Run is a single execution of the compiler on a set of units. */ - class Run extends RunContextApi { + class Run extends RunContextApi with RunReporting { /** Have been running into too many init order issues with Run * during erroneous conditions. Moved all these vals up to the * top of the file so at least they're not trivially null. @@ -1199,24 +1164,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) /** The currently compiled unit; set from GlobalPhase */ var currentUnit: CompilationUnit = NoCompilationUnit - // This change broke sbt; I gave it the thrilling name of uncheckedWarnings0 so - // as to recover uncheckedWarnings for its ever-fragile compiler interface. - val deprecationWarnings0 = new ConditionalWarning("deprecation", settings.deprecation) - val uncheckedWarnings0 = new ConditionalWarning("unchecked", settings.unchecked) - val featureWarnings = new ConditionalWarning("feature", settings.feature) - val inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings) - val allConditionalWarnings = List(deprecationWarnings0, uncheckedWarnings0, featureWarnings, inlinerWarnings) - - def uncheckedWarnings: List[(Position, String)] = uncheckedWarnings0.warnings.toList // used in sbt - def deprecationWarnings: List[(Position, String)] = deprecationWarnings0.warnings.toList // used in sbt - - var reportedFeature = Set[Symbol]() - - /** Has any macro expansion used a fallback during this run? */ - var seenMacroExpansionsFallingBack = false - - /** Have we already supplemented the error message of a compiler crash? */ - private[nsc] final var supplementedError = false + // used in sbt + def uncheckedWarnings: List[(Position, String)] = reporting.uncheckedWarnings + // used in sbt + def deprecationWarnings: List[(Position, String)] = reporting.deprecationWarnings private class SyncedCompilationBuffer { self => private val underlying = new mutable.ArrayBuffer[CompilationUnit] @@ -1414,6 +1365,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) refreshProgress() } + // for sbt def cancel() { reporter.cancelled = true } private def currentProgress = (phasec * size) + unitc @@ -1479,10 +1431,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) private def checkDeprecatedSettings(unit: CompilationUnit) { // issue warnings for any usage of deprecated settings settings.userSetSettings filter (_.isDeprecated) foreach { s => - unit.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get) + currentRun.reporting.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get) } if (settings.target.value.contains("jvm-1.5")) - unit.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated: use target for Java 1.6 or above.") + currentRun.reporting.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated: use target for Java 1.6 or above.") } /* An iterator returning all the units being compiled in this run */ @@ -1563,26 +1515,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } - def reportCompileErrors() { - if (!reporter.hasErrors && reporter.hasWarnings && settings.fatalWarnings) - globalError("No warnings can be incurred under -Xfatal-warnings.") - - if (reporter.hasErrors) { - for ((sym, file) <- symSource.iterator) { - sym.reset(new loaders.SourcefileLoader(file)) - if (sym.isTerm) - sym.moduleClass reset loaders.moduleClassLoader - } - } - else { - allConditionalWarnings foreach (_.summarize()) - - if (seenMacroExpansionsFallingBack) - warning("some macros could not be expanded and code fell back to overridden methods;"+ - "\nrecompiling with generated classfiles on the classpath might help.") - // todo: migrationWarnings - } - } /** Caching member symbols that are def-s in Defintions because they might change from Run to Run. */ val runDefinitions: definitions.RunDefinitions = new definitions.RunDefinitions @@ -1595,7 +1527,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def checkDeprecations() = { checkDeprecatedSettings(newCompilationUnit("")) - reportCompileErrors() + reporting.summarizeErrors() } val units = sources map scripted map (new CompilationUnit(_)) @@ -1619,7 +1551,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) checkDeprecatedSettings(unitbuf.head) globalPhase = fromPhase - while (globalPhase.hasNext && !reporter.hasErrors) { + while (globalPhase.hasNext && !reporter.hasErrors) { val startTime = currentTime phase = globalPhase globalPhase.run() @@ -1665,6 +1597,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) advancePhase() } + reporting.summarizeErrors() + if (traceSymbolActivity) units map (_.body) foreach (traceSymbols recordSymbolsInTree _) @@ -1672,8 +1606,15 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (settings.Yshow.isDefault) showMembers() - reportCompileErrors() + if (reporter.hasErrors) { + for ((sym, file) <- symSource.iterator) { + sym.reset(new loaders.SourcefileLoader(file)) + if (sym.isTerm) + sym.moduleClass reset loaders.moduleClassLoader + } + } symSource.keys foreach (x => resetPackageClass(x.owner)) + informTime("total", startTime) // Clear any sets or maps created via perRunCaches. diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala new file mode 100644 index 0000000000..0263586418 --- /dev/null +++ b/src/compiler/scala/tools/nsc/Reporting.scala @@ -0,0 +1,123 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2014 LAMP/EPFL, Typesafe Inc. + * @author Adriaan Moors + */ + +package scala +package tools +package nsc + +import reporters.{ Reporter, ConsoleReporter } +import scala.collection.{ mutable, immutable } +import scala.reflect.internal.util.StringOps.countElementsAsString + +/** Provides delegates to the reporter doing the actual work. + * PerRunReporting implements per-Run stateful info tracking and reporting + * + * TODO: make reporting configurable + */ +trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions with CompilationUnits with scala.reflect.internal.Symbols => + def settings: Settings + + // not deprecated yet, but a method called "error" imported into + // nearly every trait really must go. For now using globalError. + def error(msg: String) = globalError(msg) + + // a new instance of this class is created for every Run (access the current instance via `currentRun.reporting`) + protected def PerRunReporting = new PerRunReporting + class PerRunReporting extends PerRunReportingBase { + /** Collects for certain classes of warnings during this run. */ + private class ConditionalWarning(what: String, option: Settings#BooleanSetting) { + val warnings = mutable.LinkedHashMap[Position, String]() + def warn(pos: Position, msg: String) = + if (option) reporter.warning(pos, msg) + else if (!(warnings contains pos)) warnings += ((pos, msg)) + def summarize() = + if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings)) { + val numWarnings = warnings.size + val warningVerb = if (numWarnings == 1) "was" else "were" + val warningCount = countElementsAsString(numWarnings, s"$what warning") + + reporter.warning(NoPosition, s"there $warningVerb $warningCount; re-run with ${option.name} for details") + } + } + + // This change broke sbt; I gave it the thrilling name of uncheckedWarnings0 so + // as to recover uncheckedWarnings for its ever-fragile compiler interface. + private val _deprecationWarnings = new ConditionalWarning("deprecation", settings.deprecation) + private val _uncheckedWarnings = new ConditionalWarning("unchecked", settings.unchecked) + private val _featureWarnings = new ConditionalWarning("feature", settings.feature) + private val _inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings) + private val _allConditionalWarnings = List(_deprecationWarnings, _uncheckedWarnings, _featureWarnings, _inlinerWarnings) + + // TODO: remove in favor of the overload that takes a Symbol, give that argument a default (NoSymbol) + def deprecationWarning(pos: Position, msg: String): Unit = _deprecationWarnings.warn(pos, msg) + def uncheckedWarning(pos: Position, msg: String): Unit = _uncheckedWarnings.warn(pos, msg) + def featureWarning(pos: Position, msg: String): Unit = _featureWarnings.warn(pos, msg) + def inlinerWarning(pos: Position, msg: String): Unit = _inlinerWarnings.warn(pos, msg) + + def deprecationWarnings = _deprecationWarnings.warnings.toList + def uncheckedWarnings = _uncheckedWarnings.warnings.toList + def featureWarnings = _featureWarnings.warnings.toList + def inlinerWarnings = _inlinerWarnings.warnings.toList + + def allConditionalWarnings = _allConditionalWarnings flatMap (_.warnings) + + // behold! the symbol that caused the deprecation warning (may not be deprecated itself) + def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = _deprecationWarnings.warn(pos, msg) + def deprecationWarning(pos: Position, sym: Symbol): Unit = { + val suffix = sym.deprecationMessage match { case Some(msg) => ": "+ msg case _ => "" } + deprecationWarning(pos, sym, s"$sym${sym.locationString} is deprecated$suffix") + } + + private[this] var reportedFeature = Set[Symbol]() + def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = { + val req = if (required) "needs to" else "should" + val fqname = "scala.language." + featureName + val explain = ( + if (reportedFeature contains featureTrait) "" else + s"""| + |This can be achieved by adding the import clause 'import $fqname' + |or by setting the compiler option -language:$featureName. + |See the Scala docs for value $fqname for a discussion + |why the feature $req be explicitly enabled.""".stripMargin + ) + reportedFeature += featureTrait + + val msg = s"$featureDesc $req be enabled\nby making the implicit value $fqname visible.$explain" replace ("#", construct) + if (required) reporter.error(pos, msg) + else featureWarning(pos, msg) + } + + /** Has any macro expansion used a fallback during this run? */ + var seenMacroExpansionsFallingBack = false + + def summarizeErrors(): Unit = if (!reporter.hasErrors) { + _allConditionalWarnings foreach (_.summarize()) + + if (seenMacroExpansionsFallingBack) + reporter.warning(NoPosition, "some macros could not be expanded and code fell back to overridden methods;"+ + "\nrecompiling with generated classfiles on the classpath might help.") + + // todo: migrationWarnings + + if (settings.fatalWarnings && reporter.hasWarnings) + reporter.error(NoPosition, "No warnings can be incurred under -Xfatal-warnings.") + } + + // for repl + private[this] var incompleteHandler: (Position, String) => Unit = null + def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = { + val saved = incompleteHandler + incompleteHandler = handler + try thunk + finally incompleteHandler = saved + } + + def incompleteHandled = incompleteHandler != null + def incompleteInputError(pos: Position, msg: String): Unit = + if (incompleteHandled) incompleteHandler(pos, msg) + else reporter.error(pos, msg) + + } +}
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ffc45b21ea..883fd31dbc 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -205,11 +205,11 @@ self => override def newScanner() = new UnitScanner(unit, patches) override def warning(offset: Offset, msg: String) { - unit.warning(o2p(offset), msg) + reporter.warning(o2p(offset), msg) } override def deprecationWarning(offset: Offset, msg: String) { - unit.deprecationWarning(o2p(offset), msg) + currentRun.reporting.deprecationWarning(o2p(offset), msg) } private var smartParsing = false @@ -224,17 +224,17 @@ self => val syntaxErrors = new ListBuffer[(Int, String)] def showSyntaxErrors() = for ((offset, msg) <- syntaxErrors) - unit.error(o2p(offset), msg) + reporter.error(o2p(offset), msg) override def syntaxError(offset: Offset, msg: String) { if (smartParsing) syntaxErrors += ((offset, msg)) - else unit.error(o2p(offset), msg) + else reporter.error(o2p(offset), msg) } override def incompleteInputError(msg: String) { val offset = source.content.length - 1 if (smartParsing) syntaxErrors += ((offset, msg)) - else unit.incompleteInputError(o2p(offset), msg) + else currentRun.reporting.incompleteInputError(o2p(offset), msg) } /** parse unit. If there are inbalanced braces, diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index e8d46704c3..572497ac90 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -1259,9 +1259,9 @@ trait Scanners extends ScannersCommon { class UnitScanner(val unit: CompilationUnit, patches: List[BracePatch]) extends SourceFileScanner(unit.source) { def this(unit: CompilationUnit) = this(unit, List()) - override def deprecationWarning(off: Offset, msg: String) = unit.deprecationWarning(unit.position(off), msg) - override def error (off: Offset, msg: String) = unit.error(unit.position(off), msg) - override def incompleteInputError(off: Offset, msg: String) = unit.incompleteInputError(unit.position(off), msg) + override def deprecationWarning(off: Offset, msg: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg) + override def error (off: Offset, msg: String) = reporter.error(unit.position(off), msg) + override def incompleteInputError(off: Offset, msg: String) = currentRun.reporting.incompleteInputError(unit.position(off), msg) private var bracePatches: List[BracePatch] = patches diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index 3a695c6f59..64b762696e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -83,7 +83,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse private def initialUnitBody(unit: CompilationUnit): Tree = { if (unit.isJava) new JavaUnitParser(unit).parse() - else if (global.reporter.incompleteHandled) newUnitParser(unit).parse() + else if (currentRun.reporting.incompleteHandled) newUnitParser(unit).parse() else newUnitParser(unit).smartParse() } diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 0da66d43f7..d9f56b47fa 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -680,7 +680,7 @@ abstract class GenICode extends SubComponent { val dims = arr.dimensions var elemKind = arr.elementKind if (args.length > dims) - unit.error(tree.pos, "too many arguments for array constructor: found " + args.length + + reporter.error(tree.pos, "too many arguments for array constructor: found " + args.length + " but array has only " + dims + " dimension(s)") if (args.length != dims) for (i <- args.length until dims) elemKind = ARRAY(elemKind) @@ -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 index 856f85d9e3..2af2037fec 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala @@ -8,6 +8,7 @@ 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 { @@ -50,4 +51,11 @@ object AsmUtils { 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 3d1b646069..4583462b71 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, @@ -503,7 +504,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case nextCleanup :: rest => if (saveReturnValue) { if (insideCleanupBlock) { - cunit.warning(r.pos, "Return statement found in finally-clause, discarding its return-value in favor of that of a more deeply nested return.") + reporter.warning(r.pos, "Return statement found in finally-clause, discarding its return-value in favor of that of a more deeply nested return.") bc drop returnType } else { // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. @@ -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,34 +593,34 @@ 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)") + reporter.error(app.pos, s"too many arguments for array constructor: found ${args.length} but array has only $dims dimension(s)") } if (argsSize < dims) { /* In one step: * 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,12 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { case _ => } if ((targetTypeKind != null) && (sym == definitions.Array_clone) && invokeStyle.isDynamic) { - val target: String = targetTypeKind.getInternalName + // An invokevirtual points to a CONSTANT_Methodref_info which in turn points to a + // CONSTANT_Class_info of the receiver type. + // The JVMS is not explicit about this, but that receiver type may be an array type + // descriptor (instead of a class internal name): + // invokevirtual #2; //Method "[I".clone:()Ljava/lang/Object + val target: String = targetTypeKind.asRefBType.classOrArrayType bc.invokevirtual(target, "clone", "()Ljava/lang/Object;") } else { @@ -694,7 +702,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { genNormalMethodCall() - generatedType = asmMethodType(sym).getReturnType + generatedType = asmMethodType(sym).returnType } } @@ -706,7 +714,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 @@ -876,12 +884,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 +902,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 +967,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 +1033,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 +1054,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 +1139,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 1ede914288..31a392ed55 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 @@ -38,7 +38,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { outputDirectory(csym) } catch { case ex: Throwable => - cunit.error(cunit.body.pos, s"Couldn't create file for class $cName\n${ex.getMessage}") + reporter.error(cunit.body.pos, s"Couldn't create file for class $cName\n${ex.getMessage}") null } } @@ -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") @@ -139,7 +141,7 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { */ def apply(sym: Symbol, csymCompUnit: CompilationUnit): Boolean = { def fail(msg: String, pos: Position = sym.pos) = { - csymCompUnit.warning(sym.pos, + reporter.warning(sym.pos, sym.name + s" has a main method with parameter type Array[String], but ${sym.fullName('.')} will not be a runnable program.\n Reason: $msg" // TODO: make this next claim true, if possible @@ -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 } } @@ -1112,17 +1114,17 @@ abstract class BCodeHelpers extends BCodeTypes with BytecodeWriters { // 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 ee9be5b11c..8845ffa0cd 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -25,6 +25,7 @@ import java.io.PrintWriter */ abstract class BCodeSkelBuilder extends BCodeHelpers { import global._ + import bTypes._ /* * There's a dedicated PlainClassBuilder for each CompilationUnit, @@ -133,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) @@ -142,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 @@ -166,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) @@ -234,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) @@ -261,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 ) @@ -397,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 } @@ -531,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, @@ -555,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) @@ -569,7 +570,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { if (params.size > MaximumJvmParameters) { // SI-7324 - cunit.error(methSymbol.pos, s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.") + reporter.error(methSymbol.pos, s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.") return } @@ -685,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 @@ -694,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", @@ -706,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) } @@ -723,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..5b0fa6f7a8 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala @@ -0,0 +1,417 @@ +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. Also used for emitting invokevirtual calls to + * (a: Array[T]).clone() for any T, see genApply. + * + * 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/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index b7f9b30e19..b0fb3069c1 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -113,7 +113,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // Warn when classes will overwrite one another on case-insensitive systems. for ((_, v1 :: v2 :: _) <- sortedClasses groupBy (_.symbol.javaClassName.toString.toLowerCase)) { - v1.cunit.warning(v1.symbol.pos, + reporter.warning(v1.symbol.pos, s"Class ${v1.symbol.javaClassName} differs only in case from ${v2.symbol.javaClassName}. " + "Such classes will overwrite one another on case-insensitive filesystems.") } @@ -141,7 +141,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { try emitFor(c) catch { case e: FileConflictException => - c.cunit.error(c.symbol.pos, s"error writing ${c.symbol}: ${e.getMessage}") + reporter.error(c.symbol.pos, s"error writing ${c.symbol}: ${e.getMessage}") } sortedClasses = sortedClasses.tail classes -= c.symbol // GC opportunity @@ -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; @@ -1362,7 +1363,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { if (m.symbol.isStaticConstructor || definitions.isGetClass(m.symbol)) return if (m.params.size > MaximumJvmParameters) { - getCurrentCUnit().error(m.symbol.pos, s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.") + reporter.error(m.symbol.pos, s"Platform restriction: a parameter list's length cannot exceed $MaximumJvmParameters.") return } @@ -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 @@ -3241,7 +3245,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } if(!isValidSignature) { - unit.warning(sym.pos, + reporter.warning(sym.pos, """|compiler bug: created invalid generic signature for %s in %s |signature: %s |if this is reproducible, please report bug at https://issues.scala-lang.org/ @@ -3254,7 +3258,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { val normalizedTpe = enteringErasure(erasure.prepareSigMap(memberTpe)) val bytecodeTpe = owner.thisType.memberInfo(sym) if (!sym.isType && !sym.isConstructor && !(erasure.erasure(sym)(normalizedTpe) =:= bytecodeTpe)) { - unit.warning(sym.pos, + reporter.warning(sym.pos, """|compiler bug: created generic signature for %s in %s that does not conform to its erasure |signature: %s |original type: %s diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala index 61cf76f524..a401de05e5 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala @@ -156,7 +156,7 @@ abstract class GenBCode extends BCodeSyncAndTry { case None => caseInsensitively.put(lowercaseJavaClassName, claszSymbol) case Some(dupClassSym) => - item.cunit.warning( + reporter.warning( claszSymbol.pos, s"Class ${claszSymbol.javaClassName} differs only in case from ${dupClassSym.javaClassName}. " + "Such classes will overwrite one another on case-insensitive filesystems." @@ -244,14 +244,9 @@ abstract class GenBCode extends BCodeSyncAndTry { val beanC = if (bean == null) null else SubItem3(bean.name, getByteArray(bean)) if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) { - def readClass(bytes: Array[Byte]): asm.tree.ClassNode = { - val node = new asm.tree.ClassNode() - new asm.ClassReader(bytes).accept(node, 0) - node - } - if (mirrorC != null) AsmUtils.traceClass(readClass(mirrorC.jclassBytes)) - AsmUtils.traceClass(readClass(plainC.jclassBytes)) - if (beanC != null) AsmUtils.traceClass(readClass(beanC.jclassBytes)) + 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/jvm/GenJVMASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala index 01c4ff5a52..2bcde7f7b9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala @@ -27,7 +27,7 @@ trait GenJVMASM { protected def isJavaEntryPoint(icls: IClass) = { val sym = icls.symbol def fail(msg: String, pos: Position = sym.pos) = { - icls.cunit.warning(sym.pos, + reporter.warning(sym.pos, sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" + " Reason: " + msg // TODO: make this next claim true, if possible 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/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala index 235e954f88..425c10d153 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala @@ -182,7 +182,7 @@ abstract class InlineExceptionHandlers extends SubComponent { // in other words: what's on the stack MUST conform to what's in the THROW(..)! if (!canReplaceHandler) { - currentClass.cunit.warning(NoPosition, "Unable to inline the exception handler inside incorrect" + + reporter.warning(NoPosition, "Unable to inline the exception handler inside incorrect" + " block:\n" + bblock.iterator.mkString("\n") + "\nwith stack: " + typeInfo + " just " + "before instruction index " + index) } @@ -383,7 +383,7 @@ abstract class InlineExceptionHandlers extends SubComponent { Some((exceptionLocal, copy)) case _ => - currentClass.cunit.warning(NoPosition, "Unable to inline the exception handler due to incorrect format:\n" + + reporter.warning(NoPosition, "Unable to inline the exception handler due to incorrect format:\n" + handler.iterator.mkString("\n")) None } diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index f6de522d09..8df3969c49 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -195,7 +195,7 @@ abstract class Inliners extends SubComponent { /** The current iclass */ private var currentIClazz: IClass = _ - private def warn(pos: Position, msg: String) = currentIClazz.cunit.inlinerWarning(pos, msg) + private def warn(pos: Position, msg: String) = currentRun.reporting.inlinerWarning(pos, msg) private def ownedName(sym: Symbol): String = exitingUncurry { val count = ( diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index a61ad392ee..37b00aa9a3 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -26,10 +26,10 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { def freshName(prefix: String): Name = freshTermName(prefix) def freshTermName(prefix: String): TermName = unit.freshTermName(prefix) def freshTypeName(prefix: String): TypeName = unit.freshTypeName(prefix) - def deprecationWarning(off: Int, msg: String) = unit.deprecationWarning(off, msg) + def deprecationWarning(off: Int, msg: String) = currentRun.reporting.deprecationWarning(off, msg) implicit def i2p(offset : Int) : Position = Position.offset(unit.source, offset) - def warning(pos : Int, msg : String) : Unit = unit.warning(pos, msg) - def syntaxError(pos: Int, msg: String) : Unit = unit.error(pos, msg) + def warning(pos : Int, msg : String) : Unit = reporter.warning(pos, msg) + def syntaxError(pos: Int, msg: String) : Unit = reporter.error(pos, msg) } abstract class JavaParser extends ParserCommon { diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index c5401219dd..bddcf6567c 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -860,9 +860,9 @@ trait JavaScanners extends ast.parser.ScannersCommon { class JavaUnitScanner(unit: CompilationUnit) extends JavaScanner { in = new JavaCharArrayReader(unit.source.content, !settings.nouescape.value, syntaxError) init() - def error (pos: Int, msg: String) = unit. error(pos, msg) - def incompleteInputError(pos: Int, msg: String) = unit.incompleteInputError(pos, msg) - def deprecationWarning(pos: Int, msg: String) = unit.deprecationWarning(pos, msg) + def error (pos: Int, msg: String) = reporter.error(pos, msg) + def incompleteInputError(pos: Int, msg: String) = currentRun.reporting.incompleteInputError(pos, msg) + def deprecationWarning(pos: Int, msg: String) = currentRun.reporting.deprecationWarning(pos, msg) implicit def g2p(pos: Int): Position = Position.offset(unit.source, pos) } } diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index 16d432438a..6c592ead0d 100644 --- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -62,12 +62,13 @@ abstract class AbstractReporter extends Reporter { */ private def testAndLog(pos: Position, severity: Severity, msg: String): Boolean = pos != null && pos.isDefined && { - val fpos = pos.focus + val fpos = pos.focus val suppress = positions(fpos) match { - case ERROR => true // already error at position - case highest if highest > severity => true // already message higher than present severity - case `severity` => messages(fpos) contains msg // already issued this exact message - case _ => false // good to go + case ERROR => true // already error at position + case highest + if highest.id > severity.id => true // already message higher than present severity + case `severity` => messages(fpos) contains msg // already issued this exact message + case _ => false // good to go } suppress || { diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index 3f210a543c..0b218b711c 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -12,8 +12,7 @@ import scala.reflect.internal.util._ import StringOps._ /** - * This class implements a Reporter that displays messages on a text - * console. + * This class implements a Reporter that displays messages on a text console. */ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: PrintWriter) extends AbstractReporter { def this(settings: Settings) = this(settings, Console.in, new PrintWriter(Console.err, true)) diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index 68362c066d..5b576a547d 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -8,76 +8,50 @@ package reporters import scala.reflect.internal.util._ -/** - * This interface provides methods to issue information, warning and - * error messages. +/** Report information, warnings and errors. + * + * This describes the internal interface for issuing information, warnings and errors. + * The only abstract method in this class must be info0. + * + * TODO: Move external clients (sbt/ide/partest) to reflect.internal.Reporter + * This interface should be considered private to the compiler. */ -abstract class Reporter { - protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit - - object severity extends Enumeration - class Severity(val id: Int) extends severity.Value { - var count: Int = 0 - } - val INFO = new Severity(0) { - override def toString: String = "INFO" - } - val WARNING = new Severity(1) { - override def toString: String = "WARNING" - } - val ERROR = new Severity(2) { - override def toString: String = "ERROR" - } - - /** Whether very long lines can be truncated. This exists so important - * debugging information (like printing the classpath) is not rendered - * invisible due to the max message length. - */ - private var _truncationOK: Boolean = true - def truncationOK = _truncationOK - def withoutTruncating[T](body: => T): T = { - val saved = _truncationOK - _truncationOK = false - try body - finally _truncationOK = saved - } - - private var incompleteHandler: (Position, String) => Unit = null - def incompleteHandled = incompleteHandler != null - def withIncompleteHandler[T](handler: (Position, String) => Unit)(thunk: => T) = { - val saved = incompleteHandler - incompleteHandler = handler - try thunk - finally incompleteHandler = saved - } - - var cancelled = false - def hasErrors = ERROR.count > 0 || cancelled - def hasWarnings = WARNING.count > 0 +abstract class Reporter extends scala.reflect.internal.Reporter { + /** Informational messages. If `!force`, they may be suppressed. */ + final def info(pos: Position, msg: String, force: Boolean): Unit = info0(pos, msg, INFO, force) /** For sending a message which should not be labeled as a warning/error, * but also shouldn't require -verbose to be visible. */ - def echo(msg: String): Unit = info(NoPosition, msg, force = true) - def echo(pos: Position, msg: String): Unit = info(pos, msg, force = true) + def echo(msg: String): Unit = info(NoPosition, msg, force = true) - /** Informational messages, suppressed unless -verbose or force=true. */ - def info(pos: Position, msg: String, force: Boolean): Unit = info0(pos, msg, INFO, force) + // overridden by sbt, IDE -- should not be in the reporting interface + // (IDE receives comments from ScaladocAnalyzer using this hook method) + // TODO: IDE should override a hook method in the parser instead + def comment(pos: Position, msg: String): Unit = {} - /** Warnings and errors. */ - def warning(pos: Position, msg: String): Unit = withoutTruncating(info0(pos, msg, WARNING, force = false)) - def error(pos: Position, msg: String): Unit = withoutTruncating(info0(pos, msg, ERROR, force = false)) - def incompleteInputError(pos: Position, msg: String): Unit = { - if (incompleteHandled) incompleteHandler(pos, msg) - else error(pos, msg) - } + // used by sbt (via unit.cancel) to cancel a compile (see hasErrors) + // TODO: figure out how sbt uses this, come up with a separate interface for controlling the build + var cancelled: Boolean = false - def comment(pos: Position, msg: String) { } - def flush() { } - def reset() { - INFO.count = 0 - ERROR.count = 0 - WARNING.count = 0 - cancelled = false + override def hasErrors: Boolean = super.hasErrors || cancelled + + override def reset(): Unit = { + super.reset() + cancelled = false } + + // the below is copy/pasted from ReporterImpl for now + // partest expects this inner class + // TODO: rework partest to use the scala.reflect.internal interface, + // remove duplication here, and consolidate reflect.internal.{ReporterImpl & ReporterImpl} + class Severity(val id: Int)(name: String) { var count: Int = 0 ; override def toString = name} + object INFO extends Severity(0)("INFO") + object WARNING extends Severity(1)("WARNING") + // reason for copy/paste: this is used by partest (must be a val, not an object) + // TODO: use count(ERROR) in scala.tools.partest.nest.DirectCompiler#errorCount, rather than ERROR.count + lazy val ERROR = new Severity(2)("ERROR") + + def count(severity: Severity): Int = severity.count + def resetCount(severity: Severity): Unit = severity.count = 0 } diff --git a/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala b/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala index 04c5bdf824..24a61cb171 100644 --- a/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/StoreReporter.scala @@ -10,8 +10,7 @@ import scala.collection.mutable import scala.reflect.internal.util.Position /** - * This class implements a Reporter that displays messages on a text - * console. + * This class implements a Reporter that stores its reports in the set `infos`. */ class StoreReporter extends Reporter { case class Info(pos: Position, msg: String, severity: Severity) { diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 20ccc30ff6..d22dcacad6 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -42,7 +42,7 @@ trait ScalaSettings extends AbsScalaSettings def optimiseSettings = List[BooleanSetting](inline, inlineHandlers, Xcloselim, Xdce, YconstOptimization) /** If any of these settings is enabled, the compiler should print a message and exit. */ - def infoSettings = List[Setting](help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph) + def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph) /** Is an info setting set? */ def isInfo = infoSettings exists (_.isSetByUser) diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 6ca2205881..149b4fe446 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -780,32 +780,40 @@ abstract class ICodeReader extends ClassfileParser { bb = otherBlock // Console.println("\t> entering bb: " + bb) } - instr match { - case LJUMP(target) => - otherBlock = blocks(target) - bb.emitOnly(JUMP(otherBlock)) - case LCJUMP(success, failure, cond, kind) => - otherBlock = blocks(success) - val failBlock = blocks(failure) - bb.emitOnly(CJUMP(otherBlock, failBlock, cond, kind)) + if (bb.closed) { + // the basic block is closed, i.e. the previous instruction was a jump, return or throw, + // but the next instruction is not a jump target. this means that the next instruction is + // dead code. we can therefore advance until the next jump target. + debuglog(s"ICode reader skipping dead instruction $instr in classfile $instanceCode") + } else { + instr match { + case LJUMP(target) => + otherBlock = blocks(target) + bb.emitOnly(JUMP(otherBlock)) + + case LCJUMP(success, failure, cond, kind) => + otherBlock = blocks(success) + val failBlock = blocks(failure) + bb.emitOnly(CJUMP(otherBlock, failBlock, cond, kind)) - case LCZJUMP(success, failure, cond, kind) => - otherBlock = blocks(success) - val failBlock = blocks(failure) - bb.emitOnly(CZJUMP(otherBlock, failBlock, cond, kind)) + case LCZJUMP(success, failure, cond, kind) => + otherBlock = blocks(success) + val failBlock = blocks(failure) + bb.emitOnly(CZJUMP(otherBlock, failBlock, cond, kind)) - case LSWITCH(tags, targets) => - bb.emitOnly(SWITCH(tags, targets map blocks)) + case LSWITCH(tags, targets) => + bb.emitOnly(SWITCH(tags, targets map blocks)) - case RETURN(_) => - bb emitOnly instr + case RETURN(_) => + bb emitOnly instr - case THROW(clasz) => - bb emitOnly instr + case THROW(clasz) => + bb emitOnly instr - case _ => - bb emit instr + case _ => + bb emit instr + } } } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 592c5497b5..25e13a1314 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -69,7 +69,7 @@ abstract class Pickler extends SubComponent { // OPT: do this only as a recovery after fatal error. Checking in advance was expensive. if (t.isErroneous) { if (settings.debug) e.printStackTrace() - unit.error(t.pos, "erroneous or inaccessible type") + reporter.error(t.pos, "erroneous or inaccessible type") return } } @@ -186,7 +186,16 @@ abstract class Pickler extends SubComponent { val (locals, globals) = sym.children partition (_.isLocalClass) val children = if (locals.isEmpty) globals - else globals + sym.newClassWithInfo(tpnme.LOCAL_CHILD, List(sym.tpe), EmptyScope, pos = sym.pos) + else { + // The LOCAL_CHILD was introduced in 12a2b3b to fix Aladdin bug 1055. When a sealed + // class/trait has local subclasses, a single <local child> class symbol is added + // as pickled child (instead of a reference to the anonymous class; that was done + // initially, but seems not to work, as the bug shows). + // Adding the LOCAL_CHILD is necessary to retain exhaustivity warnings under separate + // compilation. See test neg/aladdin1055. + val parents = (if (sym.isTrait) List(definitions.ObjectTpe) else Nil) ::: List(sym.tpe) + globals + sym.newClassWithInfo(tpnme.LOCAL_CHILD, parents, EmptyScope, pos = sym.pos) + } putChildren(sym, children.toList sortBy (_.sealedSortName)) } diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index f14fce5de9..1664fe0e0d 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -76,7 +76,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { val qual0 = ad.qual val params = ad.args if (settings.logReflectiveCalls) - unit.echo(ad.pos, "method invocation uses reflection") + reporter.echo(ad.pos, "method invocation uses reflection") val typedPos = typedWithPos(ad.pos) _ @@ -360,13 +360,13 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL { assert(params.length == mparams.length, ((params, mparams))) (mparams, resType) case tpe @ OverloadedType(pre, alts) => - unit.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe)) + reporter.warning(ad.pos, s"Overloaded type reached the backend! This is a bug in scalac.\n Symbol: ${ad.symbol}\n Overloads: $tpe\n Arguments: " + ad.args.map(_.tpe)) alts filter (_.paramss.flatten.size == params.length) map (_.tpe) match { case mt @ MethodType(mparams, resType) :: Nil => - unit.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt) + reporter.warning(NoPosition, "Only one overload has the right arity, proceeding with overload " + mt) (mparams, resType) case _ => - unit.error(ad.pos, "Cannot resolve overload.") + reporter.error(ad.pos, "Cannot resolve overload.") (Nil, NoType) } } diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 391bce5abb..f471440293 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -54,7 +54,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { def check(tree: Tree) = { for (t <- tree) t match { case t: RefTree if uninitializedVals(t.symbol.accessedOrSelf) && t.qualifier.symbol == clazz => - unit.warning(t.pos, s"Reference to uninitialized ${t.symbol.accessedOrSelf}") + reporter.warning(t.pos, s"Reference to uninitialized ${t.symbol.accessedOrSelf}") case _ => } } @@ -685,7 +685,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL { // mangling before we introduce more of it. val conflict = clazz.info.nonPrivateMember(acc.name) filter (s => s.isGetter && !s.isOuterField && s.enclClass.isTrait) if (conflict ne NoSymbol) - unit.error(acc.pos, "parameter '%s' requires field but conflicts with %s".format(acc.name, conflict.fullLocationString)) + reporter.error(acc.pos, "parameter '%s' requires field but conflicts with %s".format(acc.name, conflict.fullLocationString)) copyParam(acc, parameter(acc)) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index e036035397..ec4deb6be0 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -488,7 +488,7 @@ abstract class Erasure extends AddInterfaces || (checkBridgeOverrides(member, other, bridge) match { case Nil => true case es if member.owner.isAnonymousClass => resolveAnonymousBridgeClash(member, bridge); true - case es => for ((pos, msg) <- es) unit.error(pos, msg); false + case es => for ((pos, msg) <- es) reporter.error(pos, msg); false }) ) @@ -724,7 +724,7 @@ abstract class Erasure extends AddInterfaces ) val when = if (exitingRefchecks(lowType matches highType)) "" else " after erasure: " + exitingPostErasure(highType) - unit.error(pos, + reporter.error(pos, s"""|$what: |${exitingRefchecks(highString)} and |${exitingRefchecks(lowString)} @@ -865,7 +865,7 @@ abstract class Erasure extends AddInterfaces fn match { case TypeApply(sel @ Select(qual, name), List(targ)) => if (qual.tpe != null && isPrimitiveValueClass(qual.tpe.typeSymbol) && targ.tpe != null && targ.tpe <:< AnyRefTpe) - unit.error(sel.pos, "isInstanceOf cannot test if value types are references.") + reporter.error(sel.pos, "isInstanceOf cannot test if value types are references.") def mkIsInstanceOf(q: () => Tree)(tp: Type): Tree = Apply( @@ -952,7 +952,7 @@ abstract class Erasure extends AddInterfaces case nme.length => nme.array_length case nme.update => nme.array_update case nme.clone_ => nme.array_clone - case _ => unit.error(tree.pos, "Unexpected array member, no translation exists.") ; nme.NO_NAME + case _ => reporter.error(tree.pos, "Unexpected array member, no translation exists.") ; nme.NO_NAME } gen.mkRuntimeCall(arrayMethodName, qual :: args) } @@ -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. + reporter.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/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 0447e23e9e..c291961447 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -481,7 +481,7 @@ abstract class ExplicitOuter extends InfoTransform // since we can't fix SI-4440 properly (we must drop the outer accessors of final classes when there's no immediate reference to them in sight) // at least don't crash... this duplicates maybeOmittable from constructors (acc.owner.isEffectivelyFinal && !acc.isOverridingSymbol)) { - unit.uncheckedWarning(tree.pos, "The outer reference in this type test cannot be checked at run time.") + currentRun.reporting.uncheckedWarning(tree.pos, "The outer reference in this type test cannot be checked at run time.") transform(TRUE) // urgh... drop condition if there's no accessor (or if it may disappear after constructors) } else { // println("(base, acc)= "+(base, acc)) diff --git a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala index 2235a93ca4..228c9da624 100644 --- a/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala +++ b/src/compiler/scala/tools/nsc/transform/ExtensionMethods.scala @@ -127,7 +127,7 @@ abstract class ExtensionMethods extends Transform with TypingTransformers { def checkNonCyclic(pos: Position, seen: Set[Symbol], clazz: Symbol): Unit = if (seen contains clazz) - unit.error(pos, "value class may not unbox to itself") + reporter.error(pos, "value class may not unbox to itself") else { val unboxed = definitions.underlyingOfValueClass(clazz).typeSymbol if (unboxed.isDerivedValueClass) checkNonCyclic(pos, seen + clazz, unboxed) diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index e38c034f4d..f85d8222f0 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -339,7 +339,7 @@ abstract class LambdaLift extends InfoTransform { if (clazz.isStaticOwner) clazz.fullLocationString else s"the unconstructed `this` of ${clazz.fullLocationString}" val msg = s"Implementation restriction: access of ${sym.fullLocationString} from ${currentClass.fullLocationString}, would require illegal premature access to $what" - currentUnit.error(curTree.pos, msg) + reporter.error(curTree.pos, msg) } val qual = if (clazz == currentClass) gen.mkAttributedThis(clazz) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 33e8e47c76..7927875583 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -336,7 +336,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { rebindSuper(clazz, mixinMember.alias, mixinClass) match { case NoSymbol => - unit.error(clazz.pos, "Member %s of mixin %s is missing a concrete super implementation.".format( + reporter.error(clazz.pos, "Member %s of mixin %s is missing a concrete super implementation.".format( mixinMember.alias, mixinClass)) case alias1 => superAccessor.asInstanceOf[TermSymbol] setAlias alias1 @@ -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/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 714f189ead..ef534f70fd 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -96,7 +96,7 @@ abstract class TailCalls extends Transform { val failReason = failReasons(ctx) val failPos = failPositions(ctx) - unit.error(failPos, s"could not optimize @tailrec annotated $method: $failReason") + reporter.error(failPos, s"could not optimize @tailrec annotated $method: $failReason") } /** Has the label been accessed? Then its symbol is in this set. */ @@ -268,14 +268,14 @@ abstract class TailCalls extends Transform { tree match { case ValDef(_, _, _, _) => if (tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass)) - unit.error(tree.pos, "lazy vals are not tailcall transformed") + reporter.error(tree.pos, "lazy vals are not tailcall transformed") super.transform(tree) case dd @ DefDef(_, name, _, vparamss0, _, rhs0) if isEligible(dd) => val newCtx = new DefDefTailContext(dd) if (newCtx.isMandatory && !(newCtx containsRecursiveCall rhs0)) - unit.error(tree.pos, "@tailrec annotated method contains no recursive calls") + reporter.error(tree.pos, "@tailrec annotated method contains no recursive calls") debuglog(s"Considering $name for tailcalls, with labels in tailpos: ${newCtx.tailLabels}") val newRHS = transform(rhs0, newCtx) @@ -328,11 +328,14 @@ abstract class TailCalls extends Transform { ) case CaseDef(pat, guard, body) => + // CaseDefs are already translated and guards were moved into the body. + // If this was not the case, guards would have to be transformed here as well. + assert(guard.isEmpty) deriveCaseDef(tree)(transform) case If(cond, thenp, elsep) => treeCopy.If(tree, - cond, + noTailTransform(cond), transform(thenp), transform(elsep) ) @@ -363,7 +366,7 @@ abstract class TailCalls extends Transform { rewriteApply(tapply, fun, targs, vargs) case Apply(fun, args) if fun.symbol == Boolean_or || fun.symbol == Boolean_and => - treeCopy.Apply(tree, fun, transformTrees(args)) + treeCopy.Apply(tree, noTailTransform(fun), transformTrees(args)) // this is to detect tailcalls in translated matches // it's a one-argument call to a label that is in a tailposition and that looks like label(x) {x} diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index d77c6b54a9..2209aac00f 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -93,7 +93,7 @@ abstract class UnCurry extends InfoTransform override def transform(tree: Tree): Tree = ( try postTransform(mainTransform(tree)) catch { case ex: TypeError => - unit.error(ex.pos, ex.msg) + reporter.error(ex.pos, ex.msg) debugStack(ex) EmptyTree } @@ -174,7 +174,7 @@ abstract class UnCurry extends InfoTransform cdef <- catches if catchesThrowable(cdef) && !isSyntheticCase(cdef) } { - unit.warning(body.pos, "catch block may intercept non-local return from " + meth) + reporter.warning(body.pos, "catch block may intercept non-local return from " + meth) } Block(List(keyDef), tryCatch) @@ -706,10 +706,10 @@ abstract class UnCurry extends InfoTransform */ private def saveRepeatedParams(dd: DefDef): Unit = if (dd.symbol.isConstructor) - unit.error(dd.symbol.pos, "A constructor cannot be annotated with a `varargs` annotation.") + reporter.error(dd.symbol.pos, "A constructor cannot be annotated with a `varargs` annotation.") else treeInfo.repeatedParams(dd) match { case Nil => - unit.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.") + reporter.error(dd.symbol.pos, "A method without repeated parameters cannot be annotated with the `varargs` annotation.") case reps => repeatedParams(dd.symbol) = reps } @@ -782,7 +782,7 @@ abstract class UnCurry extends InfoTransform // check if the method with that name and those arguments already exists in the template currentClass.info.member(forwsym.name).alternatives.find(s => s != forwsym && s.tpe.matches(forwsym.tpe)) match { - case Some(s) => unit.error(dd.symbol.pos, + case Some(s) => reporter.error(dd.symbol.pos, "A method with a varargs annotation produces a forwarder method with the same signature " + s.tpe + " as an existing method.") case None => diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index fde0aca584..0899507bab 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -505,7 +505,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { } - import global.{ConstantType, Constant, SingletonType, Literal, Ident, singleType} + import global.{ConstantType, Constant, EmptyScope, SingletonType, Literal, Ident, refinedType, singleType, TypeBounds, NoSymbol} import global.definitions._ @@ -538,23 +538,30 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { private val trees = mutable.HashSet.empty[Tree] // hashconsing trees (modulo value-equality) - private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type = - // a new type for every unstable symbol -- only stable value are uniqued - // technically, an unreachable value may change between cases - // thus, the failure of a case that matches on a mutable value does not exclude the next case succeeding - // (and thuuuuus, the latter case must be considered reachable) - if (!t.symbol.isStable) t.tpe.narrow + private[TreesAndTypesDomain] def uniqueTpForTree(t: Tree): Type = { + def freshExistentialSubtype(tp: Type): Type = { + // SI-8611 tp.narrow is tempting, but unsuitable. See `testRefinedTypeSI8611` for an explanation. + NoSymbol.freshExistential("").setInfo(TypeBounds.upper(tp)).tpe + } + + if (!t.symbol.isStable) { + // Create a fresh type for each unstable value, since we can never correlate it to another value. + // For example `case X => case X =>` should not complaing about the second case being unreachable, + // if X is mutable. + freshExistentialSubtype(t.tpe) + } else trees find (a => a.correspondsStructure(t)(sameValue)) match { case Some(orig) => - debug.patmat("unique tp for tree: "+ ((orig, orig.tpe))) + debug.patmat("unique tp for tree: " + ((orig, orig.tpe))) orig.tpe case _ => // duplicate, don't mutate old tree (TODO: use a map tree -> type instead?) - val treeWithNarrowedType = t.duplicate setType t.tpe.narrow + val treeWithNarrowedType = t.duplicate setType freshExistentialSubtype(t.tpe) debug.patmat("uniqued: "+ ((t, t.tpe, treeWithNarrowedType.tpe))) trees += treeWithNarrowedType treeWithNarrowedType.tpe } + } } sealed abstract class Const { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 894f959319..6f81cbe152 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -51,7 +51,7 @@ trait TreeAndTypeAnalysis extends Debugging { // This is a pretty poor approximation. def unsoundAssumptionUsed = binder.name != nme.WILDCARD && !(pt <:< pat.tpe) if (settings.lint && unsoundAssumptionUsed) - global.currentUnit.warning(pat.pos, + reporter.warning(pat.pos, sm"""The value matched by $pat is bound to ${binder.name}, which may be used under the |unsound assumption that it has type ${pat.tpe}, whereas we can only safely |count on it having type $pt, as the pattern is matched using `==` (see SI-1503).""") @@ -104,11 +104,18 @@ trait TreeAndTypeAnalysis extends Debugging { // TODO case _ if tp.isTupleType => // recurse into component types case modSym: ModuleClassSymbol => Some(List(tp)) + case sym: RefinementClassSymbol => + val parentSubtypes: List[Option[List[Type]]] = tp.parents.map(parent => enumerateSubtypes(parent)) + if (parentSubtypes exists (_.isDefined)) + // If any of the parents is enumerable, then the refinement type is enumerable. + Some( + // We must only include subtypes of the parents that conform to `tp`. + // See neg/virtpatmat_exhaust_compound.scala for an example. + parentSubtypes flatMap (_.getOrElse(Nil)) filter (_ <:< tp) + ) + else None // make sure it's not a primitive, else (5: Byte) match { case 5 => ... } sees no Byte - case sym if !sym.isSealed || isPrimitiveValueClass(sym) => - debug.patmat("enum unsealed "+ ((tp, sym, sym.isSealed, isPrimitiveValueClass(sym)))) - None - case sym => + case sym if sym.isSealed => val subclasses = debug.patmatResult(s"enum $sym sealed, subclasses")( // symbols which are both sealed and abstract need not be covered themselves, because // all of their children must be and they cannot otherwise be created. @@ -136,6 +143,9 @@ trait TreeAndTypeAnalysis extends Debugging { else None } }) + case sym => + debug.patmat("enum unsealed "+ ((tp, sym, sym.isSealed, isPrimitiveValueClass(sym)))) + None } // approximate a type to the static type that is fully checkable at run time, @@ -388,7 +398,7 @@ trait MatchAnalysis extends MatchApproximation { import global.definitions._ trait MatchAnalyzer extends MatchApproximator { - def uncheckedWarning(pos: Position, msg: String) = global.currentUnit.uncheckedWarning(pos, msg) + def uncheckedWarning(pos: Position, msg: String) = currentRun.reporting.uncheckedWarning(pos, msg) def warn(pos: Position, ex: AnalysisBudget.Exception, kind: String) = uncheckedWarning(pos, s"Cannot check match for $kind.\n${ex.advice}") // TODO: model dependencies between variables: if V1 corresponds to (x: List[_]) and V2 is (x.hd), V2 cannot be assigned when V1 = null or V1 = Nil diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index 8ff7824159..e9c81f4728 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -442,7 +442,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { val distinctAlts = distinctBy(switchableAlts)(extractConst) if (distinctAlts.size < switchableAlts.size) { val duplicated = switchableAlts.groupBy(extractConst).flatMap(_._2.drop(1).take(1)) // report the first duplicated - global.currentUnit.warning(pos, s"Pattern contains duplicate alternatives: ${duplicated.mkString(", ")}") + reporter.warning(pos, s"Pattern contains duplicate alternatives: ${duplicated.mkString(", ")}") } CaseDef(Alternative(distinctAlts), guard, body) } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala index 4cf8980689..0eaffe7cee 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTranslation.scala @@ -154,7 +154,7 @@ trait MatchTranslation { case SymbolBound(sym, expr) => bindingStep(sym, expr) case Literal(Constant(_)) | Ident(_) | Select(_, _) | This(_) => equalityTestStep() case Alternative(alts) => alternativesStep(alts) - case _ => context.unit.error(pos, unsupportedPatternMsg) ; noStep() + case _ => reporter.error(pos, unsupportedPatternMsg) ; noStep() } def translate(): List[TreeMaker] = nextStep() merge (_.translate()) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index 5d8a9fecef..1974befb45 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -558,7 +558,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { } emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{ - if (requireSwitch) typer.context.unit.warning(scrut.pos, "could not emit switch for @switch annotated match") + if (requireSwitch) reporter.warning(scrut.pos, "could not emit switch for @switch annotated match") if (casesNoSubstOnly nonEmpty) { // before optimizing, check casesNoSubstOnly for presence of a default case, diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala index a7d7680db1..9e9372f709 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchWarnings.scala @@ -67,7 +67,7 @@ trait MatchWarnings { val cdef = it.next() // If a default case has been seen, then every succeeding case is unreachable. if (vpat != null) - context.unit./*error*/warning(cdef.body.pos, "unreachable code due to " + vpat + addendum(cdef.pat)) + reporter.warning(cdef.body.pos, "unreachable code due to " + vpat + addendum(cdef.pat)) // TODO: make configurable whether this is an error // If this is a default case and more cases follow, warn about this one so // we have a reason to mention its pattern variable name and any corresponding // symbol in scope. Errors will follow from the remaining cases, at least @@ -78,7 +78,7 @@ trait MatchWarnings { case _ => "" } vpat = s"variable pattern$vpatName on line ${cdef.pat.pos.line}" - context.unit.warning(cdef.pos, s"patterns after a variable pattern cannot match (SLS 8.1.1)" + addendum(cdef.pat)) + reporter.warning(cdef.pos, s"patterns after a variable pattern cannot match (SLS 8.1.1)" + addendum(cdef.pat)) } } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala index f6c960d089..ef50e083a1 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/PatternMatching.scala @@ -65,7 +65,7 @@ trait PatternMatching extends Transform } catch { case x: (Types#TypeError) => // TODO: this should never happen; error should've been reported during type checking - unit.error(tree.pos, "error during expansion of this match (this is a scalac bug).\nThe underlying error was: "+ x.msg) + reporter.error(tree.pos, "error during expansion of this match (this is a scalac bug).\nThe underlying error was: "+ x.msg) translated } case Try(block, catches, finalizer) => @@ -175,13 +175,13 @@ trait Interface extends ast.TreeDSL { val matchOwner = typer.context.owner def pureType(tp: Type): Type = tp - def reportUnreachable(pos: Position) = typer.context.unit.warning(pos, "unreachable code") + def reportUnreachable(pos: Position) = reporter.warning(pos, "unreachable code") def reportMissingCases(pos: Position, counterExamples: List[String]) = { val ceString = if (counterExamples.tail.isEmpty) "input: " + counterExamples.head else "inputs: " + counterExamples.mkString(", ") - typer.context.unit.warning(pos, "match may not be exhaustive.\nIt would fail on the following "+ ceString) + reporter.warning(pos, "match may not be exhaustive.\nIt would fail on the following "+ ceString) } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala index d10eff1d8d..8f21f4ecfc 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/ScalacPatternExpanders.scala @@ -103,8 +103,8 @@ trait ScalacPatternExpanders { def offerString = if (extractor.isErroneous) "" else s" offering $offering" def arityExpected = ( if (extractor.hasSeq) "at least " else "" ) + productArity - def err(msg: String) = currentUnit.error(tree.pos, msg) - def warn(msg: String) = currentUnit.warning(tree.pos, msg) + def err(msg: String) = reporter.error(tree.pos, msg) + def warn(msg: String) = reporter.warning(tree.pos, msg) def arityError(what: String) = err(s"$what patterns for $owner$offerString: expected $arityExpected, found $totalArity") if (isStar && !isSeq) @@ -139,8 +139,10 @@ trait ScalacPatternExpanders { def acceptMessage = if (extractor.isErroneous) "" else s" to hold ${extractor.offeringString}" val requiresTupling = isUnapply && patterns.totalArity == 1 && productArity > 1 - if (requiresTupling && effectivePatternArity(args) == 1) - currentUnit.deprecationWarning(sel.pos, s"${sel.symbol.owner} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)") + if (requiresTupling && effectivePatternArity(args) == 1) { + val sym = sel.symbol.owner + currentRun.reporting.deprecationWarning(sel.pos, sym, s"${sym} expects $productArity patterns$acceptMessage but crushing into $productArity-tuple to fit single pattern (SI-6675)") + } val normalizedExtractor = if (requiresTupling) tupleExtractor(extractor) else extractor validateAligned(fn, Aligned(patterns, normalizedExtractor)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 1e544e54f6..2f4d228347 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -77,12 +77,13 @@ trait Adaptations { val msg = "Adaptation of argument list by inserting () has been deprecated: " + ( if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous." else "this is unlikely to be what you want.") - context.unit.deprecationWarning(t.pos, adaptWarningMessage(msg)) + context.deprecationWarning(t.pos, t.symbol, adaptWarningMessage(msg)) } } else if (settings.warnAdaptedArgs) context.warning(t.pos, adaptWarningMessage(s"Adapting argument list by creating a ${args.size}-tuple: this may not be what you want.")) - !settings.noAdaptedArgs || !(args.isEmpty && settings.future) + // return `true` if the adaptation should be kept + !(settings.noAdaptedArgs || (args.isEmpty && settings.future)) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala index fa6e5399eb..5a70d4c524 100644 --- a/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +++ b/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala @@ -190,6 +190,16 @@ trait AnalyzerPlugins { self: Analyzer => def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = None /** + * Figures out whether the given macro definition is blackbox or whitebox. + * + * Default implementation provided in `self.standardIsBlackbox` loads the macro impl binding + * and fetches boxity from the "isBlackbox" field of the macro signature. + * + * $nonCumulativeReturnValueDoc. + */ + def pluginsIsBlackbox(macroDef: Symbol): Option[Boolean] = None + + /** * Expands an application of a def macro (i.e. of a symbol that has the MACRO flag set), * possibly using the current typer mode and the provided prototype. * @@ -375,6 +385,14 @@ trait AnalyzerPlugins { self: Analyzer => def custom(plugin: MacroPlugin) = plugin.pluginsTypedMacroBody(typer, ddef) }) + /** @see MacroPlugin.pluginsIsBlackbox */ + def pluginsIsBlackbox(macroDef: Symbol): Boolean = invoke(new NonCumulativeOp[Boolean] { + def position = macroDef.pos + def description = "compute boxity for this macro definition" + def default = standardIsBlackbox(macroDef) + def custom(plugin: MacroPlugin) = plugin.pluginsIsBlackbox(macroDef) + }) + /** @see MacroPlugin.pluginsMacroExpand */ def pluginsMacroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = invoke(new NonCumulativeOp[Tree] { def position = expandee.pos diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 13884404b3..3a77cab919 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -275,7 +275,7 @@ trait Checkable { ; // Matching on types like case _: AnyRef { def bippy: Int } => doesn't work -- yet. case RefinedType(_, decls) if !decls.isEmpty => - getContext.unit.warning(tree.pos, s"a pattern match on a refinement type is unchecked") + reporter.warning(tree.pos, s"a pattern match on a refinement type is unchecked") case RefinedType(parents, _) => parents foreach (p => checkCheckable(tree, p, X, inPattern, canRemedy)) case _ => @@ -285,14 +285,14 @@ trait Checkable { if (checker.neverMatches) { val addendum = if (checker.neverSubClass) "" else " (but still might match its erasure)" - getContext.unit.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $PString$addendum") + reporter.warning(tree.pos, s"fruitless type test: a value of type $X cannot also be a $PString$addendum") } else if (checker.isUncheckable) { val msg = ( if (checker.uncheckableType =:= P) s"abstract type $where$PString" else s"${checker.uncheckableMessage} in type $where$PString" ) - getContext.unit.warning(tree.pos, s"$msg is unchecked since it is eliminated by erasure") + reporter.warning(tree.pos, s"$msg is unchecked since it is eliminated by erasure") } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 8e1ceffecd..72ca9b879a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -66,7 +66,7 @@ trait Contexts { self: Analyzer => def isMask(s: ImportSelector) = s.name != nme.WILDCARD && s.rename == nme.WILDCARD imp.tree.selectors filterNot (s => isMask(s) || used(s)) foreach { sel => - unit.warning(imp posOf sel, "Unused import") + reporter.warning(imp posOf sel, "Unused import") } } allUsedSelectors --= imps @@ -103,7 +103,7 @@ trait Contexts { self: Analyzer => // there must be a scala.xml package when xml literals were parsed in this unit if (unit.hasXml && ScalaXmlPackage == NoSymbol) - unit.error(unit.firstXmlPos, "To compile XML syntax, the scala.xml package must be on the classpath.\nPlease see http://docs.scala-lang.org/overviews/core/scala-2.11.html#scala-xml.") + reporter.error(unit.firstXmlPos, "To compile XML syntax, the scala.xml package must be on the classpath.\nPlease see http://docs.scala-lang.org/overviews/core/scala-2.11.html#scala-xml.") // scala-xml needs `scala.xml.TopScope` to be in scope globally as `$scope` // We detect `scala-xml` by looking for `scala.xml.TopScope` and @@ -359,7 +359,7 @@ trait Contexts { self: Analyzer => /** Issue and clear all warnings from the report buffer */ def flushAndIssueWarnings() { reportBuffer.warnings foreach { - case (pos, msg) => unit.warning(pos, msg) + case (pos, msg) => reporter.warning(pos, msg) } reportBuffer.clearAllWarnings() } @@ -541,7 +541,7 @@ trait Contexts { self: Analyzer => } private def unitError(pos: Position, msg: String): Unit = - if (checking) onTreeCheckerError(pos, msg) else unit.error(pos, msg) + if (checking) onTreeCheckerError(pos, msg) else reporter.error(pos, msg) @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) { // TODO: are errors allowed to have pos == NoPosition?? @@ -589,10 +589,20 @@ trait Contexts { self: Analyzer => /** Issue/throw the given error message according to the current mode for error reporting. */ def warning(pos: Position, msg: String, force: Boolean = false) { - if (reportErrors || force) unit.warning(pos, msg) + if (reportErrors || force) reporter.warning(pos, msg) else if (bufferErrors) reportBuffer += (pos -> msg) } + def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = + currentRun.reporting.deprecationWarning(pos, sym, msg) + def deprecationWarning(pos: Position, sym: Symbol): Unit = + currentRun.reporting.deprecationWarning(pos, sym) // TODO: allow this to escalate to an error, and implicit search will ignore deprecated implicits + + def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = + currentRun.reporting.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) + + def echo(pos: Position, msg: String): Unit = reporter.echo(pos, msg) + // nextOuter determines which context is searched next for implicits // (after `this`, which contributes `newImplicits` below.) In // most cases, it is simply the outer context: if we're owned by 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/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index fc0e2c7c80..a3f1da60ce 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -558,7 +558,7 @@ trait Infer extends Checkable { foreachWithIndex(targs) ((targ, idx) => targ.typeSymbol match { case sym @ (AnyClass | AnyValClass) => - context.unit.warning(argumentPosition(idx), s"a type was inferred to be `${sym.name}`; this may indicate a programming error.") + reporter.warning(argumentPosition(idx), s"a type was inferred to be `${sym.name}`; this may indicate a programming error.") case _ => } ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index f4456998c0..9c22688581 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -263,7 +263,12 @@ trait Macros extends MacroRuntimes with Traces with Helpers { } def isBlackbox(expandee: Tree): Boolean = isBlackbox(dissectApplied(expandee).core.symbol) - def isBlackbox(macroDef: Symbol): Boolean = { + def isBlackbox(macroDef: Symbol): Boolean = pluginsIsBlackbox(macroDef) + + /** Default implementation of `isBlackbox`. + * Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsIsBlackbox for more details) + */ + def standardIsBlackbox(macroDef: Symbol): Boolean = { val fastTrackBoxity = fastTrack.get(macroDef).map(_.isBlackbox) val bindingBoxity = loadMacroImplBinding(macroDef).map(_.isBlackbox) fastTrackBoxity orElse bindingBoxity getOrElse false @@ -417,9 +422,10 @@ trait Macros extends MacroRuntimes with Traces with Helpers { val wrappedArgs = mapWithIndex(args)((arg, j) => { val fingerprint = implParams(min(j, implParams.length - 1)) + val duplicatedArg = duplicateAndKeepPositions(arg) fingerprint match { - case LiftedTyped => context.Expr[Nothing](arg.duplicate)(TypeTag.Nothing) // TODO: SI-5752 - case LiftedUntyped => arg.duplicate + case LiftedTyped => context.Expr[Nothing](duplicatedArg)(TypeTag.Nothing) // TODO: SI-5752 + case LiftedUntyped => duplicatedArg case _ => abort(s"unexpected fingerprint $fingerprint in $binding with paramss being $paramss " + s"corresponding to arg $arg in $argss") } @@ -708,7 +714,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers { sealed abstract class MacroStatus(val result: Tree) case class Success(expanded: Tree) extends MacroStatus(expanded) - case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.seenMacroExpansionsFallingBack = true } + case class Fallback(fallback: Tree) extends MacroStatus(fallback) { currentRun.reporting.seenMacroExpansionsFallingBack = true } case class Delayed(delayed: Tree) extends MacroStatus(delayed) case class Skipped(skipped: Tree) extends MacroStatus(skipped) case class Failure(failure: Tree) extends MacroStatus(failure) @@ -782,7 +788,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers { } } catch { case ex: Throwable => - popMacroContext() + if (openMacros.nonEmpty) popMacroContext() // weirdly we started popping on an empty stack when refactoring fatalWarnings logic val realex = ReflectionUtils.unwrapThrowable(ex) realex match { case ex: AbortMacroException => MacroGeneratedAbort(expandee, ex) diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 4382a2c6f7..1328119aac 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -443,7 +443,7 @@ trait Namers extends MethodSynthesis { && clazz.exists ) if (fails) { - context.unit.error(tree.pos, ( + reporter.error(tree.pos, ( s"Companions '$clazz' and '$module' must be defined in same file:\n" + s" Found in ${clazz.sourceFile.canonicalPath} and ${module.sourceFile.canonicalPath}") ) @@ -718,7 +718,7 @@ trait Namers extends MethodSynthesis { } val owner = tree.symbol.owner if (settings.lint && owner.isPackageObjectClass && !mods.isImplicit) { - context.unit.warning(tree.pos, + reporter.warning(tree.pos, "it is not recommended to define classes/objects inside of package objects.\n" + "If possible, define " + tree.symbol + " in " + owner.skipPackageObject + " instead." ) @@ -730,7 +730,7 @@ trait Namers extends MethodSynthesis { log("enter implicit wrapper "+tree+", owner = "+owner) enterImplicitWrapper(tree) } - else context.unit.error(tree.pos, "implicit classes must accept exactly one primary constructor parameter") + else reporter.error(tree.pos, "implicit classes must accept exactly one primary constructor parameter") } validateCompanionDefs(tree) } @@ -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/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index dceb0a47d8..b6387fd56b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -174,8 +174,8 @@ trait NamesDefaults { self: Analyzer => // assigning the correct method symbol, typedSelect will just assign the type. the reason // to still call 'typed' is to correctly infer singleton types, SI-5259. val selectPos = - if(qual.pos.isRange && baseFun.pos.isRange) qual.pos.union(baseFun.pos).withStart(Math.min(qual.pos.end, baseFun.pos.end)) - else baseFun.pos + if(qual.pos.isRange && baseFun1.pos.isRange) qual.pos.union(baseFun1.pos).withStart(Math.min(qual.pos.end, baseFun1.pos.end)) + else baseFun1.pos val f = blockTyper.typedOperator(Select(newQual, selected).setSymbol(baseFun1.symbol).setPos(selectPos)) if (funTargs.isEmpty) f else TypeApply(f, funTargs).setType(baseFun.tpe) @@ -379,18 +379,37 @@ trait NamesDefaults { self: Analyzer => def makeNamedTypes(syms: List[Symbol]) = syms map (sym => NamedType(sym.name, sym.tpe)) - def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name] = nameOfNamedArg _): (List[Symbol], Boolean) = { - val namedArgs = args.dropWhile(arg => { - val n = argName(arg) - n.isEmpty || params.forall(p => p.name != n.get) - }) - val namedParams = params.drop(args.length - namedArgs.length) - // missing: keep those with a name which doesn't exist in namedArgs - val missingParams = namedParams.filter(p => namedArgs.forall(arg => { + /** + * Returns the parameter symbols of an invocation expression that are not defined by the list + * of arguments. + * + * @param args The list of arguments + * @param params The list of parameter sybols of the invoked method + * @param argName A function that extracts the name of an argument expression, if it is a named argument. + */ + def missingParams[T](args: List[T], params: List[Symbol], argName: T => Option[Name]): (List[Symbol], Boolean) = { + // The argument list contains first a mix of positional args and named args that are on the + // right parameter position, and then a number or named args on different positions. + + // collect all named arguments whose position does not match the parameter they define + val namedArgsOnChangedPosition = args.zip(params) dropWhile { + case (arg, param) => + val n = argName(arg) + // drop the argument if + // - it's not named, or + // - it's named, but defines the parameter on its current position, or + // - it's named, but none of the parameter names matches (treated as a positional argument, an assignment expression) + n.isEmpty || n.get == param.name || params.forall(_.name != n.get) + } map (_._1) + + val paramsWithoutPositionalArg = params.drop(args.length - namedArgsOnChangedPosition.length) + + // missing parameters: those with a name which is not specified in one of the namedArgsOnChangedPosition + val missingParams = paramsWithoutPositionalArg.filter(p => namedArgsOnChangedPosition.forall { arg => val n = argName(arg) n.isEmpty || n.get != p.name - })) - val allPositional = missingParams.length == namedParams.length + }) + val allPositional = missingParams.length == paramsWithoutPositionalArg.length (missingParams, allPositional) } @@ -407,7 +426,7 @@ trait NamesDefaults { self: Analyzer => previousArgss: List[List[Tree]], params: List[Symbol], pos: scala.reflect.internal.util.Position, context: Context): (List[Tree], List[Symbol]) = { if (givenArgs.length < params.length) { - val (missing, positional) = missingParams(givenArgs, params) + val (missing, positional) = missingParams(givenArgs, params, nameOfNamedArg) if (missing forall (_.hasDefault)) { val defaultArgs = missing flatMap (p => { val defGetter = defaultGetter(p, context) @@ -536,8 +555,8 @@ trait NamesDefaults { self: Analyzer => def matchesName(param: Symbol) = !param.isSynthetic && ( (param.name == name) || (param.deprecatedParamName match { case Some(`name`) => - context0.unit.deprecationWarning(arg.pos, - "the parameter name "+ name +" has been deprecated. Use "+ param.name +" instead.") + context0.deprecationWarning(arg.pos, param, + s"the parameter name $name has been deprecated. Use ${param.name} instead.") true case _ => false }) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 4540017b62..1b3da26bf2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -147,7 +147,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val owners = haveDefaults map (_.owner) // constructors of different classes are allowed to have defaults if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size) { - unit.error(clazz.pos, + reporter.error(clazz.pos, "in "+ clazz + ", multiple overloaded alternatives of "+ haveDefaults.head + " define default arguments" + ( @@ -162,7 +162,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // Check for doomed attempt to overload applyDynamic if (clazz isSubClass DynamicClass) { for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.typeParams.length)) { - unit.error(m1.pos, "implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)") + reporter.error(m1.pos, "implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)") } } @@ -172,7 +172,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // implicit classes leave both a module symbol and a method symbol as residue val alts = clazz.info.decl(sym.name).alternatives filterNot (_.isModule) if (alts.size > 1) - alts foreach (x => unit.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds")) + alts foreach (x => reporter.warning(x.pos, "parameterized overloaded implicit methods are not visible as view bounds")) } } } @@ -281,10 +281,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans mixinOverrideErrors.toList match { case List() => case List(MixinOverrideError(_, msg)) => - unit.error(clazz.pos, msg) + reporter.error(clazz.pos, msg) case MixinOverrideError(member, msg) :: others => val others1 = others.map(_.member.name.decode).filter(member.name.decode != _).distinct - unit.error( + reporter.error( clazz.pos, msg+(if (others1.isEmpty) "" else ";\n other members with override errors are: "+(others1 mkString ", "))) @@ -347,7 +347,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans ) } def emitOverrideError(fullmsg: String) { - if (member.owner == clazz) unit.error(member.pos, fullmsg) + if (member.owner == clazz) reporter.error(member.pos, fullmsg) else mixinOverrideErrors += new MixinOverrideError(member, fullmsg) } @@ -464,7 +464,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkOverrideDeprecated() if (settings.warnNullaryOverride) { if (other.paramss.isEmpty && !member.paramss.isEmpty) { - unit.warning(member.pos, "non-nullary method overrides nullary method") + reporter.warning(member.pos, "non-nullary method overrides nullary method") } } } @@ -496,7 +496,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans typer.infer.checkKindBounds(high :: Nil, lowType :: Nil, rootType, low.owner) match { // (1.7.2) case Nil => case kindErrors => - unit.error(member.pos, + reporter.error(member.pos, "The kind of "+member.keyString+" "+member.varianceString + member.nameString+ " does not conform to the expected kind of " + other.defString + other.locationString + "." + kindErrors.toList.mkString("\n", ", ", "")) @@ -507,7 +507,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans typer.infer.checkKindBounds(low :: Nil, lowType.normalize :: Nil, rootType, low.owner) match { case Nil => case kindErrors => - unit.error(member.pos, + reporter.error(member.pos, "The kind of the right-hand side "+lowType.normalize+" of "+low.keyString+" "+ low.varianceString + low.nameString+ " does not conform to its expected kind."+ kindErrors.toList.mkString("\n", ", ", "")) @@ -546,7 +546,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (other.hasDeprecatedOverridingAnnotation) { val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse "" val msg = s"overriding ${other.fullLocationString} is deprecated$suffix" - unit.deprecationWarning(member.pos, msg) + currentRun.reporting.deprecationWarning(member.pos, other, msg) } } } @@ -754,7 +754,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans checkNoAbstractDecls(clazz) if (abstractErrors.nonEmpty) - unit.error(clazz.pos, abstractErrorMessage) + reporter.error(clazz.pos, abstractErrorMessage) } else if (clazz.isTrait && !(clazz isSubClass AnyValClass)) { // For non-AnyVal classes, prevent abstract methods in interfaces that override @@ -765,7 +765,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // override a concrete method in Object. The jvm, however, does not. val overridden = decl.matchingSymbol(ObjectClass, ObjectTpe) if (overridden.isFinal) - unit.error(decl.pos, "trait cannot redefine final method from class AnyRef") + reporter.error(decl.pos, "trait cannot redefine final method from class AnyRef") } } @@ -818,7 +818,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG val nonMatching: List[Symbol] = clazz.info.member(member.name).alternatives.filterNot(_.owner == clazz).filterNot(_.isFinal) - def issueError(suffix: String) = unit.error(member.pos, member.toString() + " overrides nothing" + suffix) + def issueError(suffix: String) = reporter.error(member.pos, member.toString() + " overrides nothing" + suffix) nonMatching match { case Nil => issueError("") @@ -871,7 +871,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case _ :: Nil => ;// OK case tp1 :: tp2 :: _ => - unit.error(clazz.pos, "illegal inheritance;\n " + clazz + + reporter.error(clazz.pos, "illegal inheritance;\n " + clazz + " inherits different type instances of " + baseClass + ":\n" + tp1 + " and " + tp2) explainTypes(tp1, tp2) @@ -888,7 +888,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case _ => "type "+tp } override def issueVarianceError(base: Symbol, sym: Symbol, required: Variance) { - currentRun.currentUnit.error(base.pos, + reporter.error(base.pos, s"${sym.variance} $sym occurs in $required position in ${tpString(base.info)} of $base") } } @@ -956,7 +956,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match { case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply => - unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567 + reporter.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567 case _ => } @@ -1031,7 +1031,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) - unit.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg") + reporter.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg") isNonSensible = true } def nonSensible(pre: String, alwaysEqual: Boolean) = @@ -1046,7 +1046,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } def unrelatedTypes() = if (!isNonSensible) { val weaselWord = if (isEitherValueClass) "" else " most likely" - unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") + reporter.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") } if (nullCount == 2) // null == null @@ -1141,7 +1141,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans && callsSelf ) if (trivialInifiniteLoop) - unit.warning(valOrDef.rhs.pos, s"${valOrDef.symbol.fullLocationString} does nothing other than call itself recursively") + reporter.warning(valOrDef.rhs.pos, s"${valOrDef.symbol.fullLocationString} does nothing other than call itself recursively") } // Transformation ------------------------------------------------------------ @@ -1231,7 +1231,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans finally if (currentLevel.maxindex > 0) { // An implementation restriction to avoid VerifyErrors and lazyvals mishaps; see SI-4717 debuglog("refsym = " + currentLevel.refsym) - unit.error(currentLevel.refpos, "forward reference not allowed from self constructor invocation") + reporter.error(currentLevel.refpos, "forward reference not allowed from self constructor invocation") } case ModuleDef(_, _, _) => eliminateModuleDefs(tree) case ValDef(_, _, _, _) => @@ -1241,7 +1241,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans val lazySym = tree.symbol.lazyAccessorOrSelf if (lazySym.isLocalToBlock && index <= currentLevel.maxindex) { debuglog("refsym = " + currentLevel.refsym) - unit.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym) + reporter.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym) } tree1 :: Nil } @@ -1255,7 +1255,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans try typer.infer.checkBounds(tree0, pre, owner, tparams, argtps, "") catch { case ex: TypeError => - unit.error(tree0.pos, ex.getMessage()) + reporter.error(tree0.pos, ex.getMessage()) if (settings.explaintypes) { val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, argtps).bounds) (argtps, bounds).zipped map ((targ, bound) => explainTypes(bound.lo, targ)) @@ -1287,11 +1287,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def checkUndesiredProperties(sym: Symbol, pos: Position) { // If symbol is deprecated, and the point of reference is not enclosed // in either a deprecated member or a scala bridge method, issue a warning. - if (sym.isDeprecated && !currentOwner.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) { - unit.deprecationWarning(pos, "%s%s is deprecated%s".format( - sym, sym.locationString, sym.deprecationMessage map (": " + _) getOrElse "") - ) - } + if (sym.isDeprecated && !currentOwner.ownerChain.exists(x => x.isDeprecated || x.hasBridgeAnnotation)) + currentRun.reporting.deprecationWarning(pos, sym) + // Similar to deprecation: check if the symbol is marked with @migration // indicating it has changed semantics between versions. if (sym.hasMigrationAnnotation && settings.Xmigration.value != NoScalaVersion) { @@ -1299,12 +1297,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans settings.Xmigration.value < ScalaVersion(sym.migrationVersion.get) catch { case e : NumberFormatException => - unit.warning(pos, s"${sym.fullLocationString} has an unparsable version number: ${e.getMessage()}") + reporter.warning(pos, s"${sym.fullLocationString} has an unparsable version number: ${e.getMessage()}") // if we can't parse the format on the migration annotation just conservatively assume it changed true } if (changed) - unit.warning(pos, s"${sym.fullLocationString} has changed semantics in version ${sym.migrationVersion.get}:\n${sym.migrationMessage.get}") + reporter.warning(pos, s"${sym.fullLocationString} has changed semantics in version ${sym.migrationVersion.get}:\n${sym.migrationMessage.get}") } // See an explanation of compileTimeOnly in its scaladoc at scala.annotation.compileTimeOnly. if (sym.isCompileTimeOnly) { @@ -1312,7 +1310,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans sm"""Reference to ${sym.fullLocationString} should not have survived past type checking, |it should have been processed and eliminated during expansion of an enclosing macro.""" // The getOrElse part should never happen, it's just here as a backstop. - unit.error(pos, sym.compileTimeOnlyMessage getOrElse defaultMsg) + reporter.error(pos, sym.compileTimeOnlyMessage getOrElse defaultMsg) } } @@ -1323,7 +1321,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans && sym.accessedOrSelf.isVal ) if (settings.lint.value && isLikelyUninitialized) - unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value") + reporter.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value") } private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = ( @@ -1355,7 +1353,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans if (memberSym.isDeferred) "may be unable to provide a concrete implementation of" else "may be unable to override" - unit.warning(memberSym.pos, + reporter.warning(memberSym.pos, "%s%s references %s %s.".format( memberSym.fullLocationString, comparison, accessFlagsToString(otherSym), otherSym @@ -1390,7 +1388,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans tree match { case DefDef(_, name, _, params :: _, _, _) => if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol))) - unit.warning(tree.pos, + reporter.warning(tree.pos, "by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.") case _ => } @@ -1407,10 +1405,10 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans symbol.allOverriddenSymbols.filter(sym => !sym.isDeprecated && !sym.isDeferred) if(!concrOvers.isEmpty) - unit.deprecationWarning( + currentRun.reporting.deprecationWarning( tree.pos, - symbol.toString + " overrides concrete, non-deprecated symbol(s):" + - concrOvers.map(_.name.decode).mkString(" ", ", ", "")) + symbol, + s"${symbol.toString} overrides concrete, non-deprecated symbol(s): ${concrOvers.map(_.name.decode).mkString(", ")}") } } private def isRepeatedParamArg(tree: Tree) = currentApplication match { @@ -1471,7 +1469,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans applyChecks(sym.annotations) // validate implicitNotFoundMessage analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn => - unit.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn") + reporter.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn") } case tpt@TypeTree() => @@ -1596,7 +1594,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType)) ) if (!isOk) - unit.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead") + reporter.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead") case _ => () } @@ -1604,15 +1602,15 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans private def checkAnyValSubclass(clazz: Symbol) = { if (clazz.isDerivedValueClass) { if (clazz.isTrait) - unit.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") + reporter.error(clazz.pos, "Only classes (not traits) are allowed to extend AnyVal") else if (clazz.hasAbstractFlag) - unit.error(clazz.pos, "`abstract' modifier cannot be used with value classes") + reporter.error(clazz.pos, "`abstract' modifier cannot be used with value classes") } } private def checkUnexpandedMacro(t: Tree) = if (!t.isDef && t.hasSymbolField && t.symbol.isTermMacro) - unit.error(t.pos, "macro has not been expanded") + reporter.error(t.pos, "macro has not been expanded") override def transform(tree: Tree): Tree = { val savedLocalTyper = localTyper @@ -1707,7 +1705,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans tree case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) => - unit.error(tree.pos, "no `: _*' annotation allowed here\n"+ + reporter.error(tree.pos, "no `: _*' annotation allowed here\n"+ "(such annotations are only allowed in arguments to *-parameters)") tree @@ -1780,7 +1778,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } catch { case ex: TypeError => if (settings.debug) ex.printStackTrace() - unit.error(tree.pos, ex.getMessage()) + reporter.error(tree.pos, ex.getMessage()) tree } finally { localTyper = savedLocalTyper diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 9b9e641cad..d3a41b9570 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -12,21 +12,47 @@ import scala.collection.{ mutable, immutable } import mutable.ListBuffer import symtab.Flags._ -/** This phase adds super accessors for all super calls that either +/** This phase performs the following functions, each of which could be split out in a + * mini-phase: + * + * (1) Adds super accessors for all super calls that either * appear in a trait or have as a target a member of some outer class. - * It also replaces references to parameter accessors with aliases - * by super references to these aliases. The phase also checks that - * symbols accessed from super are not abstract, or are overridden by - * an abstract override. Finally, the phase also mangles the names - * of class-members which are private up to an enclosing non-package - * class, in order to avoid overriding conflicts. * - * This phase also sets SPECIALIZED flag on type parameters with + * (2) Converts references to parameter fields that have the same name as a corresponding + * public parameter field in a superclass to a reference to the superclass + * field (corresponding = super class field is initialized with subclass field). + * This info is pre-computed by the `alias` field in Typer. `dotc` follows a different + * route; it computes everything in SuperAccessors and changes the subclass field + * to a forwarder instead of manipulating references. This is more modular. + * + * (3) Adds protected accessors if the access to the protected member happens + * in a class which is not a subclass of the member's owner. + * + * (4) Mangles the names of class-members which are + * private up to an enclosing non-package class, in order to avoid overriding conflicts. + * This is a dubious, and it would be better to deprecate class-qualified privates. + * + * (5) This phase also sets SPECIALIZED flag on type parameters with * `@specialized` annotation. We put this logic here because the * flag must be set before pickling. * - * @author Martin Odersky - * @version 1.0 + * It also checks that: + * + * (1) Symbols accessed from super are not abstract, or are overridden by + * an abstract override. + * + * (2) If a symbol accessed accessed from super is defined in a real class (not a trait), + * there are no abstract members which override this member in Java's rules + * (see SI-4989; such an access would lead to illegal bytecode) + * + * (3) Super calls do not go to some synthetic members of Any (see isDisallowed) + * + * (4) Super calls do not go to synthetic field accessors + * + * (5) A class and its companion object do not both define a class or module with the + * same name. + * + * TODO: Rename phase to "Accessors" because it handles more than just super accessors */ abstract class SuperAccessors extends transform.Transform with transform.TypingTransformers { import global._ @@ -98,7 +124,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT if (other == NoSymbol) other = linked.info.decl(sym.name.toTermName).filter(_.isModule) if (other != NoSymbol) - unit.error(sym.pos, "name clash: "+sym.owner+" defines "+sym+ + reporter.error(sym.pos, "name clash: "+sym.owner+" defines "+sym+ "\nand its companion "+sym.owner.companionModule+" also defines "+ other) } @@ -113,14 +139,14 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val member = sym.overridingSymbol(clazz) if (mix != tpnme.EMPTY || member == NoSymbol || !(member.isAbstractOverride && member.isIncompleteIn(clazz))) - unit.error(sel.pos, ""+sym.fullLocationString+" is accessed from super. It may not be abstract "+ + reporter.error(sel.pos, ""+sym.fullLocationString+" is accessed from super. It may not be abstract "+ "unless it is overridden by a member declared `abstract' and `override'") } else if (mix == tpnme.EMPTY && !sym.owner.isTrait){ // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract. val intermediateClasses = clazz.info.baseClasses.tail.takeWhile(_ != sym.owner) intermediateClasses.map(sym.overridingSymbol).find(s => s.isDeferred && !s.isAbstractOverride && !s.owner.isTrait).foreach { absSym => - unit.error(sel.pos, s"${sym.fullLocationString} cannot be directly accessed from ${clazz} because ${absSym.owner} redeclares it as abstract") + reporter.error(sel.pos, s"${sym.fullLocationString} cannot be directly accessed from ${clazz} because ${absSym.owner} redeclares it as abstract") } } @@ -226,7 +252,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT qual.symbol.ancestors foreach { parent => parent.info.decls filterNot (x => x.isPrivate || x.isLocalToThis) foreach { m2 => if (sym.name == m2.name && m2.isGetter && m2.accessed.isMutable) { - unit.warning(sel.pos, + reporter.warning(sel.pos, sym.accessString + " " + sym.fullLocationString + " shadows mutable " + m2.name + " inherited from " + m2.owner + ". Changes to " + m2.name + " will not be visible within " + sym.owner + " - you may want to give them distinct names.") @@ -284,9 +310,9 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case Super(_, mix) => if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { if (!settings.overrideVars) - unit.error(tree.pos, "super may be not be used on " + sym.accessedOrSelf) + reporter.error(tree.pos, "super may be not be used on " + sym.accessedOrSelf) } else if (isDisallowed(sym)) { - unit.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") + reporter.error(tree.pos, "super not allowed here: use this." + name.decode + " instead") } transformSuperSelect(sel) diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index a2f52e1905..399a4ca8d5 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -17,7 +17,7 @@ abstract class TreeCheckers extends Analyzer { override protected def onTreeCheckerError(pos: Position, msg: String) { if (settings.fatalWarnings) - currentUnit.warning(pos, "\n** Error during internal checking:\n" + msg) + reporter.warning(pos, "\n** Error during internal checking:\n" + msg) } case class DiffResult[T](lost: List[T], gained: List[T]) { @@ -170,7 +170,7 @@ abstract class TreeCheckers extends Analyzer { ) - def errorFn(pos: Position, msg: Any): Unit = currentUnit.warning(pos, "[check: %s] %s".format(phase.prev, msg)) + def errorFn(pos: Position, msg: Any): Unit = reporter.warning(pos, "[check: %s] %s".format(phase.prev, msg)) def errorFn(msg: Any): Unit = errorFn(NoPosition, msg) def informFn(msg: Any) { diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 60346e7be1..7440f69e93 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -41,9 +41,9 @@ trait TypeDiagnostics { * indicate that the restriction may be lifted in the future. */ def restrictionWarning(pos: Position, unit: CompilationUnit, msg: String): Unit = - unit.warning(pos, "Implementation restriction: " + msg) + reporter.warning(pos, "Implementation restriction: " + msg) def restrictionError(pos: Position, unit: CompilationUnit, msg: String): Unit = - unit.error(pos, "Implementation restriction: " + msg) + reporter.error(pos, "Implementation restriction: " + msg) /** A map of Positions to addendums - if an error involves a position in * the map, the addendum should also be printed. @@ -435,12 +435,8 @@ trait TypeDiagnostics { trait TyperDiagnostics { self: Typer => - private def contextError(context0: Analyzer#Context, pos: Position, msg: String) = context0.error(pos, msg) - private def contextError(context0: Analyzer#Context, pos: Position, err: Throwable) = context0.error(pos, err) - private def contextWarning(pos: Position, msg: String) = context.unit.warning(pos, msg) - def permanentlyHiddenWarning(pos: Position, hidden: Name, defn: Symbol) = - contextWarning(pos, "imported `%s' is permanently hidden by definition of %s".format(hidden, defn.fullLocationString)) + context.warning(pos, "imported `%s' is permanently hidden by definition of %s".format(hidden, defn.fullLocationString)) object checkUnused { val ignoreNames = Set[TermName]("readResolve", "readObject", "writeObject", "writeReplace") @@ -542,15 +538,15 @@ trait TypeDiagnostics { else if (sym.isModule) "object" else "term" ) - unit.warning(pos, s"$why $what in ${sym.owner} is never used") + reporter.warning(pos, s"$why $what in ${sym.owner} is never used") } p.unsetVars foreach { v => - unit.warning(v.pos, s"local var ${v.name} in ${v.owner} is never set - it could be a val") + reporter.warning(v.pos, s"local var ${v.name} in ${v.owner} is never set - it could be a val") } p.unusedTypes foreach { t => val sym = t.symbol val why = if (sym.isPrivate) "private" else "local" - unit.warning(t.pos, s"$why ${sym.fullLocationString} is never used") + reporter.warning(t.pos, s"$why ${sym.fullLocationString} is never used") } } } @@ -627,13 +623,13 @@ trait TypeDiagnostics { case Import(expr, _) => expr.pos case _ => ex.pos } - contextError(context0, pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) + context0.error(pos, cyclicReferenceMessage(sym, info.tree) getOrElse ex.getMessage()) if (sym == ObjectClass) throw new FatalError("cannot redefine root "+sym) } case _ => - contextError(context0, ex.pos, ex) + context0.error(ex.pos, ex) } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index bf98c0e3dc..65f11a360e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -746,21 +746,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!OK) { val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) = featureTrait getAnnotation LanguageFeatureAnnot - val req = if (required) "needs to" else "should" - val fqname = "scala.language." + featureName - val explain = ( - if (currentRun.reportedFeature contains featureTrait) "" else - s"""| - |This can be achieved by adding the import clause 'import $fqname' - |or by setting the compiler option -language:$featureName. - |See the Scala docs for value $fqname for a discussion - |why the feature $req be explicitly enabled.""".stripMargin - ) - currentRun.reportedFeature += featureTrait - - val msg = s"$featureDesc $req be enabled\nby making the implicit value $fqname visible.$explain" replace ("#", construct) - if (required) unit.error(pos, msg) - else currentRun.featureWarnings.warn(pos, msg) + context.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) } OK } @@ -955,10 +941,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } def adaptConstant(value: Constant): Tree = { val sym = tree.symbol - if (sym != null && sym.isDeprecated) { - val msg = sym.toString + sym.locationString + " is deprecated: " + sym.deprecationMessage.getOrElse("") - unit.deprecationWarning(tree.pos, msg) - } + if (sym != null && sym.isDeprecated) + context.deprecationWarning(tree.pos, sym) + treeCopy.Literal(tree, value) } @@ -1066,7 +1051,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case coercion => def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe if (settings.logImplicitConv) - unit.echo(tree.pos, msg) + context.echo(tree.pos, msg) debuglog(msg) val silentContext = context.makeImplicit(context.ambiguousErrors) @@ -1226,7 +1211,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case EmptyTree => qual case coercion => if (settings.logImplicitConv) - unit.echo(qual.pos, + context.echo(qual.pos, "applied implicit conversion from %s to %s = %s".format( qual.tpe, searchTemplate, coercion.symbol.defString)) @@ -1291,7 +1276,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper private def validateNoCaseAncestor(clazz: Symbol) = { if (!phase.erasedTypes) { for (ancestor <- clazz.ancestors find (_.isCase)) { - unit.error(clazz.pos, ( + context.error(clazz.pos, ( "case %s has case ancestor %s, but case-to-case inheritance is prohibited."+ " To overcome this limitation, use extractors to pattern match on non-leaf nodes." ).format(clazz, ancestor.fullName)) @@ -1308,7 +1293,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val isValueClass = !clazz.isTrait def where = if (isValueClass) "value class" else "universal trait extending from class Any" def implRestriction(tree: Tree, what: String) = - unit.error(tree.pos, s"implementation restriction: $what is not allowed in $where" + + context.error(tree.pos, s"implementation restriction: $what is not allowed in $where" + "\nThis restriction is planned to be removed in subsequent releases.") /** * Deeply traverses the tree in search of constructs that are not allowed @@ -1337,7 +1322,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } } for (stat <- body) { - def notAllowed(what: String) = unit.error(stat.pos, s"$what is not allowed in $where") + def notAllowed(what: String) = context.error(stat.pos, s"$what is not allowed in $where") stat match { // see https://issues.scala-lang.org/browse/SI-6444 // see https://issues.scala-lang.org/browse/SI-6463 @@ -1365,9 +1350,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper private def validateDerivedValueClass(clazz: Symbol, body: List[Tree]) = { if (clazz.isTrait) - unit.error(clazz.pos, "only classes (not traits) are allowed to extend AnyVal") + context.error(clazz.pos, "only classes (not traits) are allowed to extend AnyVal") if (!clazz.isStatic) - unit.error(clazz.pos, "value class may not be a "+ + context.error(clazz.pos, "value class may not be a "+ (if (clazz.owner.isTerm) "local class" else "member of another class")) if (!clazz.isPrimitiveValueClass) { clazz.primaryConstructor.paramss match { @@ -1375,26 +1360,26 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val decls = clazz.info.decls val paramAccessor = clazz.constrParamAccessors.head if (paramAccessor.isMutable) - unit.error(paramAccessor.pos, "value class parameter must not be a var") + context.error(paramAccessor.pos, "value class parameter must not be a var") val accessor = decls.toList.find(x => x.isMethod && x.accessedOrSelf == paramAccessor) accessor match { case None => - unit.error(paramAccessor.pos, "value class parameter must be a val and not be private[this]") + context.error(paramAccessor.pos, "value class parameter must be a val and not be private[this]") case Some(acc) if acc.isProtectedLocal => - unit.error(paramAccessor.pos, "value class parameter must not be protected[this]") + context.error(paramAccessor.pos, "value class parameter must not be protected[this]") case Some(acc) => if (acc.tpe.typeSymbol.isDerivedValueClass) - unit.error(acc.pos, "value class may not wrap another user-defined value class") + context.error(acc.pos, "value class may not wrap another user-defined value class") checkEphemeral(clazz, body filterNot (stat => stat.symbol != null && stat.symbol.accessedOrSelf == paramAccessor)) } case _ => - unit.error(clazz.pos, "value class needs to have exactly one val parameter") + context.error(clazz.pos, "value class needs to have exactly one val parameter") } } for (tparam <- clazz.typeParams) if (tparam hasAnnotation definitions.SpecializedClass) - unit.error(tparam.pos, "type parameter of value class may not be specialized") + context.error(tparam.pos, "type parameter of value class may not be specialized") } /** Typechecks a parent type reference. @@ -1687,10 +1672,10 @@ 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) + context.deprecationWarning(parent.pos, psym, msg) } if (psym.isSealed && !phase.erasedTypes) @@ -1757,7 +1742,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if ((clazz isNonBottomSubClass ClassfileAnnotationClass) && (clazz != ClassfileAnnotationClass)) { if (!clazz.owner.isPackageClass) - unit.error(clazz.pos, "inner classes cannot be classfile annotations") + context.error(clazz.pos, "inner classes cannot be classfile annotations") else restrictionWarning(cdef.pos, unit, """|subclassing Classfile does not |make your annotation visible at runtime. If that is what @@ -1812,7 +1797,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper private def ensurePredefParentsAreInSameSourceFile(template: Template) = { val parentSyms = template.parents map (_.symbol) filterNot (_ == AnyRefClass) if (parentSyms exists (_.associatedFile != PredefModule.associatedFile)) - unit.error(template.pos, s"All parents of Predef must be defined in ${PredefModule.associatedFile}.") + context.error(template.pos, s"All parents of Predef must be defined in ${PredefModule.associatedFile}.") } /** In order to override this in the TreeCheckers Typer so synthetics aren't re-added * all the time, it is exposed here the module/class typing methods go through it. @@ -1883,7 +1868,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper ConstrArgsInParentOfTraitError(parents1.head, clazz) if ((clazz isSubClass ClassfileAnnotationClass) && !clazz.isTopLevel) - unit.error(clazz.pos, "inner classes cannot be classfile annotations") + context.error(clazz.pos, "inner classes cannot be classfile annotations") if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) @@ -1911,7 +1896,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (clazz.isTrait) { for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) { - unit.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.") + context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.") } } @@ -2099,7 +2084,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper case xs => xs.map(_.nameString).mkString(" (of ", " with ", ")") } def fail(pos: Position, msg: String): Boolean = { - unit.error(pos, msg) + context.error(pos, msg) false } /* Have to examine all parameters in all lists. @@ -3022,7 +3007,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 +3042,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 +3089,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) } } @@ -3260,25 +3248,25 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper * to that. This is the last thing which is tried (after * default arguments) */ - def tryTupleApply: Tree = ( + def tryTupleApply: Tree = { if (eligibleForTupleConversion(paramTypes, argslen) && !phase.erasedTypes) { val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args))) // expected one argument, but got 0 or >1 ==> try applying to tuple // the inner "doTypedApply" does "extractUndetparams" => restore when it fails val savedUndetparams = context.undetparams silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)) map { t => - // Depending on user options, may warn or error here if - // a Unit or tuple was inserted. - val keepTree = ( - !mode.typingExprNotFun - || t.symbol == null - || checkValidAdaptation(t, args) - ) - if (keepTree) t else EmptyTree + // Depending on user options, may warn or error here if + // a Unit or tuple was inserted. + val keepTree = ( + !mode.typingExprNotFun // why? introduced in 4e488a60, doc welcome + || t.symbol == null // ditto + || checkValidAdaptation(t, args) + ) + if (keepTree) t else EmptyTree } orElse { _ => context.undetparams = savedUndetparams ; EmptyTree } } else EmptyTree - ) + } /* Treats an application which uses named or default arguments. * Also works if names + a vararg used: when names are used, the vararg @@ -3677,7 +3665,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper } if (annType.typeSymbol == DeprecatedAttr && argss.flatten.size < 2) - unit.deprecationWarning(ann.pos, "@deprecated now takes two arguments; see the scaladoc.") + context.deprecationWarning(ann.pos, DeprecatedAttr, "@deprecated now takes two arguments; see the scaladoc.") if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation else annInfo(typedAnn) @@ -4251,7 +4239,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // it is non-Unit) so we have to retype it. Fortunately it won't come up much // unless the warning is legitimate. if (typed(expr).tpe.typeSymbol != UnitClass) - unit.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded") + context.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded") } val res = treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner) val tp = pluginsTypedReturn(NothingTpe, this, res, restpt.tpe) @@ -4396,7 +4384,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (retry) { val Select(qual, name) = fun tryTypedArgs(args, forArgMode(fun, mode)) match { - case Some(args1) => + case Some(args1) if !args1.exists(arg => arg.exists(_.isErroneous)) => val qual1 = if (!pt.isError) adaptToArguments(qual, name, args1, pt, reportAmbiguous = true, saveErrors = true) else qual @@ -4709,10 +4697,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // temporarily use `filter` as an alternative for `withFilter` def tryWithFilterAndFilter(tree: Select, qual: Tree): Tree = { - def warn() = unit.deprecationWarning(tree.pos, s"`withFilter' method does not yet exist on ${qual.tpe.widen}, using `filter' method instead") + def warn(sym: Symbol) = context.deprecationWarning(tree.pos, sym, s"`withFilter' method does not yet exist on ${qual.tpe.widen}, using `filter' method instead") silent(_ => typedSelect(tree, qual, nme.withFilter)) orElse { _ => silent(_ => typed1(Select(qual, nme.filter) setPos tree.pos, mode, pt)) match { - case SilentResultValue(res) => warn() ; res + case SilentResultValue(res) => warn(res.symbol) ; res case SilentTypeError(err) => WithFilterError(tree, err) } } @@ -5489,11 +5477,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val commonMessage = "macro defs must have explicitly specified return types" def reportFailure() = { ddef.symbol.setFlag(IS_ERROR) - unit.error(ddef.pos, commonMessage) + context.error(ddef.pos, commonMessage) } def reportWarning(inferredType: Type) = { val explanation = s"inference of $inferredType from macro impl's c.Expr[$inferredType] is deprecated and is going to stop working in 2.12" - unit.deprecationWarning(ddef.pos, s"$commonMessage ($explanation)") + context.deprecationWarning(ddef.pos, ddef.symbol, s"$commonMessage ($explanation)") } computeMacroDefTypeFromMacroImplRef(ddef, rhs1) match { case ErrorType => ErrorType diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala index e0f9bb6044..b445f1e2bb 100644 --- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala +++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala @@ -117,7 +117,7 @@ abstract class FormatInterpolator { c.error(errPoint, msg("unsupported")) s0 } else { - c.enclosingUnit.deprecationWarning(errPoint, msg("deprecated")) + currentRun.reporting.deprecationWarning(errPoint, msg("deprecated")) try StringContext.treatEscapes(s0) catch escapeHatch } } @@ -182,13 +182,23 @@ abstract class FormatInterpolator { case (part, n) => copyPart(part, n) } - //q"{..$evals; ${fstring.toString}.format(..$ids)}" - locally { + //q"{..$evals; new StringOps(${fstring.toString}).format(..$ids)}" + val format = fstring.toString + if (ids.isEmpty && !format.contains("%")) Literal(Constant(format)) + else { + val scalaPackage = Select(Ident(nme.ROOTPKG), TermName("scala")) + val newStringOps = Select( + New(Select(Select(Select(scalaPackage, + TermName("collection")), TermName("immutable")), TypeName("StringOps"))), + termNames.CONSTRUCTOR + ) val expr = Apply( Select( - Literal(Constant(fstring.toString)), - newTermName("format")), + Apply( + newStringOps, + List(Literal(Constant(format)))), + TermName("format")), ids.toList ) val p = c.macroApplication.pos diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3b12086cc7..923297bafb 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -141,6 +141,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => val run = new Run run.symSource(ownerClass) = NoAbstractFile // need to set file to something different from null, so that currentRun.defines works phase = run.typerPhase // need to set a phase to something <= typerPhase, otherwise implicits in typedSelect will be disabled + globalPhase = run.typerPhase // amazing... looks like phase and globalPhase are different things, so we need to set them separately currentTyper.context.setReportErrors() // need to manually set context mode, otherwise typer.silent will throw exceptions reporter.reset() diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala index b68022afd9..392b7fc881 100644 --- a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala +++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala @@ -69,9 +69,16 @@ trait Parsers { self: Quasiquotes => override def makeTupleType(trees: List[Tree]): Tree = TupleTypePlaceholder(trees) // q"{ $x }" - override def makeBlock(stats: List[Tree]): Tree = stats match { - case (head @ Ident(name)) :: Nil if isHole(name) => Block(Nil, head) - case _ => super.makeBlock(stats) + override def makeBlock(stats: List[Tree]): Tree = method match { + case nme.apply => + stats match { + // we don't want to eagerly flatten trees with placeholders as they + // might have to be wrapped into a block depending on their value + case (head @ Ident(name)) :: Nil if isHole(name) => Block(Nil, head) + case _ => gen.mkBlock(stats, doFlatten = true) + } + case nme.unapply => gen.mkBlock(stats, doFlatten = false) + case other => global.abort("unreachable") } // tq"$a => $b" diff --git a/src/interactive/scala/tools/nsc/interactive/Main.scala b/src/interactive/scala/tools/nsc/interactive/Main.scala index c838606f02..7796c65670 100644 --- a/src/interactive/scala/tools/nsc/interactive/Main.scala +++ b/src/interactive/scala/tools/nsc/interactive/Main.scala @@ -12,7 +12,7 @@ package interactive */ object Main extends nsc.MainClass { override def processSettingsHook(): Boolean = { - if (this.settings.Yidedebug) { + def run(): Unit = { this.settings.Xprintpos.value = true this.settings.Yrangepos.value = true val compiler = new interactive.Global(this.settings, this.reporter) @@ -27,8 +27,9 @@ object Main extends nsc.MainClass { case None => reporter.reset() // Causes other compiler errors to be ignored } askShutdown - false } - else true + super.processSettingsHook() && ( + if (this.settings.Yidedebug) { run() ; false } else true + ) } } 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/IndexedSeqOptimized.scala b/src/library/scala/collection/IndexedSeqOptimized.scala index ade04e4de8..42cb37aa24 100755 --- a/src/library/scala/collection/IndexedSeqOptimized.scala +++ b/src/library/scala/collection/IndexedSeqOptimized.scala @@ -206,7 +206,7 @@ trait IndexedSeqOptimized[+A, +Repr] extends Any with IndexedSeqLike[A, Repr] { override /*SeqLike*/ def lastIndexWhere(p: A => Boolean, end: Int): Int = { - var i = end + var i = math.min(end, length - 1) while (i >= 0 && !p(this(i))) i -= 1 i } diff --git a/src/library/scala/collection/Iterable.scala b/src/library/scala/collection/Iterable.scala index a5ab8efd5c..afbffd36c6 100644 --- a/src/library/scala/collection/Iterable.scala +++ b/src/library/scala/collection/Iterable.scala @@ -38,7 +38,7 @@ trait Iterable[+A] extends Traversable[A] } /** $factoryInfo - * The current default implementation of a $Coll is a `Vector`. + * The current default implementation of a $Coll is a `List`. * @define coll iterable collection * @define Coll `Iterable` */ 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/Searching.scala b/src/library/scala/collection/Searching.scala index fec4bbf502..b68124b3f8 100644 --- a/src/library/scala/collection/Searching.scala +++ b/src/library/scala/collection/Searching.scala @@ -54,7 +54,7 @@ object Searching { */ final def search[B >: A](elem: B)(implicit ord: Ordering[B]): SearchResult = coll match { - case _: IndexedSeq[A] => binarySearch(elem, -1, coll.length)(ord) + case _: IndexedSeq[A] => binarySearch(elem, 0, coll.length)(ord) case _ => linearSearch(coll.view, elem, 0)(ord) } @@ -81,18 +81,18 @@ object Searching { final def search[B >: A](elem: B, from: Int, to: Int) (implicit ord: Ordering[B]): SearchResult = coll match { - case _: IndexedSeq[A] => binarySearch(elem, from-1, to)(ord) + case _: IndexedSeq[A] => binarySearch(elem, from, to)(ord) case _ => linearSearch(coll.view(from, to), elem, from)(ord) } @tailrec private def binarySearch[B >: A](elem: B, from: Int, to: Int) (implicit ord: Ordering[B]): SearchResult = { - if ((to-from) == 1) InsertionPoint(from) else { - val idx = from+(to-from)/2 + if (to == from) InsertionPoint(from) else { + val idx = from+(to-from-1)/2 math.signum(ord.compare(elem, coll(idx))) match { case -1 => binarySearch(elem, from, idx)(ord) - case 1 => binarySearch(elem, idx, to)(ord) + case 1 => binarySearch(elem, idx + 1, to)(ord) case _ => Found(idx) } } @@ -105,7 +105,7 @@ object Searching { while (it.hasNext) { val cur = it.next() if (ord.equiv(elem, cur)) return Found(idx) - else if (ord.lt(elem, cur)) return InsertionPoint(idx-1) + else if (ord.lt(elem, cur)) return InsertionPoint(idx) idx += 1 } InsertionPoint(idx) diff --git a/src/library/scala/collection/SetLike.scala b/src/library/scala/collection/SetLike.scala index 0c5c7e0b29..3e549f72cd 100644 --- a/src/library/scala/collection/SetLike.scala +++ b/src/library/scala/collection/SetLike.scala @@ -17,6 +17,9 @@ import parallel.ParSet /** A template trait for sets. * * $setNote + * '''Implementation note:''' + * This trait provides most of the operations of a `Set` independently of its representation. + * It is typically inherited by concrete implementations of sets. * $setTags * @since 2.8 * @@ -24,10 +27,6 @@ import parallel.ParSet * * A set is a collection that contains no duplicate elements. * - * '''Implementation note:''' - * This trait provides most of the operations of a `Set` independently of its representation. - * It is typically inherited by concrete implementations of sets. - * * To implement a concrete set, you need to provide implementations of the * following methods: * {{{ diff --git a/src/library/scala/collection/Traversable.scala b/src/library/scala/collection/Traversable.scala index b53724c568..a35750a35f 100644 --- a/src/library/scala/collection/Traversable.scala +++ b/src/library/scala/collection/Traversable.scala @@ -87,7 +87,7 @@ trait Traversable[+A] extends TraversableLike[A, Traversable[A]] } /** $factoryInfo - * The current default implementation of a $Coll is a `Vector`. + * The current default implementation of a $Coll is a `List`. */ object Traversable extends TraversableFactory[Traversable] { self => 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/convert/DecorateAsScala.scala b/src/library/scala/collection/convert/DecorateAsScala.scala index c724831c54..5448f5f91c 100644 --- a/src/library/scala/collection/convert/DecorateAsScala.scala +++ b/src/library/scala/collection/convert/DecorateAsScala.scala @@ -135,6 +135,12 @@ trait DecorateAsScala { * If the Java `Map` was previously obtained from an implicit or explicit * call of `asMap(scala.collection.mutable.Map)` then the original * Scala `Map` will be returned. + * + * If the wrapped map is synchronized (e.g. from `java.util.Collections.synchronizedMap`), + * it is your responsibility to wrap all + * non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an + * atomic `get` when `null` values may be present. * * @param m The `Map` to be converted. * @return An object with an `asScala` method that returns a Scala mutable diff --git a/src/library/scala/collection/convert/WrapAsScala.scala b/src/library/scala/collection/convert/WrapAsScala.scala index d4ab451b0d..ab151a6778 100644 --- a/src/library/scala/collection/convert/WrapAsScala.scala +++ b/src/library/scala/collection/convert/WrapAsScala.scala @@ -133,7 +133,13 @@ trait WrapAsScala { * If the Java `Map` was previously obtained from an implicit or * explicit call of `mapAsScalaMap(scala.collection.mutable.Map)` then * the original Scala Map will be returned. - * + * + * If the wrapped map is synchronized (e.g. from `java.util.Collections.synchronizedMap`), + * it is your responsibility to wrap all + * non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an + * atomic `get` when `null` values may be present. + * * @param m The Map to be converted. * @return A Scala mutable Map view of the argument. */ diff --git a/src/library/scala/collection/convert/Wrappers.scala b/src/library/scala/collection/convert/Wrappers.scala index 7d1d6b3781..9f9732c62f 100644 --- a/src/library/scala/collection/convert/Wrappers.scala +++ b/src/library/scala/collection/convert/Wrappers.scala @@ -288,6 +288,13 @@ private[collection] trait Wrappers { override def empty: Repr = null.asInstanceOf[Repr] } + /** Wraps a Java map as a Scala one. If the map is to support concurrent access, + * use [[JConcurrentMapWrapper]] instead. If the wrapped map is synchronized + * (e.g. from `java.util.Collections.synchronizedMap`), it is your responsibility + * to wrap all non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an + * atomic `get` when `null` values may be present. + */ case class JMapWrapper[A, B](underlying : ju.Map[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JMapWrapper[A, B]] { override def empty = JMapWrapper(new ju.HashMap[A, B]) } @@ -314,6 +321,10 @@ private[collection] trait Wrappers { def replace(k: A, oldval: B, newval: B) = underlying.replace(k, oldval, newval) } + /** Wraps a concurrent Java map as a Scala one. Single-element concurrent + * access is supported; multi-element operations such as maps and filters + * are not guaranteed to be atomic. + */ case class JConcurrentMapWrapper[A, B](underlying: juc.ConcurrentMap[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]] with concurrent.Map[A, B] { override def get(k: A) = { val v = underlying get k 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/Iterable.scala b/src/library/scala/collection/immutable/Iterable.scala index 6e4eb1e45f..df322396d0 100644 --- a/src/library/scala/collection/immutable/Iterable.scala +++ b/src/library/scala/collection/immutable/Iterable.scala @@ -35,6 +35,7 @@ trait Iterable[+A] extends Traversable[A] } /** $factoryInfo + * The current default implementation of a $Coll is a `List`. * @define Coll `immutable.Iterable` * @define coll immutable iterable collection */ diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index 930e13a9d3..aa9dec2761 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -190,11 +190,9 @@ sealed abstract class List[+A] extends AbstractSeq[A] // Overridden methods from IterableLike and SeqLike or overloaded variants of such methods - override def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That = { - val b = bf(this) - if (b.isInstanceOf[ListBuffer[_]]) (this ::: that.seq.toList).asInstanceOf[That] + override def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[List[A], B, That]): That = + if (bf eq List.ReusableCBF) (this ::: that.seq.toList).asInstanceOf[That] else super.++(that) - } override def +:[B >: A, That](elem: B)(implicit bf: CanBuildFrom[List[A], B, That]): That = bf match { case _: List.GenericCanBuildFrom[_] => (elem :: this).asInstanceOf[That] 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/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index 60de147477..d3ff5e8abf 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -97,6 +97,14 @@ import scala.language.implicitConversions * If, on the other hand, there is nothing holding on to the head (e.g. we used * `def` to define the `Stream`) then once it is no longer being used directly, * it disappears. + * + * - Note that some operations, including [[drop]], [[dropWhile]], + * [[flatMap]] or [[collect]] may process a large number of intermediate + * elements before returning. These necessarily hold onto the head, since + * they are methods on `Stream`, and a stream holds its own head. For + * computations of this sort where memoization is not desired, use + * `Iterator` when possible. + * * {{{ * // For example, let's build the natural numbers and do some silly iteration * // over them. diff --git a/src/library/scala/collection/immutable/Traversable.scala b/src/library/scala/collection/immutable/Traversable.scala index 775d635fae..5fc0607a00 100644 --- a/src/library/scala/collection/immutable/Traversable.scala +++ b/src/library/scala/collection/immutable/Traversable.scala @@ -29,7 +29,7 @@ trait Traversable[+A] extends scala.collection.Traversable[A] } /** $factoryInfo - * The current default implementation of a $Coll is a `Vector`. + * The current default implementation of a $Coll is a `List`. * @define coll immutable traversable collection * @define Coll `immutable.Traversable` */ 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/collection/mutable/SetLike.scala b/src/library/scala/collection/mutable/SetLike.scala index d749167870..a377b03124 100644 --- a/src/library/scala/collection/mutable/SetLike.scala +++ b/src/library/scala/collection/mutable/SetLike.scala @@ -16,19 +16,20 @@ import scala.annotation.migration import parallel.mutable.ParSet /** A template trait for mutable sets of type `mutable.Set[A]`. + * + * This trait provides most of the operations of a `mutable.Set` independently of its representation. + * It is typically inherited by concrete implementations of sets. + * + * $setNote + * * @tparam A the type of the elements of the set * @tparam This the type of the set itself. * - * $setnote - * * @author Martin Odersky * @version 2.8 * @since 2.8 * - * @define setnote - * @note - * This trait provides most of the operations of a `mutable.Set` independently of its representation. - * It is typically inherited by concrete implementations of sets. + * @define setNote * * To implement a concrete mutable set, you need to provide implementations * of the following methods: @@ -36,13 +37,13 @@ import parallel.mutable.ParSet * def contains(elem: A): Boolean * def iterator: Iterator[A] * def += (elem: A): this.type - * def -= (elem: A): this.type</pre> + * def -= (elem: A): this.type * }}} * If you wish that methods like `take`, * `drop`, `filter` return the same kind of set, * you should also override: * {{{ - * def empty: This</pre> + * def empty: This * }}} * It is also good idea to override methods `foreach` and * `size` for efficiency. diff --git a/src/library/scala/collection/mutable/UnrolledBuffer.scala b/src/library/scala/collection/mutable/UnrolledBuffer.scala index 1f89199bdc..693c47d86e 100644 --- a/src/library/scala/collection/mutable/UnrolledBuffer.scala +++ b/src/library/scala/collection/mutable/UnrolledBuffer.scala @@ -300,27 +300,33 @@ object UnrolledBuffer extends ClassTagTraversableFactory[UnrolledBuffer] { if (next eq null) true else false // checks if last node was thrown out } else false - @tailrec final def insertAll(idx: Int, t: scala.collection.Traversable[T], buffer: UnrolledBuffer[T]): Unit = if (idx < size) { - // divide this node at the appropriate position and insert all into head - // update new next - val newnextnode = new Unrolled[T](0, new Array(array.length), null, buff) - Array.copy(array, idx, newnextnode.array, 0, size - idx) - newnextnode.size = size - idx - newnextnode.next = next - - // update this - nullout(idx, size) - size = idx - next = null - - // insert everything from iterable to this - var curr = this - for (elem <- t) curr = curr append elem - curr.next = newnextnode - - // try to merge the last node of this with the newnextnode - if (curr.tryMergeWithNext()) buffer.lastPtr = curr - } else insertAll(idx - size, t, buffer) + @tailrec final def insertAll(idx: Int, t: scala.collection.Traversable[T], buffer: UnrolledBuffer[T]): Unit = { + if (idx < size) { + // divide this node at the appropriate position and insert all into head + // update new next + val newnextnode = new Unrolled[T](0, new Array(array.length), null, buff) + Array.copy(array, idx, newnextnode.array, 0, size - idx) + newnextnode.size = size - idx + newnextnode.next = next + + // update this + nullout(idx, size) + size = idx + next = null + + // insert everything from iterable to this + var curr = this + for (elem <- t) curr = curr append elem + curr.next = newnextnode + + // try to merge the last node of this with the newnextnode + if (curr.tryMergeWithNext()) buffer.lastPtr = curr + } + else if (idx == size) { + var curr = this + for (elem <- t) curr = curr append elem + } else insertAll(idx - size, t, buffer) + } private def nullout(from: Int, until: Int) { var idx = from while (idx < until) { diff --git a/src/library/scala/concurrent/Channel.scala b/src/library/scala/concurrent/Channel.scala index 067244bd1c..89ad7d8c0e 100644 --- a/src/library/scala/concurrent/Channel.scala +++ b/src/library/scala/concurrent/Channel.scala @@ -10,8 +10,10 @@ package scala.concurrent -/** This class ... +/** This class provides a simple FIFO queue of data objects, + * which are read by one or more reader threads. * + * @tparam A type of data exchanged * @author Martin Odersky * @version 1.0, 10/03/2003 */ @@ -20,11 +22,14 @@ class Channel[A] { var elem: A = _ var next: LinkedList[A] = null } - private var written = new LinkedList[A] // FIFO buffer, realized through + private var written = new LinkedList[A] // FIFO queue, realized through private var lastWritten = written // aliasing of a linked list private var nreaders = 0 - /** + /** Append a value to the FIFO queue to be read by `read`. + * This operation is nonblocking and can be executed by any thread. + * + * @param x object to enqueue to this channel */ def write(x: A) = synchronized { lastWritten.elem = x @@ -33,6 +38,11 @@ class Channel[A] { if (nreaders > 0) notify() } + /** Retrieve the next waiting object from the FIFO queue, + * blocking if necessary until an object is available. + * + * @return next object dequeued from this channel + */ def read: A = synchronized { while (written.next == null) { try { @@ -45,5 +55,4 @@ class Channel[A] { written = written.next x } - } diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala index a1e94c8876..11d3bb8b02 100644 --- a/src/library/scala/concurrent/ExecutionContext.scala +++ b/src/library/scala/concurrent/ExecutionContext.scala @@ -61,28 +61,44 @@ or import scala.concurrent.ExecutionContext.Implicits.global.""") trait ExecutionContext { /** Runs a block of code on this execution context. + * + * @param runnable the task to execute */ def execute(runnable: Runnable): Unit /** Reports that an asynchronous computation failed. + * + * @param cause the cause of the failure */ def reportFailure(@deprecatedName('t) cause: Throwable): Unit - /** Prepares for the execution of a task. Returns the prepared - * execution context. A valid implementation of `prepare` is one - * that simply returns `this`. + /** Prepares for the execution of a task. Returns the prepared execution context. + * + * `prepare` should be called at the site where an `ExecutionContext` is received (for + * example, through an implicit method parameter). The returned execution context may + * then be used to execute tasks. The role of `prepare` is to save any context relevant + * to an execution's ''call site'', so that this context may be restored at the + * ''execution site''. (These are often different: for example, execution may be + * suspended through a `Promise`'s future until the `Promise` is completed, which may + * be done in another thread, on another stack.) + * + * Note: a valid implementation of `prepare` is one that simply returns `this`. + * + * @return the prepared execution context */ def prepare(): ExecutionContext = this } /** - * Union interface since Java does not support union types + * An [[ExecutionContext]] that is also a + * Java [[http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html Executor]]. */ trait ExecutionContextExecutor extends ExecutionContext with Executor /** - * Union interface since Java does not support union types + * An [[ExecutionContext]] that is also a + * Java [[http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html ExecutorService]]. */ trait ExecutionContextExecutorService extends ExecutionContextExecutor with ExecutorService @@ -91,38 +107,70 @@ trait ExecutionContextExecutorService extends ExecutionContextExecutor with Exec */ object ExecutionContext { /** - * This is the explicit global ExecutionContext, - * call this when you want to provide the global ExecutionContext explicitly + * The explicit global `ExecutionContext`. Invoke `global` when you want to provide the global + * `ExecutionContext` explicitly. + * + * The default `ExecutionContext` implementation is backed by a port of + * [[http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166-4jdk7docs/java/util/concurrent/ForkJoinPool.html java.util.concurrent.ForkJoinPool]]. + * + * @return the global `ExecutionContext` */ def global: ExecutionContextExecutor = Implicits.global object Implicits { /** - * This is the implicit global ExecutionContext, - * import this when you want to provide the global ExecutionContext implicitly + * The implicit global `ExecutionContext`. Import `global` when you want to provide the global + * `ExecutionContext` implicitly. + * + * The default `ExecutionContext` implementation is backed by a port of + * [[http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166-4jdk7docs/java/util/concurrent/ForkJoinPool.html java.util.concurrent.ForkJoinPool]]. */ implicit lazy val global: ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(null: Executor) } /** Creates an `ExecutionContext` from the given `ExecutorService`. + * + * @param e the `ExecutorService` to use + * @param reporter a function for error reporting + * @return the `ExecutionContext` using the given `ExecutorService` */ def fromExecutorService(e: ExecutorService, reporter: Throwable => Unit): ExecutionContextExecutorService = impl.ExecutionContextImpl.fromExecutorService(e, reporter) - /** Creates an `ExecutionContext` from the given `ExecutorService` with the default Reporter. + /** Creates an `ExecutionContext` from the given `ExecutorService` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. + * + * If it is guaranteed that none of the executed tasks are blocking, a single-threaded `ExecutorService` + * can be used to create an `ExecutionContext` as follows: + * + * {{{ + * import java.util.concurrent.Executors + * val ec = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor()) + * }}} + * + * @param e the `ExecutorService` to use + * @return the `ExecutionContext` using the given `ExecutorService` */ def fromExecutorService(e: ExecutorService): ExecutionContextExecutorService = fromExecutorService(e, defaultReporter) /** Creates an `ExecutionContext` from the given `Executor`. + * + * @param e the `Executor` to use + * @param reporter a function for error reporting + * @return the `ExecutionContext` using the given `Executor` */ def fromExecutor(e: Executor, reporter: Throwable => Unit): ExecutionContextExecutor = impl.ExecutionContextImpl.fromExecutor(e, reporter) - /** Creates an `ExecutionContext` from the given `Executor` with the default Reporter. + /** Creates an `ExecutionContext` from the given `Executor` with the [[scala.concurrent.ExecutionContext$.defaultReporter default reporter]]. + * + * @param e the `Executor` to use + * @return the `ExecutionContext` using the given `Executor` */ def fromExecutor(e: Executor): ExecutionContextExecutor = fromExecutor(e, defaultReporter) - /** The default reporter simply prints the stack trace of the `Throwable` to System.err. + /** The default reporter simply prints the stack trace of the `Throwable` to [[http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#err System.err]]. + * + * @return the function for error reporting */ def defaultReporter: Throwable => Unit = _.printStackTrace() } 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/library/scala/concurrent/SyncVar.scala b/src/library/scala/concurrent/SyncVar.scala index d5dc3d7e3f..494c955833 100644 --- a/src/library/scala/concurrent/SyncVar.scala +++ b/src/library/scala/concurrent/SyncVar.scala @@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit /** A class to provide safe concurrent access to a mutable cell. * All methods are synchronized. * + * @tparam A type of the contained value * @author Martin Odersky * @version 1.0, 10/03/2003 */ @@ -20,6 +21,12 @@ class SyncVar[A] { private var isDefined: Boolean = false private var value: Option[A] = None + /** + * Waits for this SyncVar to become defined and returns + * the result, without modifying the stored value. + * + * @return value that is held in this container + */ def get: A = synchronized { while (!isDefined) wait() value.get @@ -57,8 +64,12 @@ class SyncVar[A] { value } - /** Waits for this SyncVar to become defined and returns - * the result */ + /** + * Waits for this SyncVar to become defined and returns + * the result, unsetting the stored value before returning. + * + * @return value that was held in this container + */ def take(): A = synchronized { try get finally unsetVal() @@ -129,4 +140,3 @@ class SyncVar[A] { } } - diff --git a/src/library/scala/concurrent/duration/Duration.scala b/src/library/scala/concurrent/duration/Duration.scala index 1b50b7fa56..2eded9f060 100644 --- a/src/library/scala/concurrent/duration/Duration.scala +++ b/src/library/scala/concurrent/duration/Duration.scala @@ -621,7 +621,7 @@ final class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duratio } def -(other: Duration) = other match { case x: FiniteDuration => add(-x.length, x.unit) - case _ => other + case _ => -other } def *(factor: Double) = diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index cc1350f5a9..4d88253de4 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -55,6 +55,11 @@ package object concurrent { } package concurrent { + /** + * This marker trait is used by [[Await]] to ensure that [[Awaitable.ready]] and [[Awaitable.result]] + * are not directly called by user code. An implicit instance of this trait is only available when + * user code is currently calling the methods on [[Await]]. + */ @implicitNotFound("Don't call `Awaitable` methods directly, use the `Await` object.") sealed trait CanAwait diff --git a/src/library/scala/io/BufferedSource.scala b/src/library/scala/io/BufferedSource.scala index 1c87a1f421..52fa525b24 100644 --- a/src/library/scala/io/BufferedSource.scala +++ b/src/library/scala/io/BufferedSource.scala @@ -93,7 +93,7 @@ class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val cod val buf = new Array[Char](bufferSize) var n = 0 while (n != -1) { - n = charReader.read(buf) + n = allReader.read(buf) if (n>0) sb.appendAll(buf, 0, n) } sb.result diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index 33c5cee783..bced505273 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -70,26 +70,36 @@ trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serial * `SomeExtractor(...)` is turned into `ct(SomeExtractor(...))` if `T` in `SomeExtractor.unapply(x: T)` * is uncheckable, but we have an instance of `ClassTag[T]`. */ - def unapply(x: Any): Option[T] = unapply_impl(x) - def unapply(x: Byte): Option[T] = unapply_impl(x) - def unapply(x: Short): Option[T] = unapply_impl(x) - def unapply(x: Char): Option[T] = unapply_impl(x) - def unapply(x: Int): Option[T] = unapply_impl(x) - def unapply(x: Long): Option[T] = unapply_impl(x) - def unapply(x: Float): Option[T] = unapply_impl(x) - def unapply(x: Double): Option[T] = unapply_impl(x) - def unapply(x: Boolean): Option[T] = unapply_impl(x) - def unapply(x: Unit): Option[T] = unapply_impl(x) + def unapply(x: Any): Option[T] = x match { + case null => None + case b: Byte => unapply(b) + case s: Short => unapply(s) + case c: Char => unapply(c) + case i: Int => unapply(i) + case l: Long => unapply(l) + case f: Float => unapply(f) + case d: Double => unapply(d) + case b: Boolean => unapply(b) + case u: Unit => unapply(u) + case a: Any => unapplyImpl(a) + } - private def unapply_impl[U: ClassTag](x: U): Option[T] = - if (x == null) None - else { - val staticClass = classTag[U].runtimeClass - val dynamicClass = x.getClass - val effectiveClass = if (staticClass.isPrimitive) staticClass else dynamicClass - val conforms = runtimeClass.isAssignableFrom(effectiveClass) - if (conforms) Some(x.asInstanceOf[T]) else None - } + // TODO: Inline the bodies of these into the Any-accepting unapply overload above and delete them. + // This cannot be done until at least 2.12.0 for reasons of binary compatibility + def unapply(x: Byte) : Option[T] = unapplyImpl(x, classOf[Byte]) + def unapply(x: Short) : Option[T] = unapplyImpl(x, classOf[Short]) + def unapply(x: Char) : Option[T] = unapplyImpl(x, classOf[Char]) + def unapply(x: Int) : Option[T] = unapplyImpl(x, classOf[Int]) + def unapply(x: Long) : Option[T] = unapplyImpl(x, classOf[Long]) + def unapply(x: Float) : Option[T] = unapplyImpl(x, classOf[Float]) + def unapply(x: Double) : Option[T] = unapplyImpl(x, classOf[Double]) + def unapply(x: Boolean) : Option[T] = unapplyImpl(x, classOf[Boolean]) + def unapply(x: Unit) : Option[T] = unapplyImpl(x, classOf[Unit]) + + private[this] def unapplyImpl(x: Any, alternative: jClass[_] = null): Option[T] = { + val conforms = runtimeClass.isAssignableFrom(x.getClass) || (alternative != null && runtimeClass.isAssignableFrom(alternative)) + if (conforms) Some(x.asInstanceOf[T]) else None + } // case class accessories override def canEqual(x: Any) = x.isInstanceOf[ClassTag[_]] diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index d597feb898..2daa4de9a6 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -155,9 +155,12 @@ private[scala] trait PropertiesTrait { // This is looking for javac, tools.jar, etc. // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, // and finally the system property based javaHome. - def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) + def jdkHome = envOrElse("JDK_HOME", envOrElse("JAVA_HOME", javaHome)) - def versionMsg = "Scala %s %s -- %s".format(propCategory, versionString, copyrightString) + // private[scala] for 2.12 + private[this] def versionFor(command: String) = f"Scala $command $versionString -- $copyrightString" + + def versionMsg = versionFor(propCategory) def scalaCmd = if (isWin) "scala.bat" else "scala" def scalacCmd = if (isWin) "scalac.bat" else "scalac" diff --git a/src/library/scala/util/matching/Regex.scala b/src/library/scala/util/matching/Regex.scala index 6743b9e42a..f35ea566ba 100644 --- a/src/library/scala/util/matching/Regex.scala +++ b/src/library/scala/util/matching/Regex.scala @@ -274,12 +274,18 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends // @see UnanchoredRegex protected def runMatcher(m: Matcher) = m.matches() - /** Return all matches of this regexp in given character sequence as a [[scala.util.matching.Regex.MatchIterator]], + /** Return all non-overlapping matches of this regexp in given character + * sequence as a [[scala.util.matching.Regex.MatchIterator]], * which is a special [[scala.collection.Iterator]] that returns the * matched strings, but can also be converted into a normal iterator * that returns objects of type [[scala.util.matching.Regex.Match]] * that can be queried for data such as the text that precedes the * match, subgroups, etc. + * + * Where potential matches overlap, the first possible match is returned, + * followed by the next match that is completely after the first. For + * instance, `"hat[^a]+".r` will match `hath` and `hattth` in the string + * `"hathatthattthatttt"`. * * Attempting to retrieve information about a match before initializing * the iterator can result in [[java.lang.IllegalStateException]]s. See @@ -292,7 +298,7 @@ class Regex private[matching](val pattern: Pattern, groupNames: String*) extends def findAllIn(source: CharSequence) = new Regex.MatchIterator(source, this, groupNames) - /** Return all matches of this regexp in given character sequence as a + /** Return all non-overlapping matches of this regexp in given character sequence as a * [[scala.collection.Iterator]] of [[scala.util.matching.Regex.Match]]. * * @param source The text to match against. 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/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala index 5b6ff2325c..3230fdbc67 100644 --- a/src/reflect/scala/reflect/api/Exprs.scala +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -9,6 +9,7 @@ package api import scala.reflect.runtime.{universe => ru} import scala.annotation.compileTimeOnly +import java.io.ObjectStreamException /** * <span class="badge badge-red" style="float: right;">EXPERIMENTAL</span> @@ -157,23 +158,23 @@ trait Exprs { self: Universe => |if you want to get a value of the underlying expression, add scala-compiler.jar to the classpath, |import `scala.tools.reflect.Eval` and call `<your expr>.eval` instead.""".trim.stripMargin) + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedExpr(treec, implicitly[WeakTypeTag[T]].in(ru.rootMirror)) } } +@SerialVersionUID(1L) private[scala] class SerializedExpr(var treec: TreeCreator, var tag: ru.WeakTypeTag[_]) extends Serializable { - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.writeObject(treec) - out.writeObject(tag) - } - - private def readObject(in: java.io.ObjectInputStream): Unit = { - treec = in.readObject().asInstanceOf[TreeCreator] - tag = in.readObject().asInstanceOf[ru.WeakTypeTag[_]] - } + import scala.reflect.runtime.universe.{Expr, runtimeMirror} + @throws(classOf[ObjectStreamException]) private def readResolve(): AnyRef = { - import ru._ - Expr(rootMirror, treec)(tag) + val loader: ClassLoader = try { + Thread.currentThread().getContextClassLoader() + } catch { + case se: SecurityException => null + } + val m = runtimeMirror(loader) + Expr(m, treec)(tag.in(m)) } } diff --git a/src/reflect/scala/reflect/api/TreeCreator.scala b/src/reflect/scala/reflect/api/TreeCreator.scala index 027c840955..000eaa1aa6 100644 --- a/src/reflect/scala/reflect/api/TreeCreator.scala +++ b/src/reflect/scala/reflect/api/TreeCreator.scala @@ -2,12 +2,12 @@ package scala package reflect package api -/** This is an internal implementation class. +/** A mirror-aware factory for trees. * * This class is used internally by Scala Reflection, and is not recommended for use in client code. * - * @group ReflectionAPI + * @group ReflectionAPI */ -abstract class TreeCreator { +abstract class TreeCreator extends Serializable { def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Tree } diff --git a/src/reflect/scala/reflect/api/TypeCreator.scala b/src/reflect/scala/reflect/api/TypeCreator.scala index 37fff90b43..cbd55b9428 100644 --- a/src/reflect/scala/reflect/api/TypeCreator.scala +++ b/src/reflect/scala/reflect/api/TypeCreator.scala @@ -8,6 +8,6 @@ package api * * @group ReflectionAPI */ -abstract class TypeCreator { +abstract class TypeCreator extends Serializable { def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Type } diff --git a/src/reflect/scala/reflect/api/TypeTags.scala b/src/reflect/scala/reflect/api/TypeTags.scala index 1dfc84be69..7db375ca61 100644 --- a/src/reflect/scala/reflect/api/TypeTags.scala +++ b/src/reflect/scala/reflect/api/TypeTags.scala @@ -9,6 +9,7 @@ package api import java.lang.{ Class => jClass } import scala.language.implicitConversions +import java.io.ObjectStreamException /** * A `TypeTag[T]` encapsulates the runtime type representation of some type `T`. @@ -233,6 +234,7 @@ trait TypeTags { self: Universe => val otherMirror1 = otherMirror.asInstanceOf[scala.reflect.api.Mirror[otherMirror.universe.type]] otherMirror.universe.WeakTypeTag[T](otherMirror1, tpec) } + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedTypeTag(tpec, concrete = false) } @@ -293,10 +295,13 @@ trait TypeTags { self: Universe => val otherMirror1 = otherMirror.asInstanceOf[scala.reflect.api.Mirror[otherMirror.universe.type]] otherMirror.universe.TypeTag[T](otherMirror1, tpec) } + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedTypeTag(tpec, concrete = true) } /* @group TypeTags */ + // This class only exists to silence MIMA complaining about a binary incompatibility. + // Only the top-level class (api.PredefTypeCreator) should be used. private class PredefTypeCreator[T](copyIn: Universe => Universe#TypeTag[T]) extends TypeCreator { def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Type = { copyIn(m.universe).asInstanceOf[U # TypeTag[T]].tpe @@ -304,8 +309,9 @@ trait TypeTags { self: Universe => } /* @group TypeTags */ - private class PredefTypeTag[T](_tpe: Type, copyIn: Universe => Universe#TypeTag[T]) extends TypeTagImpl[T](rootMirror, new PredefTypeCreator(copyIn)) { + private class PredefTypeTag[T](_tpe: Type, copyIn: Universe => Universe#TypeTag[T]) extends TypeTagImpl[T](rootMirror, new api.PredefTypeCreator(copyIn)) { override lazy val tpe: Type = _tpe + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedTypeTag(tpec, concrete = true) } @@ -341,20 +347,27 @@ trait TypeTags { self: Universe => def symbolOf[T: WeakTypeTag]: TypeSymbol } +// This class should be final, but we can't do that in Scala 2.11.x without breaking +// binary incompatibility. +@SerialVersionUID(1L) private[scala] class SerializedTypeTag(var tpec: TypeCreator, var concrete: Boolean) extends Serializable { - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.writeObject(tpec) - out.writeBoolean(concrete) - } - - private def readObject(in: java.io.ObjectInputStream): Unit = { - tpec = in.readObject().asInstanceOf[TypeCreator] - concrete = in.readBoolean() + import scala.reflect.runtime.universe.{TypeTag, WeakTypeTag, runtimeMirror} + @throws(classOf[ObjectStreamException]) + private def readResolve(): AnyRef = { + val loader: ClassLoader = try { + Thread.currentThread().getContextClassLoader() + } catch { + case se: SecurityException => null + } + val m = runtimeMirror(loader) + if (concrete) TypeTag(m, tpec) + else WeakTypeTag(m, tpec) } +} - private def readResolve(): AnyRef = { - import scala.reflect.runtime.universe._ - if (concrete) TypeTag(rootMirror, tpec) - else WeakTypeTag(rootMirror, tpec) +/* @group TypeTags */ +private class PredefTypeCreator[T](copyIn: Universe => Universe#TypeTag[T]) extends TypeCreator { + def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Type = { + copyIn(m.universe).asInstanceOf[U # TypeTag[T]].tpe } } diff --git a/src/reflect/scala/reflect/internal/Internals.scala b/src/reflect/scala/reflect/internal/Internals.scala index e9916cf7d1..26f3bfd9d0 100644 --- a/src/reflect/scala/reflect/internal/Internals.scala +++ b/src/reflect/scala/reflect/internal/Internals.scala @@ -129,7 +129,7 @@ trait Internals extends api.Internals { def typeBounds(lo: Type, hi: Type): TypeBounds = self.TypeBounds(lo, hi) def boundedWildcardType(bounds: TypeBounds): BoundedWildcardType = self.BoundedWildcardType(bounds) - def subpatterns(tree: Tree): Option[List[Tree]] = tree.attachments.get[SubpatternsAttachment].map(_.patterns.map(_.duplicate)) + def subpatterns(tree: Tree): Option[List[Tree]] = tree.attachments.get[SubpatternsAttachment].map(_.patterns.map(duplicateAndKeepPositions)) type Decorators = MacroDecoratorApi lazy val decorators: Decorators = new MacroDecoratorApi { 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/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index 01fba1efc1..c16d8778d9 100644 --- a/src/reflect/scala/reflect/internal/Positions.scala +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -23,13 +23,10 @@ import scala.collection.mutable.ListBuffer * Otherwise, the singleton consisting of the node itself. */ trait Positions extends api.Positions { self: SymbolTable => - type Position = scala.reflect.internal.util.Position val NoPosition = scala.reflect.internal.util.NoPosition implicit val PositionTag = ClassTag[Position](classOf[Position]) - def inform(msg: String): Unit - def useOffsetPositions: Boolean = true /** A position that wraps a set of trees. @@ -100,7 +97,7 @@ trait Positions extends api.Positions { self: SymbolTable => inform("\nWhile validating #" + tree.id) inform(treeStatus(tree)) inform("\nChildren:") - tree.children map (t => " " + treeStatus(t, tree)) foreach inform + tree.children foreach (t => inform(" " + treeStatus(t, tree))) inform("=======") throw new ValidateException(msg) } @@ -109,7 +106,7 @@ trait Positions extends api.Positions { self: SymbolTable => if (!tree.isEmpty && tree.canHaveAttrs) { if (settings.Yposdebug && (settings.verbose || settings.Yrangepos)) - println("[%10s] %s".format("validate", treeStatus(tree, encltree))) + inform("[%10s] %s".format("validate", treeStatus(tree, encltree))) if (!tree.pos.isDefined) positionError("Unpositioned tree #"+tree.id) { @@ -176,7 +173,7 @@ trait Positions extends api.Positions { self: SymbolTable => case r :: rs1 => assert(!t.pos.isTransparent) if (r.isFree && (r.pos includes t.pos)) { -// println("subdividing "+r+"/"+t.pos) +// inform("subdividing "+r+"/"+t.pos) maybeFree(t.pos.end, r.pos.end) ::: List(Range(t.pos, t)) ::: maybeFree(r.pos.start, t.pos.start) ::: rs1 } else { if (!r.isFree && (r.pos overlaps t.pos)) conflicting += r.tree @@ -225,7 +222,7 @@ trait Positions extends api.Positions { self: SymbolTable => } } catch { case ex: Exception => - println("error while set children pos "+pos+" of "+trees) + inform("error while set children pos "+pos+" of "+trees) throw ex } diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index fcc377ba32..2ce861898f 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -596,18 +596,26 @@ trait Printers extends api.Printers { self: SymbolTable => } } - protected def emptyTree(tree: Tree) = tree match { - case EmptyTree | build.SyntacticEmptyTypeTree() => true - case _ => false + object EmptyTypeTree { + def unapply(tt: TypeTree): Boolean = tt match { + case build.SyntacticEmptyTypeTree() if tt.wasEmpty || tt.isEmpty => true + case _ => false + } } + protected def isEmptyTree(tree: Tree) = + tree match { + case EmptyTree | EmptyTypeTree() => true + case _ => false + } + protected def originalTypeTrees(trees: List[Tree]) = - trees.filter(!emptyTree(_)) map { - case tt: TypeTree => tt.original - case tree => tree + trees.filter(!isEmptyTree(_)) map { + case tt: TypeTree if tt.original != null => tt.original + case tree => tree } - val defaultClasses = List(tpnme.AnyRef) + val defaultClasses = List(tpnme.AnyRef, tpnme.Object) val defaultTraitsForCase = List(tpnme.Product, tpnme.Serializable) protected def removeDefaultTypesFromList(trees: List[Tree])(classesToRemove: List[Name] = defaultClasses)(traitsToRemove: List[Name]) = { def removeDefaultTraitsFromList(trees: List[Tree], traitsToRemove: List[Name]): List[Tree] = @@ -623,9 +631,10 @@ trait Printers extends api.Printers { self: SymbolTable => removeDefaultTraitsFromList(removeDefaultClassesFromList(trees, classesToRemove), traitsToRemove) } - protected def removeDefaultClassesFromList(trees: List[Tree], classesToRemove: List[Name] = defaultClasses) = + protected def removeDefaultClassesFromList(trees: List[Tree], classesToRemove: List[Name] = defaultClasses) = originalTypeTrees(trees) filter { case Select(Ident(sc), name) => !(classesToRemove.contains(name) && sc == nme.scala_) + case tt: TypeTree if tt.tpe != null => !(classesToRemove contains(newTypeName(tt.tpe.toString()))) case _ => true } @@ -637,7 +646,7 @@ trait Printers extends api.Printers { self: SymbolTable => } override def printOpt(prefix: String, tree: Tree) = - if (!emptyTree(tree)) super.printOpt(prefix, tree) + if (!isEmptyTree(tree)) super.printOpt(prefix, tree) override def printColumn(ts: List[Tree], start: String, sep: String, end: String) = { super.printColumn(ts.filter(!syntheticToRemove(_)), start, sep, end) @@ -952,7 +961,7 @@ trait Printers extends api.Printers { self: SymbolTable => def printTp = print("(", tp, ")") tp match { - case EmptyTree | build.SyntacticEmptyTypeTree() => printTp + case EmptyTree | EmptyTypeTree() => printTp // case for untypechecked trees case Annotated(annot, arg) if (expr ne null) && (arg ne null) && expr.equalsStructure(arg) => printTp // remove double arg - 5: 5: @unchecked case tt: TypeTree if tt.original.isInstanceOf[Annotated] => printTp @@ -963,7 +972,7 @@ trait Printers extends api.Printers { self: SymbolTable => // print only fun when targs are TypeTrees with empty original case TypeApply(fun, targs) => - if (targs.exists(emptyTree(_))) { + if (targs.exists(isEmptyTree(_))) { print(fun) } else super.printTree(tree) @@ -984,8 +993,8 @@ trait Printers extends api.Printers { self: SymbolTable => case treeInfo.Unapplied(body) => body match { case Select(qual, name) if name == nme.unapply => print(qual) - case TypeApply(Select(qual, name), args) if name == nme.unapply || name == nme.unapplySeq => - print(TypeApply(qual, args)) + case TypeApply(Select(qual, name), _) if name == nme.unapply || name == nme.unapplySeq => + print(qual) case _ => print(body) } case _ => print(fun) @@ -1061,7 +1070,11 @@ trait Printers extends api.Printers { self: SymbolTable => print("(", qualifier, ")#", blankForOperatorName(selector), printedName(selector)) case tt: TypeTree => - if (!emptyTree(tt)) print(tt.original) + if (!isEmptyTree(tt)) { + val original = tt.original + if (original != null) print(original) + else super.printTree(tree) + } case AppliedTypeTree(tp, args) => // it's possible to have (=> String) => String type but Function1[=> String, String] is not correct diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index ad8a2594dd..2caa30d27e 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -97,6 +97,8 @@ trait ReificationSupport { self: SymbolTable => def toStats(tree: Tree): List[Tree] = tree match { case EmptyTree => Nil case SyntacticBlock(stats) => stats + case defn if defn.isDef => defn :: Nil + case imp: Import => imp :: Nil case _ => throw new IllegalArgumentException(s"can't flatten $tree") } diff --git a/src/reflect/scala/reflect/internal/Reporting.scala b/src/reflect/scala/reflect/internal/Reporting.scala new file mode 100644 index 0000000000..423127803e --- /dev/null +++ b/src/reflect/scala/reflect/internal/Reporting.scala @@ -0,0 +1,113 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2014 LAMP/EPFL, Typesafe Inc. + * @author Adriaan Moors + */ + +package scala +package reflect +package internal + +/** Provides delegates to the reporter doing the actual work. + * All forwarding methods should be marked final, + * but some subclasses out of our reach stil override them. + * + * Eventually, this interface should be reduced to one method: `reporter`, + * and clients should indirect themselves (reduce duplication of forwarders). + */ +trait Reporting { self : Positions => + def reporter: Reporter + def currentRun: RunReporting + + trait RunReporting { + val reporting: PerRunReporting = PerRunReporting + } + + type PerRunReporting <: PerRunReportingBase + protected def PerRunReporting: PerRunReporting + abstract class PerRunReportingBase { + def deprecationWarning(pos: Position, msg: String): Unit + + /** Have we already supplemented the error message of a compiler crash? */ + private[this] var supplementedError = false + def supplementErrorMessage(errorMessage: String): String = + if (supplementedError) errorMessage + else { + supplementedError = true + supplementTyperState(errorMessage) + } + + } + + // overridden in Global + def supplementTyperState(errorMessage: String): String = errorMessage + + def supplementErrorMessage(errorMessage: String) = currentRun.reporting.supplementErrorMessage(errorMessage) + + @deprecatedOverriding("This forwards to the corresponding method in reporter -- override reporter instead", "2.11.2") + def inform(msg: String): Unit = inform(NoPosition, msg) + @deprecatedOverriding("This forwards to the corresponding method in reporter -- override reporter instead", "2.11.2") + def warning(msg: String): Unit = warning(NoPosition, msg) + // globalError(msg: String) used to abort -- not sure that was a good idea, so I made it more regular + // (couldn't find any uses that relied on old behavior) + @deprecatedOverriding("This forwards to the corresponding method in reporter -- override reporter instead", "2.11.2") + def globalError(msg: String): Unit = globalError(NoPosition, msg) + + def abort(msg: String): Nothing = { + val augmented = supplementErrorMessage(msg) + // Needs to call error to make sure the compile fails. + globalError(augmented) + throw new FatalError(augmented) + } + + @deprecatedOverriding("This forwards to the corresponding method in reporter -- override reporter instead", "2.11.2") + def inform(pos: Position, msg: String) = reporter.echo(pos, msg) + @deprecatedOverriding("This forwards to the corresponding method in reporter -- override reporter instead", "2.11.2") + def warning(pos: Position, msg: String) = reporter.warning(pos, msg) + @deprecatedOverriding("This forwards to the corresponding method in reporter -- override reporter instead", "2.11.2") + def globalError(pos: Position, msg: String) = reporter.error(pos, msg) +} + +import util.Position + +/** Report information, warnings and errors. + * + * This describes the (future) external interface for issuing information, warnings and errors. + * Currently, scala.tools.nsc.Reporter is used by sbt/ide/partest. + */ +abstract class Reporter { + protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit + + def echo(pos: Position, msg: String): Unit = info0(pos, msg, INFO, force = true) + def warning(pos: Position, msg: String): Unit = info0(pos, msg, WARNING, force = false) + def error(pos: Position, msg: String): Unit = info0(pos, msg, ERROR, force = false) + + type Severity + val INFO: Severity + val WARNING: Severity + val ERROR: Severity + + def count(severity: Severity): Int + def resetCount(severity: Severity): Unit + + def hasErrors: Boolean = count(ERROR) > 0 + def hasWarnings: Boolean = count(WARNING) > 0 + + def reset(): Unit = { + resetCount(INFO) + resetCount(WARNING) + resetCount(ERROR) + } + + def flush(): Unit = { } +} + +// TODO: move into superclass once partest cuts tie on Severity +abstract class ReporterImpl extends Reporter { + class Severity(val id: Int)(name: String) { var count: Int = 0 ; override def toString = name} + object INFO extends Severity(0)("INFO") + object WARNING extends Severity(1)("WARNING") + object ERROR extends Severity(2)("ERROR") + + def count(severity: Severity): Int = severity.count + def resetCount(severity: Severity): Unit = severity.count = 0 +} 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/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index c76dedbff4..ed5c68fe82 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -46,16 +46,12 @@ abstract class SymbolTable extends macros.Universe with pickling.Translations with FreshNames with Internals + with Reporting { val gen = new InternalTreeGen { val global: SymbolTable.this.type = SymbolTable.this } def log(msg: => AnyRef): Unit - def deprecationWarning(pos: Position, msg: String): Unit = warning(msg) - def warning(msg: String): Unit = Console.err.println(msg) - def inform(msg: String): Unit = Console.err.println(msg) - def globalError(msg: String): Unit = abort(msg) - def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg)) protected def elapsedMessage(msg: String, start: Long) = msg + " in " + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) - start) + "ms" @@ -82,9 +78,6 @@ abstract class SymbolTable extends macros.Universe /** Prints a stack trace if -Ydebug or equivalent was given, otherwise does nothing. */ def debugStack(t: Throwable): Unit = devWarning(throwableAsString(t)) - /** Overridden when we know more about what was happening during a failure. */ - def supplementErrorMessage(msg: String): String = msg - private[scala] def printCaller[T](msg: String)(result: T) = { Console.err.println("%s: %s\nCalled from: %s".format(msg, result, (new Throwable).getStackTrace.drop(2).take(50).mkString("\n"))) diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 9066c73393..6e8e992d16 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -451,10 +451,10 @@ abstract class TreeGen { def mkSyntheticUnit() = Literal(Constant(())).updateAttachment(SyntheticUnitAttachment) /** Create block of statements `stats` */ - def mkBlock(stats: List[Tree]): Tree = + def mkBlock(stats: List[Tree], doFlatten: Boolean = true): Tree = if (stats.isEmpty) mkSyntheticUnit() else if (!stats.last.isTerm) Block(stats, mkSyntheticUnit()) - else if (stats.length == 1) stats.head + else if (stats.length == 1 && doFlatten) stats.head else Block(stats.init, stats.last) /** Create a block that wraps multiple statements but don't diff --git a/src/reflect/scala/reflect/internal/Variances.scala b/src/reflect/scala/reflect/internal/Variances.scala index cfe2ad8b87..12b765b7a6 100644 --- a/src/reflect/scala/reflect/internal/Variances.scala +++ b/src/reflect/scala/reflect/internal/Variances.scala @@ -79,7 +79,7 @@ trait Variances { // Unsound pre-2.11 behavior preserved under -Xsource:2.10 if (settings.isScala211 || sym.isOverridingSymbol) Invariant else { - deprecationWarning(sym.pos, s"Construct depends on unsound variance analysis and will not compile in scala 2.11 and beyond") + currentRun.reporting.deprecationWarning(sym.pos, s"Construct depends on unsound variance analysis and will not compile in scala 2.11 and beyond") Bivariant } ) diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 64a1a44722..b808666360 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -290,6 +290,25 @@ abstract class UnPickler { def pflags = flags & PickledFlags def finishSym(sym: Symbol): Symbol = { + /** + * member symbols (symbols owned by a class) are added to the class's scope, with a number + * of exceptions: + * + * (.) ... + * (1) `local child` represents local child classes, see comment in Pickler.putSymbol. + * Since it is not a member, it should not be entered in the owner's scope. + */ + def shouldEnterInOwnerScope = { + sym.owner.isClass && + sym != classRoot && + sym != moduleRoot && + !sym.isModuleClass && + !sym.isRefinementClass && + !sym.isTypeParameter && + !sym.isExistentiallyBound && + sym.rawname != tpnme.LOCAL_CHILD // (1) + } + markFlagsCompleted(sym)(mask = AllFlags) sym.privateWithin = privateWithin sym.info = ( @@ -302,8 +321,7 @@ abstract class UnPickler { newLazyTypeRefAndAlias(inforef, readNat()) } ) - if (sym.owner.isClass && sym != classRoot && sym != moduleRoot && - !sym.isModuleClass && !sym.isRefinementClass && !sym.isTypeParameter && !sym.isExistentiallyBound) + if (shouldEnterInOwnerScope) symScope(sym.owner) enter sym sym @@ -681,10 +699,24 @@ abstract class UnPickler { private val p = phase protected def completeInternal(sym: Symbol) : Unit = try { val tp = at(i, () => readType(sym.isTerm)) // after NMT_TRANSITION, revert `() => readType(sym.isTerm)` to `readType` - if (p ne null) - slowButSafeEnteringPhase(p) (sym setInfo tp) + + // This is a temporary fix allowing to read classes generated by an older, buggy pickler. + // See the generation of the LOCAL_CHILD class in Pickler.scala. In an earlier version, the + // pickler did not add the ObjectTpe superclass, it used a trait as the first parent. This + // tripped an assertion in AddInterfaces which checks that the first parent is not a trait. + // This workaround can probably be removed in 2.12, because the 2.12 compiler is supposed + // to only read classfiles generated by 2.12. + val fixLocalChildTp = if (sym.rawname == tpnme.LOCAL_CHILD) tp match { + case ClassInfoType(superClass :: traits, decls, typeSymbol) if superClass.typeSymbol.isTrait => + ClassInfoType(definitions.ObjectTpe :: superClass :: traits, decls, typeSymbol) + case _ => tp + } else tp + + if (p ne null) { + slowButSafeEnteringPhase(p)(sym setInfo fixLocalChildTp) + } if (currentRunId != definedAtRunId) - sym.setInfo(adaptToNewRunMap(tp)) + sym.setInfo(adaptToNewRunMap(fixLocalChildTp)) } catch { case e: MissingRequirementError => throw toTypeError(e) 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/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala index 5ccdc15a03..b5c340645a 100644 --- a/src/reflect/scala/reflect/macros/Attachments.scala +++ b/src/reflect/scala/reflect/macros/Attachments.scala @@ -35,7 +35,7 @@ abstract class Attachments { self => def all: Set[Any] = Set.empty private def matchesTag[T: ClassTag](datum: Any) = - classTag[T].runtimeClass == datum.getClass + classTag[T].runtimeClass.isInstance(datum) /** An underlying payload of the given class type `T`. */ def get[T: ClassTag]: Option[T] = diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index b5446694ed..fe39e1f245 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -14,15 +14,27 @@ import scala.reflect.api.{TreeCreator, TypeCreator, Universe} * @contentDiagram hideNodes "*Api" "*Extractor" */ class JavaUniverse extends InternalSymbolTable with JavaUniverseForce with ReflectSetup with RuntimeSymbolTable { self => - - override def inform(msg: String): Unit = log(msg) def picklerPhase = SomePhase def erasurePhase = SomePhase lazy val settings = new Settings - private val isLogging = sys.props contains "scala.debug.reflect" + private val isLogging = sys.props contains "scala.debug.reflect" def log(msg: => AnyRef): Unit = if (isLogging) Console.err.println("[reflect] " + msg) + // TODO: why put output under isLogging? Calls to inform are already conditional on debug/verbose/... + import scala.reflect.internal.{Reporter, ReporterImpl} + override def reporter: Reporter = new ReporterImpl { + protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = log(msg) + } + + // minimal Run to get Reporting wired + def currentRun = new RunReporting {} + class PerRunReporting extends PerRunReportingBase { + def deprecationWarning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) + } + protected def PerRunReporting = new PerRunReporting + + type TreeCopier = InternalTreeCopierOps implicit val TreeCopierTag: ClassTag[TreeCopier] = ClassTag[TreeCopier](classOf[TreeCopier]) def newStrictTreeCopier: TreeCopier = new StrictTreeCopier 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/repl/scala/tools/nsc/MainGenericRunner.scala b/src/repl/scala/tools/nsc/MainGenericRunner.scala index 43f0ea1256..34057ed341 100644 --- a/src/repl/scala/tools/nsc/MainGenericRunner.scala +++ b/src/repl/scala/tools/nsc/MainGenericRunner.scala @@ -8,7 +8,6 @@ package tools.nsc import io.{ File } import util.{ ClassPath, ScalaClassLoader } -import Properties.{ versionString, copyrightString } import GenericRunnerCommand._ object JarRunner extends CommonRunner { @@ -28,79 +27,78 @@ object JarRunner extends CommonRunner { } /** An object that runs Scala code. It has three possible - * sources for the code to run: pre-compiled code, a script file, - * or interactive entry. - */ + * sources for the code to run: pre-compiled code, a script file, + * or interactive entry. + */ class MainGenericRunner { - def errorFn(ex: Throwable): Boolean = { - ex.printStackTrace() - false - } - def errorFn(str: String): Boolean = { - Console.err println str - false + def errorFn(str: String, e: Option[Throwable] = None, isFailure: Boolean = true): Boolean = { + if (str.nonEmpty) Console.err println str + e foreach (_.printStackTrace()) + !isFailure } def process(args: Array[String]): Boolean = { val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x)) - import command.{ settings, howToRun, thingToRun } - def sampleCompiler = new Global(settings) // def so its not created unless needed - - if (!command.ok) return errorFn("\n" + command.shortUsageMsg) - else if (settings.version) return errorFn("Scala code runner %s -- %s".format(versionString, copyrightString)) - else if (command.shouldStopWithInfo) return errorFn(command getInfoMessage sampleCompiler) - - def isE = !settings.execute.isDefault - def dashe = settings.execute.value - - def isI = !settings.loadfiles.isDefault - def dashi = settings.loadfiles.value - - // Deadlocks on startup under -i unless we disable async. - if (isI) - settings.Yreplsync.value = true - - def combinedCode = { - val files = if (isI) dashi map (file => File(file).slurp()) else Nil - val str = if (isE) List(dashe) else Nil - - files ++ str mkString "\n\n" - } - - def runTarget(): Either[Throwable, Boolean] = howToRun match { - case AsObject => - ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments) - case AsScript => - ScriptRunner.runScriptAndCatch(settings, thingToRun, command.arguments) - case AsJar => - JarRunner.runJar(settings, thingToRun, command.arguments) - case Error => - Right(false) - case _ => - // We start the repl when no arguments are given. - Right(new interpreter.ILoop process settings) + import command.{ settings, howToRun, thingToRun, shortUsageMsg, shouldStopWithInfo } + def sampleCompiler = new Global(settings) // def so it's not created unless needed + + def run(): Boolean = { + def isE = !settings.execute.isDefault + def dashe = settings.execute.value + + def isI = !settings.loadfiles.isDefault + def dashi = settings.loadfiles.value + + // Deadlocks on startup under -i unless we disable async. + if (isI) + settings.Yreplsync.value = true + + def combinedCode = { + val files = if (isI) dashi map (file => File(file).slurp()) else Nil + val str = if (isE) List(dashe) else Nil + + files ++ str mkString "\n\n" + } + + def runTarget(): Either[Throwable, Boolean] = howToRun match { + case AsObject => + ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments) + case AsScript => + ScriptRunner.runScriptAndCatch(settings, thingToRun, command.arguments) + case AsJar => + JarRunner.runJar(settings, thingToRun, command.arguments) + case Error => + Right(false) + case _ => + // We start the repl when no arguments are given. + Right(new interpreter.ILoop process settings) + } + + /** If -e and -i were both given, we want to execute the -e code after the + * -i files have been included, so they are read into strings and prepended to + * the code given in -e. The -i option is documented to only make sense + * interactively so this is a pretty reasonable assumption. + * + * This all needs a rewrite though. + */ + if (isE) { + ScriptRunner.runCommand(settings, combinedCode, thingToRun +: command.arguments) + } + else runTarget() match { + case Left(ex) => errorFn("", Some(ex)) // there must be a useful message of hope to offer here + case Right(b) => b + } } - /** If -e and -i were both given, we want to execute the -e code after the - * -i files have been included, so they are read into strings and prepended to - * the code given in -e. The -i option is documented to only make sense - * interactively so this is a pretty reasonable assumption. - * - * This all needs a rewrite though. - */ - if (isE) { - ScriptRunner.runCommand(settings, combinedCode, thingToRun +: command.arguments) - } - else runTarget() match { - case Left(ex) => errorFn(ex) - case Right(b) => b - } + if (!command.ok) + errorFn(f"%n$shortUsageMsg") + else if (shouldStopWithInfo) + errorFn(command getInfoMessage sampleCompiler, isFailure = false) + else + run() } } object MainGenericRunner extends MainGenericRunner { - def main(args: Array[String]) { - if (!process(args)) - sys.exit(1) - } + def main(args: Array[String]): Unit = if (!process(args)) sys.exit(1) } diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 47d97dd4dd..8ea8759ee5 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -110,7 +110,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set lazy val reporter: ReplReporter = new ReplReporter(this) import formatting._ - import reporter.{ printMessage, withoutTruncating } + import reporter.{ printMessage, printUntruncatedMessage } // This exists mostly because using the reporter too early leads to deadlock. private def echo(msg: String) { Console println msg } @@ -609,7 +609,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set } else { // don't truncate stack traces - withoutTruncating(printMessage(result)) + printUntruncatedMessage(result) IR.Error } } @@ -793,7 +793,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set } ((pos, msg)) :: loop(filtered) } - val warnings = loop(run.allConditionalWarnings flatMap (_.warnings)) + val warnings = loop(run.reporting.allConditionalWarnings) if (warnings.nonEmpty) mostRecentWarnings = warnings } @@ -1121,7 +1121,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def apply(line: String): Result = debugging(s"""parse("$line")""") { var isIncomplete = false - reporter.withIncompleteHandler((_, _) => isIncomplete = true) { + currentRun.reporting.withIncompleteHandler((_, _) => isIncomplete = true) { reporter.reset() val trees = newUnitParser(line).parseStats() if (reporter.hasErrors) Error diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala index 915fd57bf8..3cb6ba11c1 100644 --- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala @@ -41,16 +41,16 @@ class JavapClass( * Byte data for filename args is retrieved with findBytes. */ def apply(args: Seq[String]): List[JpResult] = { - val (options, claases) = args partition (s => (s startsWith "-") && s.length > 1) + val (options, classes) = args partition (s => (s startsWith "-") && s.length > 1) val (flags, upgraded) = upgrade(options) import flags.{ app, fun, help, raw } - val targets = if (fun && !help) FunFinder(loader, intp).funs(claases) else claases - if (help || claases.isEmpty) + val targets = if (fun && !help) FunFinder(loader, intp).funs(classes) else classes + if (help || classes.isEmpty) List(JpResult(JavapTool.helper(printWriter))) else if (targets.isEmpty) List(JpResult("No anonfuns found.")) else - tool(raw, upgraded)(targets map (claas => targeted(claas, app))) + tool(raw, upgraded)(targets map (klass => targeted(klass, app))) } /** Cull our tool options. */ @@ -67,17 +67,18 @@ class JavapClass( case f: Failure[_] => (path, Failure(f.exception)) } - /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). */ + /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). + * @return the path to use for filtering, and the byte array + */ private def bytesFor(path: String, app: Boolean) = Try { def last = intp.get.mostRecentVar // fail if no intp - def req = path match { - case "-" => last - case HashSplit(prefix, member) => - if (prefix != null) prefix - else if (member != null) member - else "#" + val req = path match { + case "-" => last + case HashSplit(prefix, _) if prefix != null => prefix + case HashSplit(_, member) if member != null => member + case s => s } - val targetedBytes = if (app) findAppBody(req) else (req, findBytes(req)) + val targetedBytes = if (app) findAppBody(req) else (path, findBytes(req)) if (targetedBytes._2.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '$path'") targetedBytes } @@ -217,27 +218,36 @@ class JavapClass( // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } var filtering = false // true if in region matching filter - // true to output - def checkFilter(line: String) = if (filterOn.isEmpty) true else { + // turn filtering on/off given the pattern of interest + def filterStatus(line: String, pattern: String) = { // cheap heuristic, todo maybe parse for the java sig. // method sigs end in paren semi def isAnyMethod = line.endsWith(");") def isOurMethod = { val lparen = line.lastIndexOf('(') val blank = line.lastIndexOf(' ', lparen) - (blank >= 0 && line.substring(blank+1, lparen) == filterOn.get) - } - filtering = if (filtering) { - // next blank line terminates section - // for -public, next line is next method, more or less - line.trim.nonEmpty && !isAnyMethod - } else { - isAnyMethod && isOurMethod + if (blank < 0) false + else { + val method = line.substring(blank+1, lparen) + (method == pattern || ((method startsWith pattern+"$") && (method endsWith "$sp"))) + } } + filtering = + if (filtering) { + // next blank line terminates section + // for -public, next line is next method, more or less + line.trim.nonEmpty && !isAnyMethod + } else { + isAnyMethod && isOurMethod + } filtering } - for (line <- Source.fromString(preamble + written).getLines(); if checkFilter(line)) - printWriter write line+lineSeparator + // do we output this line? + def checkFilter(line: String) = filterOn map (filterStatus(line, _)) getOrElse true + for { + line <- Source.fromString(preamble + written).getLines() + if checkFilter(line) + } printWriter write f"$line%n" printWriter.flush() } } @@ -275,7 +285,7 @@ class JavapClass( override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { - case (claas, Success(ba)) => JpResult(showable(raw, claas, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) + case (klass, Success(ba)) => JpResult(showable(raw, klass, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) case (_, Failure(e)) => JpResult(e.toString) }).toList orFailed List(noToolError) } @@ -290,10 +300,10 @@ class JavapClass( //object TaskResult extends Enumeration { // val Ok, Error, CmdErr, SysErr, Abnormal = Value //} - val TaskClaas = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull - override protected def failed = TaskClaas eq null + val TaskClass = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull + override protected def failed = TaskClass eq null - val TaskCtor = TaskClaas.getConstructor( + val TaskCtor = TaskClass.getConstructor( classOf[Writer], classOf[JavaFileManager], classOf[DiagnosticListener[_]], @@ -344,8 +354,12 @@ class JavapClass( import Kind._ import StandardLocation._ import JavaFileManager.Location - import java.net.URI - def uri(name: String): URI = new URI(name) // new URI("jfo:" + name) + import java.net.{ URI, URISyntaxException } + + // name#fragment is OK, but otherwise fragile + def uri(name: String): URI = + try new URI(name) // new URI("jfo:" + name) + catch { case _: URISyntaxException => new URI("dummy") } def inputNamed(name: String): Try[ByteAry] = (managed find (_._1 == name)).get._2 def managedFile(name: String, kind: Kind) = kind match { @@ -379,19 +393,19 @@ class JavapClass( def showable(raw: Boolean, target: String): Showable = showWithPreamble(raw, target, reporter.reportable(raw)) // eventually, use the tool interface - def task(options: Seq[String], claases: Seq[String], inputs: Seq[Input]): Task = { + def task(options: Seq[String], classes: Seq[String], inputs: Seq[Input]): Task = { //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). - //getTask(writer, fileManager, reporter, options.asJava, claases.asJava) + //getTask(writer, fileManager, reporter, options.asJava, classes.asJava) import JavaConverters.asJavaIterableConverter - TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, claases.asJava) + TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, classes.asJava) .orFailed (throw new IllegalStateException) } // a result per input - private def applyOne(raw: Boolean, options: Seq[String], claas: String, inputs: Seq[Input]): Try[JpResult] = + private def applyOne(raw: Boolean, options: Seq[String], klass: String, inputs: Seq[Input]): Try[JpResult] = Try { - task(options, Seq(claas), inputs).call() + task(options, Seq(klass), inputs).call() } map { - case true => JpResult(showable(raw, claas)) + case true => JpResult(showable(raw, klass)) case _ => JpResult(reporter.reportable(raw)) } recoverWith { case e: java.lang.reflect.InvocationTargetException => e.getCause match { @@ -402,7 +416,7 @@ class JavapClass( reporter.clear() } override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { - case (claas, Success(_)) => applyOne(raw, options, claas, inputs).get + case (klass, Success(_)) => applyOne(raw, options, klass, inputs).get case (_, Failure(e)) => JpResult(e.toString) }).toList orFailed List(noToolError) } @@ -534,6 +548,7 @@ class JavapClass( private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) + /** Select the tool implementation for this platform. */ def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 } } @@ -545,7 +560,8 @@ object JavapClass { intp: Option[IMain] = None ) = new JavapClass(loader, printWriter, intp) - val HashSplit = "(.*?)(?:#([^#]*))?".r + val HashSplit = "([^#]+)?(?:#(.+)?)?".r + // We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget // or a resource path com/acme/Widget.class; but not widget.out implicit class MaybeClassLike(val s: String) extends AnyVal { @@ -580,11 +596,11 @@ object JavapClass { /* only the file location from which the given class is loaded */ def locate(k: String): Option[Path] = { Try { - val claas = try cl loadClass k catch { + val klass = try cl loadClass k catch { case _: NoClassDefFoundError => null // let it snow } // cf ScalaClassLoader.originOfClass - claas.getProtectionDomain.getCodeSource.getLocation + klass.getProtectionDomain.getCodeSource.getLocation } match { case Success(null) => None case Success(loc) if loc.isFile => Some(Path(new JFile(loc.toURI))) diff --git a/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala index b20166d070..88372334d6 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala @@ -9,11 +9,29 @@ package interpreter import reporters._ import IMain._ +import scala.reflect.internal.util.Position + /** Like ReplGlobal, a layer for ensuring extra functionality. */ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.in, new ReplStrippingWriter(intp)) { def printUntruncatedMessage(msg: String) = withoutTruncating(printMessage(msg)) + /** Whether very long lines can be truncated. This exists so important + * debugging information (like printing the classpath) is not rendered + * invisible due to the max message length. + */ + private var _truncationOK: Boolean = !intp.settings.verbose + def truncationOK = _truncationOK + def withoutTruncating[T](body: => T): T = { + val saved = _truncationOK + _truncationOK = false + try body + finally _truncationOK = saved + } + + override def warning(pos: Position, msg: String): Unit = withoutTruncating(super.warning(pos, msg)) + override def error(pos: Position, msg: String): Unit = withoutTruncating(super.error(pos, msg)) + override def printMessage(msg: String) { // Avoiding deadlock if the compiler starts logging before // the lazy val is complete. @@ -31,4 +49,5 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i if (intp.totalSilence) () else super.displayPrompt() } + } diff --git a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala index dce52af56a..47ddfb8aa9 100644 --- a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala @@ -95,11 +95,11 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor val documentError: PartialFunction[Throwable, Unit] = { case NoCompilerRunException => reporter.info(null, "No documentation generated with unsuccessful compiler run", force = false) - case _: ClassNotFoundException => - () + case e @ (_:ClassNotFoundException | _:IllegalAccessException | _:InstantiationException | _:SecurityException | _:ClassCastException) => + reporter.error(null, s"Cannot load the doclet class ${settings.docgenerator.value} (specified with ${settings.docgenerator.name}): $e. Leaving the default settings will generate the html version of scaladoc.") } - /** Generate document(s) for all `files` containing scaladoc documenataion. + /** Generate document(s) for all `files` containing scaladoc documentation. * @param files The list of paths (relative to the compiler's source path, or absolute) of files to document. */ def document(files: List[String]) { def generate() = { diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala index e5c64c6f45..10c382e169 100644 --- a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala +++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala @@ -39,12 +39,12 @@ trait ScaladocAnalyzer extends Analyzer { for (useCase <- comment.useCases) { typer1.silent(_.asInstanceOf[ScaladocTyper].defineUseCases(useCase)) match { case SilentTypeError(err) => - unit.warning(useCase.pos, err.errMsg) + reporter.warning(useCase.pos, err.errMsg) case _ => } for (useCaseSym <- useCase.defined) { if (sym.name != useCaseSym.name) - unit.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode) + reporter.warning(useCase.pos, "@usecase " + useCaseSym.name.decode + " does not match commented symbol: " + sym.name.decode) } } } @@ -191,7 +191,7 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax } def isDirty = unclean(unmooredParser parseComment doc) if ((doc ne null) && (settings.lint || isDirty)) - unit.warning(doc.pos, "discarding unmoored doc comment") + reporter.warning(doc.pos, "discarding unmoored doc comment") } override def flushDoc(): DocComment = (try lastDoc finally lastDoc = null) diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala index a933c35c99..19cc27b40b 100755 --- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala @@ -666,7 +666,7 @@ trait CommentFactoryBase { this: MemberLookupBase => } def summary(): Inline = { - val i = inline(check(".")) + val i = inline(checkSentenceEnded()) Summary( if (jump(".")) Chain(List(i, Text("."))) @@ -785,6 +785,16 @@ trait CommentFactoryBase { this: MemberLookupBase => }) } + def checkSentenceEnded(): Boolean = { + (char == '.') && { + val poff = offset + nextChar() // read '.' + val ok = char == endOfText || char == endOfLine || isWhitespace(char) + offset = poff + ok + } + } + def reportError(pos: Position, message: String) { reporter.warning(pos, message) } 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/deprecation.check b/test/files/jvm/deprecation.check index d116778d3f..d57b6b55a5 100644 --- a/test/files/jvm/deprecation.check +++ b/test/files/jvm/deprecation.check @@ -1,3 +1,3 @@ -warning: there were 4 deprecation warning(s); re-run with -deprecation for details +warning: there were four deprecation warnings; re-run with -deprecation for details Note: deprecation/Use_2.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details. diff --git a/test/files/jvm/duration-tck.scala b/test/files/jvm/duration-tck.scala index 3bc8a2c100..7db6c49964 100644 --- a/test/files/jvm/duration-tck.scala +++ b/test/files/jvm/duration-tck.scala @@ -61,6 +61,11 @@ object Test extends App { minf - inf mustBe minf minf + minf mustBe minf + for (i <- Seq(zero, one, two, three)) { + i - inf mustBe minf + i - minf mustBe inf + } + inf.compareTo(inf) mustBe 0 inf.compareTo(one) mustBe 1 inf.compareTo(minf) mustBe 1 diff --git a/test/files/jvm/future-spec.check b/test/files/jvm/future-spec.check index 844ca54682..df1629dd7e 100644 --- a/test/files/jvm/future-spec.check +++ b/test/files/jvm/future-spec.check @@ -1 +1 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check index b55ecc10e6..d124794e72 100644 --- a/test/files/jvm/interpreter.check +++ b/test/files/jvm/interpreter.check @@ -95,7 +95,7 @@ scala> case class Bar(n: Int) defined class Bar scala> implicit def foo2bar(foo: Foo) = Bar(foo.n) -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details foo2bar: (foo: Foo)Bar scala> val bar: Bar = Foo(3) @@ -269,7 +269,7 @@ scala> xs map (x => x) res6: Array[_] = Array(1, 2) scala> xs map (x => (x, x)) -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details res7: Array[(_$1, _$1)] forSome { type _$1 } = Array((1,1), (2,2)) scala> diff --git a/test/files/jvm/serialization-new.check b/test/files/jvm/serialization-new.check index 47d7bfd920..1555135926 100644 --- a/test/files/jvm/serialization-new.check +++ b/test/files/jvm/serialization-new.check @@ -1,4 +1,4 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details a1 = Array[1,2,3] _a1 = Array[1,2,3] arrayEquals(a1, _a1): true diff --git a/test/files/jvm/serialization.check b/test/files/jvm/serialization.check index 47d7bfd920..1555135926 100644 --- a/test/files/jvm/serialization.check +++ b/test/files/jvm/serialization.check @@ -1,4 +1,4 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details a1 = Array[1,2,3] _a1 = Array[1,2,3] arrayEquals(a1, _a1): true diff --git a/test/files/neg/aladdin1055.check b/test/files/neg/aladdin1055.check new file mode 100644 index 0000000000..41782ae987 --- /dev/null +++ b/test/files/neg/aladdin1055.check @@ -0,0 +1,7 @@ +Test_1.scala:2: warning: match may not be exhaustive. +It would fail on the following input: (_ : this.<local child>) + def foo(t: A.T) = t match { + ^ +error: No warnings can be incurred under -Xfatal-warnings. +one warning found +one error found diff --git a/test/files/neg/aladdin1055.flags b/test/files/neg/aladdin1055.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/aladdin1055.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/aladdin1055/A.scala b/test/files/neg/aladdin1055/A.scala new file mode 100644 index 0000000000..862336e30c --- /dev/null +++ b/test/files/neg/aladdin1055/A.scala @@ -0,0 +1,6 @@ +object A { + sealed trait T { def f: Int } + class TT extends T { def f = 0 } + + def foo = new T { def f = 1 } // local subclass of sealed trait T +} diff --git a/test/files/neg/aladdin1055/Test_1.scala b/test/files/neg/aladdin1055/Test_1.scala new file mode 100644 index 0000000000..39d9b1dc98 --- /dev/null +++ b/test/files/neg/aladdin1055/Test_1.scala @@ -0,0 +1,5 @@ +object Test { + def foo(t: A.T) = t match { + case a: A.TT => 0 + } +} diff --git a/test/files/neg/checksensible.check b/test/files/neg/checksensible.check index e5f1a38d96..7de22fef54 100644 --- a/test/files/neg/checksensible.check +++ b/test/files/neg/checksensible.check @@ -97,6 +97,7 @@ checksensible.scala:84: warning: comparing values of types EqEqRefTest.this.C3 a checksensible.scala:95: warning: comparing values of types Unit and Int using `!=' will always yield true while ((c = in.read) != -1) ^ +warning: there were three deprecation warnings; re-run with -deprecation for details error: No warnings can be incurred under -Xfatal-warnings. -33 warnings found +34 warnings found one error found 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/overloaded-implicit.check b/test/files/neg/overloaded-implicit.check index ca0870705d..0e6617d904 100644 --- a/test/files/neg/overloaded-implicit.check +++ b/test/files/neg/overloaded-implicit.check @@ -4,6 +4,7 @@ overloaded-implicit.scala:2: warning: parameterized overloaded implicit methods overloaded-implicit.scala:3: warning: parameterized overloaded implicit methods are not visible as view bounds implicit def imp1[T](x: Set[T]): Map[T, T] = Map() ^ +warning: there were four feature warnings; re-run with -feature for details error: No warnings can be incurred under -Xfatal-warnings. -two warnings found +three warnings found one error found diff --git a/test/files/neg/t1909-object.check b/test/files/neg/t1909-object.check index 401c1f7ebf..7141c84d4b 100644 --- a/test/files/neg/t1909-object.check +++ b/test/files/neg/t1909-object.check @@ -1,4 +1,6 @@ -t1909-object.scala:4: error: !!! SI-1909 Unable to STATICally lift object InnerTrouble$1, which is defined in the self- or super-constructor call of class Kaboom. A VerifyError is likely. +t1909-object.scala:4: warning: !!! SI-1909 Unable to STATICally lift object InnerTrouble$1, which is defined in the self- or super-constructor call of class Kaboom. A VerifyError is likely. object InnerTrouble ^ +error: No warnings can be incurred under -Xfatal-warnings. +one warning found one error found diff --git a/test/files/neg/t5675.check b/test/files/neg/t5675.check index da608a2b78..3b3b2fa04c 100644 --- a/test/files/neg/t5675.check +++ b/test/files/neg/t5675.check @@ -1,2 +1,4 @@ -error: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details +error: No warnings can be incurred under -Xfatal-warnings. +one warning found one error found 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/t6289.check b/test/files/neg/t6289.check index f6f43cabd3..989932750f 100644 --- a/test/files/neg/t6289.check +++ b/test/files/neg/t6289.check @@ -3,7 +3,7 @@ t6289/J.java:2: method does not override or implement a method from a supertype @Override public void foo() { } ^ 1 error -#partest java7 +#partest !java6 t6289/J.java:2: error: method does not override or implement a method from a supertype @Override public void foo() { } ^ diff --git a/test/files/neg/t6567.check b/test/files/neg/t6567.check index a733d75354..f42f157371 100644 --- a/test/files/neg/t6567.check +++ b/test/files/neg/t6567.check @@ -4,6 +4,7 @@ t6567.scala:8: warning: Suspicious application of an implicit view (Test.this.a2 t6567.scala:10: warning: Suspicious application of an implicit view (Test.this.a2b) in the argument to Option.apply. val b: Option[B] = Option(a) ^ +warning: there was one feature warning; re-run with -feature for details error: No warnings can be incurred under -Xfatal-warnings. -two warnings found +three warnings found one error found diff --git a/test/files/neg/t8035-no-adapted-args.check b/test/files/neg/t8035-no-adapted-args.check new file mode 100644 index 0000000000..43637b2c1f --- /dev/null +++ b/test/files/neg/t8035-no-adapted-args.check @@ -0,0 +1,21 @@ +t8035-no-adapted-args.scala:4: warning: No automatic adaptation here: use explicit parentheses. + signature: Test.f[T](x: T): Int + given arguments: 1, 2, 3 + after adaptation: Test.f((1, 2, 3): (Int, Int, Int)) + f(1, 2, 3) + ^ +t8035-no-adapted-args.scala:4: error: too many arguments for method f: (x: (Int, Int, Int))Int + f(1, 2, 3) + ^ +t8035-no-adapted-args.scala:5: warning: No automatic adaptation here: use explicit parentheses. + signature: Test.f[T](x: T): Int + given arguments: <none> + after adaptation: Test.f((): Unit) + f() + ^ +t8035-no-adapted-args.scala:5: error: not enough arguments for method f: (x: Unit)Int. +Unspecified value parameter x. + f() + ^ +two warnings found +two errors found diff --git a/test/files/neg/t8035-no-adapted-args.flags b/test/files/neg/t8035-no-adapted-args.flags new file mode 100644 index 0000000000..b3e8c505e2 --- /dev/null +++ b/test/files/neg/t8035-no-adapted-args.flags @@ -0,0 +1 @@ +-Yno-adapted-args
\ No newline at end of file diff --git a/test/files/neg/t8035-no-adapted-args.scala b/test/files/neg/t8035-no-adapted-args.scala new file mode 100644 index 0000000000..82690ebe94 --- /dev/null +++ b/test/files/neg/t8035-no-adapted-args.scala @@ -0,0 +1,6 @@ +object Test { + def f[T](x: T) = 0 + + f(1, 2, 3) + f() +} 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/neg/t8675.check b/test/files/neg/t8675.check new file mode 100644 index 0000000000..4e44fba918 --- /dev/null +++ b/test/files/neg/t8675.check @@ -0,0 +1,11 @@ +t8675.scala:13: error: type mismatch; + found : Boolean(true) + required: String + a.update(0, x[A]({new isString(true)})) // !!! allowed + ^ +t8675.scala:22: error: type mismatch; + found : Boolean(true) + required: String + new X().m(x[A]({new isString(true)})) // !!! allowed + ^ +two errors found diff --git a/test/files/neg/t8675.scala b/test/files/neg/t8675.scala new file mode 100644 index 0000000000..ca9bb57ffa --- /dev/null +++ b/test/files/neg/t8675.scala @@ -0,0 +1,24 @@ +class A(s: String) { + def foo(x: A) = x +} + +class isString(s: String) + +class Test { + + def x[A](a: Any): A = ??? + + def test { + val a = Array[A]() + a.update(0, x[A]({new isString(true)})) // !!! allowed + + // boils down to + class X { + def m(p: Any) {} + } + implicit class XOps(x: X) { + def m(p: Any) {} + } + new X().m(x[A]({new isString(true)})) // !!! allowed + } +} diff --git a/test/files/neg/t8675b.check b/test/files/neg/t8675b.check new file mode 100644 index 0000000000..cb7ac8af59 --- /dev/null +++ b/test/files/neg/t8675b.check @@ -0,0 +1,6 @@ +t8675b.scala:19: error: missing parameter type for expanded function +The argument types of an anonymous function must be fully known. (SLS 8.5) +Expected type was: List[Test.Reportable1[?,?]] => Boolean + for (path: List[Any] <- (null : Engine1).asRequirement.pathsIncludingSelf.toList) { + ^ +one error found diff --git a/test/files/neg/t8675b.scala b/test/files/neg/t8675b.scala new file mode 100644 index 0000000000..2c5015b1d0 --- /dev/null +++ b/test/files/neg/t8675b.scala @@ -0,0 +1,22 @@ +object Test { + trait Engine1 + + implicit class EngineTools1[Params, R](e: Engine1) { + def asRequirement: Requirement1[Params, R] = ??? + } + trait Requirement1[Params, R] { + def pathsIncludingSelf: Traversable[List[Reportable1[Params, R]]] + } + trait Reportable1[Params, R] + + // "missing paramater type" error was swallowed in 2.11.0 leading to a crash + // in the backend. + // + // This error is itself a regression (or at least a change) in 2.11.0-M7, + // specifically in SI-7944. The type paramaters to the implicit view + // `EngineTools1` are undetermined, and are now treated as type variables + // in the expected type of the closure argument to `withFilter`. + for (path: List[Any] <- (null : Engine1).asRequirement.pathsIncludingSelf.toList) { + ??? + } +} diff --git a/test/files/neg/tailrec-4.check b/test/files/neg/tailrec-4.check new file mode 100644 index 0000000000..3ec3274478 --- /dev/null +++ b/test/files/neg/tailrec-4.check @@ -0,0 +1,16 @@ +tailrec-4.scala:6: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position + @tailrec def foo: Int = foo + 1 + ^ +tailrec-4.scala:11: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position + @tailrec def foo: Int = foo + 1 + ^ +tailrec-4.scala:17: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position + @tailrec def foo: Int = foo + 1 + ^ +tailrec-4.scala:23: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position + @tailrec def foo: Int = foo + 1 + ^ +tailrec-4.scala:31: error: could not optimize @tailrec annotated method foo: it contains a recursive call not in tail position + @tailrec def foo: Int = foo + 1 + ^ +5 errors found diff --git a/test/files/neg/tailrec-4.scala b/test/files/neg/tailrec-4.scala new file mode 100644 index 0000000000..4822799dfa --- /dev/null +++ b/test/files/neg/tailrec-4.scala @@ -0,0 +1,35 @@ +import annotation._ + +object Tail { + def tcInFunc: Unit = { + () => { + @tailrec def foo: Int = foo + 1 + } + } + def tcInBooleanExprFirstOp(x: Int, v: Int): Boolean = { + { + @tailrec def foo: Int = foo + 1 + foo + } == v && true + } + def tcInBooleanExprSecondOp(x: Int, v: Int): Boolean = { + true && { + @tailrec def foo: Int = foo + 1 + foo + } == v + } + def tcInIfCond(x: Int, v: Int): Boolean = { + if ({ + @tailrec def foo: Int = foo + 1 + foo + } == v) true else false + } + def tcInPatternGuard(x: Int, v: Int): Boolean = + v match { + case _ if + { + @tailrec def foo: Int = foo + 1 + foo == 42 + } => true + } +} diff --git a/test/files/neg/unchecked-refinement.check b/test/files/neg/unchecked-refinement.check index e85a51f44d..0bb944621b 100644 --- a/test/files/neg/unchecked-refinement.check +++ b/test/files/neg/unchecked-refinement.check @@ -10,6 +10,7 @@ unchecked-refinement.scala:23: warning: a pattern match on a refinement type is unchecked-refinement.scala:24: warning: a pattern match on a refinement type is unchecked /* nowarn - todo */ case x: AnyRef { def size: Int } if b => x.size // this could/should do a static conformance test and not warn ^ +warning: there was one feature warning; re-run with -feature for details error: No warnings can be incurred under -Xfatal-warnings. -four warnings found +5 warnings found one error found diff --git a/test/files/neg/virtpatmat_exhaust_compound.check b/test/files/neg/virtpatmat_exhaust_compound.check new file mode 100644 index 0000000000..72e0340682 --- /dev/null +++ b/test/files/neg/virtpatmat_exhaust_compound.check @@ -0,0 +1,15 @@ +virtpatmat_exhaust_compound.scala:14: warning: match may not be exhaustive. +It would fail on the following inputs: O1, O2, O4 + a match { + ^ +virtpatmat_exhaust_compound.scala:18: warning: match may not be exhaustive. +It would fail on the following input: O4 + def t1(a: Product with Base with Base2) = a match { + ^ +virtpatmat_exhaust_compound.scala:22: warning: match may not be exhaustive. +It would fail on the following input: O2 + def t2(a: Product with Base { def foo: Int }) = a match { + ^ +error: No warnings can be incurred under -Xfatal-warnings. +three warnings found +one error found diff --git a/test/files/neg/virtpatmat_exhaust_compound.flags b/test/files/neg/virtpatmat_exhaust_compound.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/virtpatmat_exhaust_compound.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/virtpatmat_exhaust_compound.scala b/test/files/neg/virtpatmat_exhaust_compound.scala new file mode 100644 index 0000000000..386c7af98d --- /dev/null +++ b/test/files/neg/virtpatmat_exhaust_compound.scala @@ -0,0 +1,29 @@ +sealed trait Base +case object O1 extends Base +case object O2 extends Base { + def foo: Int = 0 +} + +sealed trait Base2 +case object O3 extends Base2 + +case object O4 extends Base with Base2 + +object Test { + val a /*: Product with Serialiable with Base */ = if (true) O1 else O2 + a match { + case null => + } + + def t1(a: Product with Base with Base2) = a match { + case null => // O1..O3 should *not* be possible here + } + + def t2(a: Product with Base { def foo: Int }) = a match { + case null => // O2 in the domain + } + + def t3(a: Product with Base { def bar: Int }) = a match { + case null => // nothing in the domain + } +} diff --git a/test/files/pos/macro-attachments/Macros_1.scala b/test/files/pos/macro-attachments/Macros_1.scala new file mode 100644 index 0000000000..38d05d5b85 --- /dev/null +++ b/test/files/pos/macro-attachments/Macros_1.scala @@ -0,0 +1,19 @@ +import scala.language.experimental.macros +import scala.reflect.macros.whitebox.Context + +trait Base +class Att extends Base + +object Macros { + def impl(c: Context) = { + import c.universe._ + import c.internal._ + import decorators._ + val dummy = q"x" + dummy.updateAttachment(new Att) + if (dummy.attachments.get[Base].isEmpty) c.abort(c.enclosingPosition, "that's not good") + q"()" + } + + def foo: Any = macro impl +}
\ No newline at end of file diff --git a/test/files/pos/macro-attachments/Test_2.scala b/test/files/pos/macro-attachments/Test_2.scala new file mode 100644 index 0000000000..acfddae942 --- /dev/null +++ b/test/files/pos/macro-attachments/Test_2.scala @@ -0,0 +1,3 @@ +object Test extends App { + Macros.foo +}
\ No newline at end of file diff --git a/test/files/pos/t8596.flags b/test/files/pos/t8596.flags new file mode 100644 index 0000000000..281f0a10cd --- /dev/null +++ b/test/files/pos/t8596.flags @@ -0,0 +1 @@ +-Yrangepos diff --git a/test/files/pos/t8596.scala b/test/files/pos/t8596.scala new file mode 100644 index 0000000000..bfed58eadf --- /dev/null +++ b/test/files/pos/t8596.scala @@ -0,0 +1,7 @@ +class TypeTreeObjects { + class Container { + def typeParamAndDefaultArg[C](name: String = ""): String = "" + } + // crashed under -Yrangepos + new Container().typeParamAndDefaultArg[Any]() +} 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/pos/t8708/Either_1.scala b/test/files/pos/t8708/Either_1.scala new file mode 100644 index 0000000000..000ed6e7c2 --- /dev/null +++ b/test/files/pos/t8708/Either_1.scala @@ -0,0 +1,6 @@ +sealed trait \/[+A, +B] + +sealed trait EitherT[F[+_], +A, +B] +object EitherT { + def apply[F[+_], A, B](a: F[A \/ B]): EitherT[F, A, B] = new EitherT[F, A, B] { val run = a } +} diff --git a/test/files/pos/t8708/Test_2.scala b/test/files/pos/t8708/Test_2.scala new file mode 100644 index 0000000000..d0e56b9a37 --- /dev/null +++ b/test/files/pos/t8708/Test_2.scala @@ -0,0 +1,13 @@ +import scala.language.higherKinds + +trait ClientTypes[M[+_]] { + final type Context[+A] = EitherT[M, String, A] + object Context { + def apply[A](ca: M[String \/ A]): Context[A] = EitherT[M, String, A](ca) + } + + final type StatefulContext[+A] = EitherT[Context, String, A] + object StatefulContext { + def apply[A](state: Context[String \/ A]): StatefulContext[A] = ??? + } +} diff --git a/test/files/presentation/ide-bug-1000531.check b/test/files/presentation/ide-bug-1000531.check index d8c7a369f7..12eafcd6de 100644 --- a/test/files/presentation/ide-bug-1000531.check +++ b/test/files/presentation/ide-bug-1000531.check @@ -1,111 +1,24 @@ -reload: CrashOnLoad.scala +reload: CrashOnLoad.scala, TestIterable.java -askTypeCompletion at CrashOnLoad.scala(6,12) +askTypeCompletion at CrashOnLoad.scala(6,11) ================================================================================ -[response] askTypeCompletion at (6,12) -retrieved 117 members +[response] askTypeCompletion at (6,11) +retrieved 30 members [inaccessible] protected[package lang] def clone(): Object [inaccessible] protected[package lang] def finalize(): Unit -[inaccessible] protected[this] def reversed: List[B] -class GroupedIterator[B >: A] extends AbstractIterator[Seq[B]] with Iterator[Seq[B]] def +(other: String): String -def ++[B >: B](that: => scala.collection.GenTraversableOnce[B]): Iterator[B] -def ->[B](y: B): (java.util.Iterator[B], B) -def /:[B](z: B)(op: (B, B) => B): B -def :\[B](z: B)(op: (B, B) => B): B -def addString(b: StringBuilder): StringBuilder -def addString(b: StringBuilder,sep: String): StringBuilder -def addString(b: StringBuilder,start: String,sep: String,end: String): StringBuilder -def aggregate[B](z: => B)(seqop: (B, B) => B,combop: (B, B) => B): B -def buffered: scala.collection.BufferedIterator[B] -def collectFirst[B](pf: PartialFunction[B,B]): Option[B] -def collect[B](pf: PartialFunction[B,B]): Iterator[B] -def contains(elem: Any): Boolean -def copyToArray[B >: B](xs: Array[B]): Unit -def copyToArray[B >: B](xs: Array[B],start: Int): Unit -def copyToArray[B >: B](xs: Array[B],start: Int,len: Int): Unit -def copyToBuffer[B >: B](dest: scala.collection.mutable.Buffer[B]): Unit -def corresponds[B](that: scala.collection.GenTraversableOnce[B])(p: (B, B) => Boolean): Boolean -def count(p: B => Boolean): Int -def drop(n: Int): Iterator[B] -def dropWhile(p: B => Boolean): Iterator[B] -def duplicate: (Iterator[B], Iterator[B]) -def ensuring(cond: Boolean): java.util.Iterator[B] -def ensuring(cond: Boolean,msg: => Any): java.util.Iterator[B] -def ensuring(cond: java.util.Iterator[B] => Boolean): java.util.Iterator[B] -def ensuring(cond: java.util.Iterator[B] => Boolean,msg: => Any): java.util.Iterator[B] +def ->[B](y: B): (other.TestIterator[Nothing], B) +def ensuring(cond: Boolean): other.TestIterator[Nothing] +def ensuring(cond: Boolean,msg: => Any): other.TestIterator[Nothing] +def ensuring(cond: other.TestIterator[Nothing] => Boolean): other.TestIterator[Nothing] +def ensuring(cond: other.TestIterator[Nothing] => Boolean,msg: => Any): other.TestIterator[Nothing] def equals(x$1: Any): Boolean -def exists(p: B => Boolean): Boolean -def filter(p: B => Boolean): Iterator[B] -def filterNot(p: B => Boolean): Iterator[B] -def find(p: B => Boolean): Option[B] -def flatMap[B](f: B => scala.collection.GenTraversableOnce[B]): Iterator[B] -def foldLeft[B](z: B)(op: (B, B) => B): B -def foldRight[B](z: B)(op: (B, B) => B): B -def fold[A1 >: B](z: A1)(op: (A1, A1) => A1): A1 -def forall(p: B => Boolean): Boolean -def foreach[U](f: B => U): Unit def formatted(fmtstr: String): String -def grouped[B >: B](size: Int): Iterator[B]#GroupedIterator[B] -def hasDefiniteSize: Boolean -def hasNext(): Boolean +def hasNext: Boolean def hashCode(): Int -def indexOf[B >: B](elem: B): Int -def indexWhere(p: B => Boolean): Int -def isEmpty: Boolean -def isTraversableAgain: Boolean -def length: Int -def map[B](f: B => B): Iterator[B] -def maxBy[B](f: B => B)(implicit cmp: Ordering[B]): B -def max[B >: B](implicit cmp: Ordering[B]): B -def minBy[B](f: B => B)(implicit cmp: Ordering[B]): B -def min[B >: B](implicit cmp: Ordering[B]): B -def mkString(sep: String): String -def mkString(start: String,sep: String,end: String): String -def mkString: String -def next(): B -def nonEmpty: Boolean -def padTo[A1 >: B](len: Int,elem: A1): Iterator[A1] -def partition(p: B => Boolean): (Iterator[B], Iterator[B]) -def patch[B >: B](from: Int,patchElems: Iterator[B],replaced: Int): Iterator[B] -def product[B >: B](implicit num: Numeric[B]): B -def reduceLeftOption[B >: B](op: (B, B) => B): Option[B] -def reduceLeft[B >: B](op: (B, B) => B): B -def reduceOption[A1 >: B](op: (A1, A1) => A1): Option[A1] -def reduceRightOption[B >: B](op: (B, B) => B): Option[B] -def reduceRight[B >: B](op: (B, B) => B): B -def reduce[A1 >: B](op: (A1, A1) => A1): A1 -def remove(): Unit -def sameElements(that: Iterator[_]): Boolean -def scanLeft[B](z: B)(op: (B, B) => B): Iterator[B] -def scanRight[B](z: B)(op: (B, B) => B): Iterator[B] -def seq: Iterator[B] -def size: Int -def slice(from: Int,until: Int): Iterator[B] -def sliding[B >: B](size: Int,step: Int): Iterator[B]#GroupedIterator[B] -def span(p: B => Boolean): (Iterator[B], Iterator[B]) -def sum[B >: B](implicit num: Numeric[B]): B -def take(n: Int): Iterator[B] -def takeWhile(p: B => Boolean): Iterator[B] -def toArray[B >: B](implicit evidence$1: scala.reflect.ClassTag[B]): Array[B] -def toBuffer[B >: B]: scala.collection.mutable.Buffer[B] -def toIndexedSeq: scala.collection.immutable.IndexedSeq[B] -def toIterable: Iterable[B] -def toIterator: Iterator[B] -def toList: List[B] -def toMap[T, U](implicit ev: <:<[B,(T, U)]): scala.collection.immutable.Map[T,U] -def toSeq: Seq[B] -def toSet[B >: B]: scala.collection.immutable.Set[B] -def toStream: scala.collection.immutable.Stream[B] +def next: T def toString(): String -def toTraversable: Traversable[B] -def toVector: Vector[B] -def to[Col[_]](implicit cbf: scala.collection.generic.CanBuildFrom[Nothing,B,Col[B]]): Col[B] -def withFilter(p: B => Boolean): Iterator[B] -def zipAll[B, A1 >: B, B1 >: B](that: Iterator[B],thisElem: A1,thatElem: B1): Iterator[(A1, B1)] -def zipWithIndex: Iterator[(B, Int)] -def zip[B](that: Iterator[B]): Iterator[(B, B)] -def →[B](y: B): (java.util.Iterator[B], B) +def →[B](y: B): (other.TestIterator[Nothing], B) final def !=(x$1: Any): Boolean final def ##(): Int final def ==(x$1: Any): Boolean diff --git a/test/files/presentation/ide-bug-1000531/src/CrashOnLoad.scala b/test/files/presentation/ide-bug-1000531/src/CrashOnLoad.scala index 878bbfa19e..3f59282083 100644 --- a/test/files/presentation/ide-bug-1000531/src/CrashOnLoad.scala +++ b/test/files/presentation/ide-bug-1000531/src/CrashOnLoad.scala @@ -1,7 +1,14 @@ /** When this files is opened within the IDE, a typing error is reported. */ -class A[B] extends java.lang.Iterable[B] { +class A[B] extends TestIterable[B] { import scala.collection.JavaConversions._ - def iterator = Iterator.empty + def iterator: other.TestIterator[Nothing] = ??? - iterator. /*!*/ -}
\ No newline at end of file + iterator./*!*/ +} + +object other { + trait TestIterator[T] { + def hasNext: Boolean + def next: T + } +} diff --git a/test/files/presentation/ide-bug-1000531/src/TestIterable.java b/test/files/presentation/ide-bug-1000531/src/TestIterable.java new file mode 100644 index 0000000000..84a6fe77f1 --- /dev/null +++ b/test/files/presentation/ide-bug-1000531/src/TestIterable.java @@ -0,0 +1,7 @@ +public abstract class TestIterable<T> { + public abstract TestIterator<T> iterator(); + public static abstract class TestIterator<T> { + public abstract T next(); + public abstract boolean hasNext(); + } +} diff --git a/test/files/presentation/t7915.check b/test/files/presentation/t7915.check index b18b4ddb55..0849aaa82b 100644 --- a/test/files/presentation/t7915.check +++ b/test/files/presentation/t7915.check @@ -9,3 +9,23 @@ askHyperlinkPos for `bar` at (7,22) Foo.scala ================================================================================ [response] found askHyperlinkPos for `bar` at (2,7) Foo.scala ================================================================================ + +askHyperlinkPos for `Bar` at (8,11) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `Bar` at (1,7) Foo.scala +================================================================================ + +askHyperlinkPos for `baz` at (8,22) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `baz` at (2,31) Foo.scala +================================================================================ + +askHyperlinkPos for `Bar` at (9,11) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `Bar` at (1,7) Foo.scala +================================================================================ + +askHyperlinkPos for `baz` at (9,22) Foo.scala +================================================================================ +[response] found askHyperlinkPos for `baz` at (2,31) Foo.scala +================================================================================ diff --git a/test/files/presentation/t7915/src/Foo.scala b/test/files/presentation/t7915/src/Foo.scala index a4166ae5b4..5c9ca36a6e 100644 --- a/test/files/presentation/t7915/src/Foo.scala +++ b/test/files/presentation/t7915/src/Foo.scala @@ -1,9 +1,11 @@ class Bar { - def bar(b: Int = 2) {} + def bar(b: Int = 2) {}; def baz[X](b: Int = 2) {} } class Foo { def foo() { new Bar/*#*/().bar/*#*/() + new Bar/*#*/().baz/*#*/[Any]() + new Bar/*#*/().baz/*#*/() } } diff --git a/test/files/run/abstypetags_serialize.check b/test/files/run/abstypetags_serialize.check index bddc4523e6..1b5e2ebddf 100644 --- a/test/files/run/abstypetags_serialize.check +++ b/test/files/run/abstypetags_serialize.check @@ -1,2 +1,2 @@ -java.io.NotSerializableException: Test$$typecreator1$1 -java.io.NotSerializableException: Test$$typecreator2$1 +WeakTypeTag[T] +WeakTypeTag[U[String]] 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/collection-stacks.check b/test/files/run/collection-stacks.check index 895bde374d..3a366bfcdf 100644 --- a/test/files/run/collection-stacks.check +++ b/test/files/run/collection-stacks.check @@ -1,4 +1,4 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details 3-2-1: true 3-2-1: true apply diff --git a/test/files/run/colltest.check b/test/files/run/colltest.check index 1e850bb582..9579d781aa 100644 --- a/test/files/run/colltest.check +++ b/test/files/run/colltest.check @@ -1,4 +1,4 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details true false true diff --git a/test/files/run/colltest1.scala b/test/files/run/colltest1.scala index 8dce69afc9..e0ec378585 100644 --- a/test/files/run/colltest1.scala +++ b/test/files/run/colltest1.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warnings; re-run with -Yinline-warnings for details */ import scala.collection._ import scala.language.postfixOps diff --git a/test/files/run/compiler-asSeenFrom.scala b/test/files/run/compiler-asSeenFrom.scala index ea96c6fba7..677dd40ddc 100644 --- a/test/files/run/compiler-asSeenFrom.scala +++ b/test/files/run/compiler-asSeenFrom.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warning; re-run with -Yinline-warnings for details */ import scala.tools.nsc._ import scala.tools.partest.DirectTest diff --git a/test/files/run/constrained-types.check b/test/files/run/constrained-types.check index 9a106785a1..a3cd59b9fb 100644 --- a/test/files/run/constrained-types.check +++ b/test/files/run/constrained-types.check @@ -71,11 +71,11 @@ scala> var four = "four" four: String = four scala> val four2 = m(four) // should have an existential bound -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details four2: String @Annot(x) forSome { val x: String } = four scala> val four3 = four2 // should have the same type as four2 -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details four3: String @Annot(x) forSome { val x: String } = four scala> val stuff = m("stuff") // should not crash @@ -98,7 +98,7 @@ scala> def m = { val y : String @Annot(x) = x y } // x should not escape the local scope with a narrow type -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details m: String @Annot(x) forSome { val x: String } scala> @@ -112,7 +112,7 @@ scala> def n(y: String) = { } m("stuff".stripMargin) } // x should be existentially bound -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details n: (y: String)String @Annot(x) forSome { val x: String } scala> diff --git a/test/files/run/delambdafy_t6028.check b/test/files/run/delambdafy_t6028.check index 92cfbaefb6..7bd8cd7202 100644 --- a/test/files/run/delambdafy_t6028.check +++ b/test/files/run/delambdafy_t6028.check @@ -54,4 +54,4 @@ package <empty> { } } -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details diff --git a/test/files/run/delay-bad.check b/test/files/run/delay-bad.check index 5d8c5fa1d4..cb6e329f7a 100644 --- a/test/files/run/delay-bad.check +++ b/test/files/run/delay-bad.check @@ -4,7 +4,7 @@ delay-bad.scala:53: warning: a pure expression does nothing in statement positio delay-bad.scala:73: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses f(new { val x = 5 } with E() { 5 }) ^ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details // new C { } diff --git a/test/files/run/eta-expand-star2.check b/test/files/run/eta-expand-star2.check index cbf4781255..d6929e4969 100644 --- a/test/files/run/eta-expand-star2.check +++ b/test/files/run/eta-expand-star2.check @@ -1,2 +1,2 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details hello diff --git a/test/files/run/existentials-in-compiler.scala b/test/files/run/existentials-in-compiler.scala index d019d56b42..dfc7048b31 100644 --- a/test/files/run/existentials-in-compiler.scala +++ b/test/files/run/existentials-in-compiler.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warnings; re-run with -Yinline-warnings for details */ import scala.tools.nsc._ import scala.tools.partest.CompilerTest diff --git a/test/files/run/exprs_serialize.check b/test/files/run/exprs_serialize.check index 20ad6c110c..551823ccdc 100644 --- a/test/files/run/exprs_serialize.check +++ b/test/files/run/exprs_serialize.check @@ -1,2 +1,19 @@ -java.io.NotSerializableException: Test$$treecreator1$1 -java.io.NotSerializableException: Test$$treecreator2$1 +Expr[Int(2)](2) +Expr[java.lang.String]({ + def foo = "hello"; + foo.$plus("world!") +}) +Expr[Boolean]({ + def foo(x: Int) = { + class Local extends AnyRef { + def <init>() = { + super.<init>(); + () + }; + val f = 2 + }; + val obj = new Local(); + x.$percent(obj.f).$eq$eq(0) + }; + foo(5) +}) diff --git a/test/files/run/exprs_serialize.scala b/test/files/run/exprs_serialize.scala index c4310b0fe1..91027803b4 100644 --- a/test/files/run/exprs_serialize.scala +++ b/test/files/run/exprs_serialize.scala @@ -26,4 +26,14 @@ object Test extends App { test(reify(2)) test(reify{def foo = "hello"; foo + "world!"}) -}
\ No newline at end of file + test(reify { + def foo(x: Int) = { + class Local { + val f = 2 + } + val obj = new Local + x % obj.f == 0 + } + foo(5) + }) +} diff --git a/test/files/run/icode-reader-dead-code.check b/test/files/run/icode-reader-dead-code.check new file mode 100644 index 0000000000..d1739fed3b --- /dev/null +++ b/test/files/run/icode-reader-dead-code.check @@ -0,0 +1,19 @@ +Bytecode for method f + L0 + LINENUMBER 4 L0 + ICONST_1 + IRETURN + L1 + LOCALVARIABLE this Lp/A; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 +Bytecode for method f + L0 + LINENUMBER 4 L0 + ICONST_1 + ATHROW + IRETURN + L1 + LOCALVARIABLE this Lp/A; L0 L1 0 + MAXSTACK = 1 + MAXLOCALS = 1 diff --git a/test/files/run/icode-reader-dead-code.scala b/test/files/run/icode-reader-dead-code.scala new file mode 100644 index 0000000000..00ba58829f --- /dev/null +++ b/test/files/run/icode-reader-dead-code.scala @@ -0,0 +1,82 @@ +import java.io.{FileOutputStream, FileInputStream} + +import scala.tools.asm.{ClassWriter, Opcodes, ClassReader} +import scala.tools.asm.tree.{InsnNode, ClassNode} +import scala.tools.nsc.backend.jvm.AsmUtils +import scala.tools.partest.DirectTest +import scala.collection.JavaConverters._ + +/** + * Test that the ICodeReader does not crash if the bytecode of a method has unreachable code. + */ +object Test extends DirectTest { + def code: String = ??? + + def show(): Unit = { + // The bytecode of f will be modified using ASM by `addDeadCode` + val aCode = + """ + |package p + |class A { + | @inline final def f = 1 + |} + """.stripMargin + + val bCode = + """ + |package p + |class B { + | def g = (new A()).f + |} + """.stripMargin + + compileString(newCompiler("-usejavacp"))(aCode) + + addDeadCode() + + // If inlining fails, the compiler will issue an inliner warning that is not present in the + // check file + compileString(newCompiler("-usejavacp", "-optimise"))(bCode) + } + + def readClass(file: String) = { + val cnode = new ClassNode() + val is = new FileInputStream(file) + val reader = new ClassReader(is) + reader.accept(cnode, 0) + is.close() + cnode + } + + def writeClass(file: String, cnode: ClassNode): Unit = { + val writer = new ClassWriter(0) + cnode.accept(writer) + + val os = new FileOutputStream(file) + os.write(writer.toByteArray) + os.close() + } + + def addDeadCode() { + val file = (testOutput / "p" / "A.class").path + val cnode = readClass(file) + val method = cnode.methods.asScala.find(_.name == "f").head + + AsmUtils.traceMethod(method) + + val insns = method.instructions + val it = insns.iterator() + while (it.hasNext) { + val in = it.next() + if (in.getOpcode == Opcodes.IRETURN) { + // Insert an ATHROW before the IRETURN. The IRETURN will then be dead code. + // The ICodeReader should not crash if there's dead code. + insns.insert(in.getPrevious, new InsnNode(Opcodes.ATHROW)) + } + } + + AsmUtils.traceMethod(method) + + writeClass(file, cnode) + } +} diff --git a/test/files/run/inferred-type-constructors.check b/test/files/run/inferred-type-constructors.check index 5992ef02ad..4a63853bd9 100644 --- a/test/files/run/inferred-type-constructors.check +++ b/test/files/run/inferred-type-constructors.check @@ -1,4 +1,4 @@ -warning: there were 2 feature warning(s); re-run with -feature for details +warning: there were two feature warnings; re-run with -feature for details p.Iterable[Int] p.Set[Int] p.Seq[Int] diff --git a/test/files/run/is-valid-num.scala b/test/files/run/is-valid-num.scala index 65e8ceeca6..4ab2fac8dd 100644 --- a/test/files/run/is-valid-num.scala +++ b/test/files/run/is-valid-num.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warnings; re-run with -Yinline-warnings for details */ object Test { def x = BigInt("10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") diff --git a/test/files/run/iterator-from.scala b/test/files/run/iterator-from.scala index 269e859657..e2ca5864ea 100644 --- a/test/files/run/iterator-from.scala +++ b/test/files/run/iterator-from.scala @@ -1,5 +1,5 @@ /* This file tests iteratorFrom, keysIteratorFrom, and valueIteratorFrom on various sorted sets and maps - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warnings; re-run with -Yinline-warnings for details */ import scala.util.{Random => R} 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/literals.check b/test/files/run/literals.check index ed7c6ca5b3..62c5fd68ae 100644 --- a/test/files/run/literals.check +++ b/test/files/run/literals.check @@ -1,4 +1,4 @@ -warning: there were 5 deprecation warning(s); re-run with -deprecation for details +warning: there were 5 deprecation warnings; re-run with -deprecation for details test '\u0024' == '$' was successful test '\u005f' == '_' was successful test 65.asInstanceOf[Char] == 'A' was successful diff --git a/test/files/run/macro-rangepos-args.check b/test/files/run/macro-rangepos-args.check new file mode 100644 index 0000000000..d779505c66 --- /dev/null +++ b/test/files/run/macro-rangepos-args.check @@ -0,0 +1 @@ +Line: 3. Width: 5. diff --git a/test/files/run/macro-rangepos-args.flags b/test/files/run/macro-rangepos-args.flags new file mode 100644 index 0000000000..fcf951d907 --- /dev/null +++ b/test/files/run/macro-rangepos-args.flags @@ -0,0 +1 @@ +-Yrangepos
\ No newline at end of file diff --git a/test/files/run/macro-rangepos-args/Macros_1.scala b/test/files/run/macro-rangepos-args/Macros_1.scala new file mode 100644 index 0000000000..97b938613c --- /dev/null +++ b/test/files/run/macro-rangepos-args/Macros_1.scala @@ -0,0 +1,10 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl(c: Context)(x: c.Tree): c.Tree = { + import c.universe._ + Literal(Constant(s"Line: ${x.pos.line}. Width: ${x.pos.end - x.pos.start}.")) + } + def pos(x: Any): String = macro impl +} diff --git a/test/files/run/macro-rangepos-args/Test_2.scala b/test/files/run/macro-rangepos-args/Test_2.scala new file mode 100644 index 0000000000..8c770e9010 --- /dev/null +++ b/test/files/run/macro-rangepos-args/Test_2.scala @@ -0,0 +1,4 @@ +object Test extends App { + val x = 2 + println(Macros.pos(x + 2)) +}
\ No newline at end of file diff --git a/test/files/run/macro-rangepos-subpatterns.check b/test/files/run/macro-rangepos-subpatterns.check new file mode 100644 index 0000000000..760e15d019 --- /dev/null +++ b/test/files/run/macro-rangepos-subpatterns.check @@ -0,0 +1 @@ +The width of the subpattern is: 2 diff --git a/test/files/run/macro-rangepos-subpatterns.flags b/test/files/run/macro-rangepos-subpatterns.flags new file mode 100644 index 0000000000..fcf951d907 --- /dev/null +++ b/test/files/run/macro-rangepos-subpatterns.flags @@ -0,0 +1 @@ +-Yrangepos
\ No newline at end of file diff --git a/test/files/run/macro-rangepos-subpatterns/Macros_1.scala b/test/files/run/macro-rangepos-subpatterns/Macros_1.scala new file mode 100644 index 0000000000..0f30862347 --- /dev/null +++ b/test/files/run/macro-rangepos-subpatterns/Macros_1.scala @@ -0,0 +1,18 @@ +import scala.reflect.macros.whitebox.Context +import language.experimental.macros + +object Extractor { + def unapply(x: Any): Any = macro unapplyImpl + def unapplyImpl(c: Context)(x: c.Tree) = { + import c.universe._ + import internal._ + val pos = subpatterns(x).get.head.pos + q""" + new { + def isEmpty = false + def get = ${"The width of the subpattern is: " + (pos.end - pos.start + 1)} + def unapply(x: Any) = this + }.unapply($x) + """ + } +} diff --git a/test/files/run/macro-rangepos-subpatterns/Test_2.scala b/test/files/run/macro-rangepos-subpatterns/Test_2.scala new file mode 100644 index 0000000000..7b076e6632 --- /dev/null +++ b/test/files/run/macro-rangepos-subpatterns/Test_2.scala @@ -0,0 +1,5 @@ +object Test extends App { + 42 match { + case Extractor(a) => println(a) + } +} diff --git a/test/files/run/macroPlugins-isBlackbox/Macros_2.scala b/test/files/run/macroPlugins-isBlackbox/Macros_2.scala new file mode 100644 index 0000000000..a90dd702df --- /dev/null +++ b/test/files/run/macroPlugins-isBlackbox/Macros_2.scala @@ -0,0 +1,11 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl(c: Context) = { + import c.universe._ + q"42" + } + + def foo: Any = macro impl +}
\ No newline at end of file diff --git a/test/files/run/macroPlugins-isBlackbox/Plugin_1.scala b/test/files/run/macroPlugins-isBlackbox/Plugin_1.scala new file mode 100644 index 0000000000..b78a18ea6a --- /dev/null +++ b/test/files/run/macroPlugins-isBlackbox/Plugin_1.scala @@ -0,0 +1,21 @@ +package isblackbox + +import scala.tools.nsc.Global +import scala.tools.nsc.plugins.{Plugin => NscPlugin} + +class Plugin(val global: Global) extends NscPlugin { + import global._ + import analyzer._ + import scala.reflect.internal.Mode + + val name = "isBlackbox" + val description = "A sample analyzer plugin that overrides isBlackbox." + val components = Nil + addMacroPlugin(MacroPlugin) + + object MacroPlugin extends MacroPlugin { + override def pluginsIsBlackbox(macroDef: Symbol): Option[Boolean] = { + Some(false) + } + } +}
\ No newline at end of file diff --git a/test/files/run/macroPlugins-isBlackbox/Test_3.flags b/test/files/run/macroPlugins-isBlackbox/Test_3.flags new file mode 100644 index 0000000000..966df731d0 --- /dev/null +++ b/test/files/run/macroPlugins-isBlackbox/Test_3.flags @@ -0,0 +1 @@ +-Xplugin:.
\ No newline at end of file diff --git a/test/files/run/macroPlugins-isBlackbox/Test_3.scala b/test/files/run/macroPlugins-isBlackbox/Test_3.scala new file mode 100644 index 0000000000..552e888143 --- /dev/null +++ b/test/files/run/macroPlugins-isBlackbox/Test_3.scala @@ -0,0 +1,3 @@ +object Test extends App { + val x: Int = Macros.foo +}
\ No newline at end of file diff --git a/test/files/run/macroPlugins-isBlackbox/scalac-plugin.xml b/test/files/run/macroPlugins-isBlackbox/scalac-plugin.xml new file mode 100644 index 0000000000..09b9c14648 --- /dev/null +++ b/test/files/run/macroPlugins-isBlackbox/scalac-plugin.xml @@ -0,0 +1,4 @@ +<plugin> + <name>is-blackbox</name> + <classname>isblackbox.Plugin</classname> +</plugin>
\ No newline at end of file diff --git a/test/files/run/mapConserve.scala b/test/files/run/mapConserve.scala index d1d52f3107..f52af3b9f4 100644 --- a/test/files/run/mapConserve.scala +++ b/test/files/run/mapConserve.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warnings; re-run with -Yinline-warnings for details */ import scala.annotation.tailrec import scala.collection.mutable.ListBuffer diff --git a/test/files/run/names-defaults.check b/test/files/run/names-defaults.check index 0037822f3b..c358dc5849 100644 --- a/test/files/run/names-defaults.check +++ b/test/files/run/names-defaults.check @@ -1,7 +1,7 @@ names-defaults.scala:269: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses spawn(b = { val ttt = 1; ttt }, a = 0) ^ -warning: there were 4 deprecation warning(s); re-run with -deprecation for details +warning: there were four deprecation warnings; re-run with -deprecation for details 1: @ get: $ get: 2 @@ -124,3 +124,4 @@ List(1, 2) 3 3 (1,0), (1,2) +1 1 0 diff --git a/test/files/run/names-defaults.scala b/test/files/run/names-defaults.scala index 05cd4a540c..b7ed490cbc 100644 --- a/test/files/run/names-defaults.scala +++ b/test/files/run/names-defaults.scala @@ -401,6 +401,10 @@ object Test extends App { C4441a().copy() C4441b()().copy()() + // SI-8117 + def f8177(a: Int = 0, b: Int = 0, c: Int = 0) = s"$a $b $c" + println(f8177(a = 1, 1)) + // DEFINITIONS def test1(a: Int, b: String) = println(a +": "+ b) def test2(u: Int, v: Int)(k: String, l: Int) = println(l +": "+ k +", "+ (u + v)) diff --git a/test/files/run/pc-conversions.scala b/test/files/run/pc-conversions.scala index 19fef355c8..5fecac9d94 100644 --- a/test/files/run/pc-conversions.scala +++ b/test/files/run/pc-conversions.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warning; re-run with -Yinline-warnings for details */ import collection._ diff --git a/test/files/run/reflection-attachments.check b/test/files/run/reflection-attachments.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/reflection-attachments.check diff --git a/test/files/run/reflection-java-annotations.check b/test/files/run/reflection-java-annotations.check index 72d40989fe..842037254e 100644 --- a/test/files/run/reflection-java-annotations.check +++ b/test/files/run/reflection-java-annotations.check @@ -1,4 +1,4 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details List(JavaComplexAnnotation_1(v1 = 1, v10 = "hello", v101 = [101, 101], v102 = [102, 102], v103 = ['g', 'g'], v104 = [104, 104], v105 = [105L, 105L], v106 = [106.0, 106.0], v107 = [107.0, 107.0], v108 = [false, true], v11 = classOf[JavaAnnottee_1], v110 = ["hello", "world"], v111 = [classOf[JavaSimpleAnnotation_1], classOf[JavaComplexAnnotation_1]], v112 = [FOO, BAR], v113 = [JavaSimpleAnnotation_1(v1 = 21, v10 = "world2", v11 = classOf[JavaComplexAnnotation_1], v12 = BAR, v2 = 22, v3 = '\027', v4 = 24, v5 = 25L, v6 = 26.0, v7 = 27.0, v8 = false)], v12 = FOO, v13 = JavaSimpleAnnotation_1(v1 = 11, v10 = "world1", v11 = classOf[JavaSimpleAnnotation_1], v12 = FOO, v2 = 12, v3 = '\r', v4 = 14, v5 = 15L, v6 = 16.0, v7 = 17.0, v8 = false), v2 = 2, v3 = '\03', v4 = 4, v5 = 5L, v6 = 6.0, v7 = 7.0, v8 = false)) ======= new JavaComplexAnnotation_1(v1 = 1, v10 = "hello", v101 = Array(101, 101), v102 = Array(102, 102), v103 = Array('g', 'g'), v104 = Array(104, 104), v105 = Array(105L, 105L), v106 = Array(106.0, 106.0), v107 = Array(107.0, 107.0), v108 = Array(false, true), v11 = classOf[JavaAnnottee_1], v110 = Array("hello", "world"), v111 = Array(classOf[JavaSimpleAnnotation_1], classOf[JavaComplexAnnotation_1]), v112 = Array(FOO, BAR), v113 = Array(new JavaSimpleAnnotation_1(v1 = 21, v10 = "world2", v11 = classOf[JavaComplexAnnotation_1], v12 = BAR, v2 = 22, v3 = '\027', v4 = 24, v5 = 25L, v6 = 26.0, v7 = 27.0, v8 = false)), v12 = FOO, v13 = new JavaSimpleAnnotation_1(v1 = 11, v10 = "world1", v11 = classOf[JavaSimpleAnnotation_1], v12 = FOO, v2 = 12, v3 = '\r', v4 = 14, v5 = 15L, v6 = 16.0, v7 = 17.0, v8 = false), v2 = 2, v3 = '\03', v4 = 4, v5 = 5L, v6 = 6.0, v7 = 7.0, v8 = false) diff --git a/test/files/run/reflection-magicsymbols-repl.check b/test/files/run/reflection-magicsymbols-repl.check index a1bee76652..72aef1d3be 100644 --- a/test/files/run/reflection-magicsymbols-repl.check +++ b/test/files/run/reflection-magicsymbols-repl.check @@ -21,7 +21,7 @@ scala> def test(n: Int): Unit = { val x = sig.asInstanceOf[MethodType].params.head println(x.info) } -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details test: (n: Int)Unit scala> for (i <- 1 to 8) test(i) diff --git a/test/files/run/repl-javap-app.check b/test/files/run/repl-javap-app.check index 490860585c..1136b415d7 100644 --- a/test/files/run/repl-javap-app.check +++ b/test/files/run/repl-javap-app.check @@ -1,4 +1,5 @@ #partest java6 +Welcome to Scala Type in expressions to have them evaluated. Type :help for more information. @@ -6,16 +7,17 @@ scala> :javap -app MyApp$ public final void delayedEndpoint$MyApp$1(); Code: Stack=2, Locals=1, Args_size=1 - 0: getstatic #61; //Field scala/Console$.MODULE$:Lscala/Console$; - 3: ldc #63; //String Hello, delayed world. - 5: invokevirtual #67; //Method scala/Console$.println:(Ljava/lang/Object;)V + 0: getstatic #XX; //Field scala/Console$.MODULE$:Lscala/Console$; + 3: ldc #XX; //String Hello, delayed world. + 5: invokevirtual #XX; //Method scala/Console$.println:(Ljava/lang/Object;)V 8: return LocalVariableTable: Start Length Slot Name Signature 0 9 0 this LMyApp$; scala> -#partest !java6 +#partest java7 +Welcome to Scala Type in expressions to have them evaluated. Type :help for more information. @@ -24,9 +26,9 @@ scala> :javap -app MyApp$ flags: ACC_PUBLIC, ACC_FINAL Code: stack=2, locals=1, args_size=1 - 0: getstatic #61 // Field scala/Console$.MODULE$:Lscala/Console$; - 3: ldc #63 // String Hello, delayed world. - 5: invokevirtual #67 // Method scala/Console$.println:(Ljava/lang/Object;)V + 0: getstatic #XX // Field scala/Console$.MODULE$:Lscala/Console$; + 3: ldc #XX // String Hello, delayed world. + 5: invokevirtual #XX // Method scala/Console$.println:(Ljava/lang/Object;)V 8: return LocalVariableTable: Start Length Slot Name Signature @@ -36,3 +38,26 @@ scala> :javap -app MyApp$ } scala> +#partest java8 +Welcome to Scala +Type in expressions to have them evaluated. +Type :help for more information. + +scala> :javap -app MyApp$ + public final void delayedEndpoint$MyApp$1(); + descriptor: ()V + flags: ACC_PUBLIC, ACC_FINAL + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #XX // Field scala/Console$.MODULE$:Lscala/Console$; + 3: ldc #XX // String Hello, delayed world. + 5: invokevirtual #XX // Method scala/Console$.println:(Ljava/lang/Object;)V + 8: return + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this LMyApp$; + LineNumberTable: + line 5: 0 +} + +scala> diff --git a/test/files/run/repl-javap-app.scala b/test/files/run/repl-javap-app.scala index be04920be1..ad6076c2d5 100644 --- a/test/files/run/repl-javap-app.scala +++ b/test/files/run/repl-javap-app.scala @@ -7,4 +7,15 @@ object MyApp extends App { object Test extends ReplTest { def code = ":javap -app MyApp$" + + override def welcoming = true + + // The constant pool indices are not the same for GenASM / GenBCode, so + // replacing the exact numbers by XX. + lazy val hasConstantPoolRef = """(.*)(#\d\d)(.*)""".r + + override def normalize(s: String) = s match { + case hasConstantPoolRef(start, ref, end) => start + "#XX" + end + case _ => super.normalize(s) + } } diff --git a/test/files/run/repl-power.check b/test/files/run/repl-power.check index e56901e0f2..8a8ca46012 100644 --- a/test/files/run/repl-power.check +++ b/test/files/run/repl-power.check @@ -11,11 +11,11 @@ scala> :power scala> // guarding against "error: reference to global is ambiguous" scala> global.emptyValDef // "it is imported twice in the same scope by ..." -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details res0: $r.global.noSelfType.type = private val _ = _ scala> val tp = ArrayClass[scala.util.Random] // magic with tags -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details tp: $r.global.Type = Array[scala.util.Random] scala> tp.memberType(Array_apply) // evidence diff --git a/test/files/run/richs.check b/test/files/run/richs.check index 02a98b376d..cf265ae007 100644 --- a/test/files/run/richs.check +++ b/test/files/run/richs.check @@ -1,4 +1,4 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details RichCharTest1: true diff --git a/test/files/run/search.check b/test/files/run/search.check index a885696509..e0c55043e3 100644 --- a/test/files/run/search.check +++ b/test/files/run/search.check @@ -1,6 +1,6 @@ Found(2) Found(4) -InsertionPoint(9) +InsertionPoint(10) Found(2) Found(4) -InsertionPoint(9) +InsertionPoint(10) diff --git a/test/files/run/stringinterpolation_macro-run.check b/test/files/run/stringinterpolation_macro-run.check index ead61e76ac..c7f46bac87 100644 --- a/test/files/run/stringinterpolation_macro-run.check +++ b/test/files/run/stringinterpolation_macro-run.check @@ -63,5 +63,9 @@ She is 4 feet tall. 05/26/12 05/26/12 % + mind +------ +matter + 7 7 9 7 9 9 diff --git a/test/files/run/stringinterpolation_macro-run.scala b/test/files/run/stringinterpolation_macro-run.scala index ff779dd1d3..e18375d521 100644 --- a/test/files/run/stringinterpolation_macro-run.scala +++ b/test/files/run/stringinterpolation_macro-run.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warnings; re-run with -Yinline-warnings for details */ object Test extends App { @@ -115,6 +115,7 @@ println(f"""${"1234"}%TD""") // literals and arg indexes println(f"%%") +println(f" mind%n------%nmatter%n") println(f"${7}%d %<d ${9}%d") println(f"${7}%d %2$$d ${9}%d") diff --git a/test/files/run/synchronized.check b/test/files/run/synchronized.check index 6e99739633..eab191b4ed 100644 --- a/test/files/run/synchronized.check +++ b/test/files/run/synchronized.check @@ -1,4 +1,4 @@ -warning: there were 14 inliner warning(s); re-run with -Yinline-warnings for details +warning: there were 14 inliner warnings; re-run with -Yinline-warnings for details .|. c1.f1: OK .|. c1.fi: OK .|... c1.fv: OK diff --git a/test/files/run/t2212.check b/test/files/run/t2212.check index 8ab4d60ab3..1465f1341a 100644 --- a/test/files/run/t2212.check +++ b/test/files/run/t2212.check @@ -1,4 +1,4 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details LinkedList(1) LinkedList(1) true diff --git a/test/files/run/t3361.check b/test/files/run/t3361.check index c18bdc9aff..5e0a763501 100644 --- a/test/files/run/t3361.check +++ b/test/files/run/t3361.check @@ -1 +1 @@ -warning: there were 16 deprecation warning(s); re-run with -deprecation for details +warning: there were 16 deprecation warnings; re-run with -deprecation for details diff --git a/test/files/run/t3888.check b/test/files/run/t3888.check index 844ca54682..df1629dd7e 100644 --- a/test/files/run/t3888.check +++ b/test/files/run/t3888.check @@ -1 +1 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details diff --git a/test/files/run/t3970.check b/test/files/run/t3970.check index bd89fff9d9..0683a6c1a6 100644 --- a/test/files/run/t3970.check +++ b/test/files/run/t3970.check @@ -1 +1 @@ -warning: there were 5 deprecation warning(s); re-run with -deprecation for details +warning: there were 5 deprecation warnings; re-run with -deprecation for details diff --git a/test/files/run/t3996.check b/test/files/run/t3996.check index a92ddc0e51..a9ecc29fea 100644 --- a/test/files/run/t3996.check +++ b/test/files/run/t3996.check @@ -1 +1 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details diff --git a/test/files/run/t4080.check b/test/files/run/t4080.check index 1953a68ad3..462e925b76 100644 --- a/test/files/run/t4080.check +++ b/test/files/run/t4080.check @@ -1,2 +1,2 @@ -warning: there were 3 deprecation warning(s); re-run with -deprecation for details +warning: there were three deprecation warnings; re-run with -deprecation for details LinkedList(1, 0, 2, 3) diff --git a/test/files/run/t4172.check b/test/files/run/t4172.check index d94638d27e..a748430e2e 100644 --- a/test/files/run/t4172.check +++ b/test/files/run/t4172.check @@ -2,7 +2,7 @@ Type in expressions to have them evaluated. Type :help for more information. scala> val c = { class C { override def toString = "C" }; ((new C, new C { def f = 2 })) } -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details c: (C, C{def f: Int}) forSome { type C <: AnyRef } = (C,C) scala> diff --git a/test/files/run/t4396.check b/test/files/run/t4396.check index a75e1f257f..d38fb7fae7 100644 --- a/test/files/run/t4396.check +++ b/test/files/run/t4396.check @@ -1,4 +1,4 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details hallo constructor out:22 diff --git a/test/files/run/t4461.check b/test/files/run/t4461.check index 9488669324..346993af6f 100644 --- a/test/files/run/t4461.check +++ b/test/files/run/t4461.check @@ -1,4 +1,4 @@ -warning: there were 4 deprecation warning(s); re-run with -deprecation for details +warning: there were four deprecation warnings; re-run with -deprecation for details Include(End,1) Include(End,2) Include(End,3) diff --git a/test/files/run/t4594-repl-settings.scala b/test/files/run/t4594-repl-settings.scala index d2335460e5..8b8b2e3746 100644 --- a/test/files/run/t4594-repl-settings.scala +++ b/test/files/run/t4594-repl-settings.scala @@ -11,7 +11,7 @@ object Test extends SessionTest { |depp: String | |scala> def a = depp - |warning: there were 1 deprecation warning(s); re-run with -deprecation for details + |warning: there was one deprecation warning; re-run with -deprecation for details |a: String | |scala> :settings +deprecation diff --git a/test/files/run/t4680.check b/test/files/run/t4680.check index 512bfd4b54..21a1e0cd15 100644 --- a/test/files/run/t4680.check +++ b/test/files/run/t4680.check @@ -4,7 +4,7 @@ t4680.scala:51: warning: a pure expression does nothing in statement position; y t4680.scala:69: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses new { val x = 5 } with E() { 5 } ^ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details // new C { } diff --git a/test/files/run/t4710.check b/test/files/run/t4710.check index f2335d1bdd..6ee7198b4b 100644 --- a/test/files/run/t4710.check +++ b/test/files/run/t4710.check @@ -2,7 +2,7 @@ Type in expressions to have them evaluated. Type :help for more information. scala> def method : String = { implicit def f(s: Symbol) = "" ; 'symbol } -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details method: String scala> diff --git a/test/files/run/t4813.check b/test/files/run/t4813.check index a92ddc0e51..a9ecc29fea 100644 --- a/test/files/run/t4813.check +++ b/test/files/run/t4813.check @@ -1 +1 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details diff --git a/test/files/run/t5428.check b/test/files/run/t5428.check index a46514ae7c..52fce09399 100644 --- a/test/files/run/t5428.check +++ b/test/files/run/t5428.check @@ -1,2 +1,2 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details Stack(8, 7, 6, 5, 4, 3) diff --git a/test/files/run/t576.check b/test/files/run/t576.check index 6458d5d743..22f3843abf 100644 --- a/test/files/run/t576.check +++ b/test/files/run/t576.check @@ -1,4 +1,4 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details 1 2 3 diff --git a/test/files/run/t5905-features.scala b/test/files/run/t5905-features.scala index a3848eef2a..b518d61145 100644 --- a/test/files/run/t5905-features.scala +++ b/test/files/run/t5905-features.scala @@ -3,13 +3,13 @@ import tools.partest.DirectTest // verify that all languageFeature names are accepted by -language object Test extends DirectTest { - override def code = "class Code { def f = (1 to 10) size }" // exercise a feature + override def code = "class Code { def f = (1 to 10) size }" // exercise a feature to sanity-check coverage of -language options override def extraSettings = s"-usejavacp -d ${testOutput.path}" override def show() = { - val global = newCompiler("-language:postfixOps", "-Ystop-after:typer") - compileString(global)(code) + val global = newCompiler("-Ystop-after:typer") + compileString(global)("") // warm me up, scotty import global._ exitingTyper { //def isFeature(s: Symbol) = s.annotations.exists((a: AnnotationInfo) => a.tpe <:< typeOf[scala.annotation.meta.languageFeature]) @@ -21,6 +21,8 @@ object Test extends DirectTest { assert(feats.nonEmpty, "Test must find feature flags.") + //compile("junk") // tragically, does not fail the test, i.e., arg must not be totally borked + //dynamics,postfixOps,reflectiveCalls,implicitConversions,higherKinds,existentials,experimental.macros compile(s"-language:$all") } diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check index a6c4db8f11..55ff42d8d7 100644 --- a/test/files/run/t6028.check +++ b/test/files/run/t6028.check @@ -81,4 +81,4 @@ package <empty> { } } -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details diff --git a/test/files/run/t6111.check b/test/files/run/t6111.check index 1f23a87f73..5880658001 100644 --- a/test/files/run/t6111.check +++ b/test/files/run/t6111.check @@ -1,3 +1,3 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details (8,8) (x,x) diff --git a/test/files/run/t6292.check b/test/files/run/t6292.check index 6232ba7519..6f7430d5b8 100644 --- a/test/files/run/t6292.check +++ b/test/files/run/t6292.check @@ -1 +1 @@ -warning: there were 7 deprecation warning(s); re-run with -deprecation for details +warning: there were 7 deprecation warnings; re-run with -deprecation for details diff --git a/test/files/run/t6318_primitives.check b/test/files/run/t6318_primitives.check index b330f91276..4bc5e598eb 100644 --- a/test/files/run/t6318_primitives.check +++ b/test/files/run/t6318_primitives.check @@ -1,36 +1,54 @@ -true +Checking if byte matches byte Some(1) -false +Checking if byte matches short None -true +Checking if class java.lang.Byte matches byte Some(1) -false +Checking if short matches short +Some(1) +Checking if short matches char None -true +Checking if class java.lang.Short matches short +Some(1) +Checking if char matches char Some() -false +Checking if char matches int None -true +Checking if class java.lang.Character matches char +Some() +Checking if int matches int Some(1) -false +Checking if int matches long None -true +Checking if class java.lang.Integer matches int Some(1) -false +Checking if long matches long +Some(1) +Checking if long matches float None -true +Checking if class java.lang.Long matches long +Some(1) +Checking if float matches float Some(1.0) -false +Checking if float matches double None -true +Checking if class java.lang.Float matches float Some(1.0) -false +Checking if double matches double +Some(1.0) +Checking if double matches boolean None -true +Checking if class java.lang.Double matches double +Some(1.0) +Checking if boolean matches boolean Some(true) -false +Checking if boolean matches void None -true +Checking if class java.lang.Boolean matches boolean +Some(true) +Checking if void matches void Some(()) -false +Checking if void matches byte None +Checking if class scala.runtime.BoxedUnit matches void +Some(()) diff --git a/test/files/run/t6318_primitives.scala b/test/files/run/t6318_primitives.scala index 30f27120b3..bc8ec88359 100644 --- a/test/files/run/t6318_primitives.scala +++ b/test/files/run/t6318_primitives.scala @@ -2,70 +2,88 @@ import scala.reflect.{ClassTag, classTag} object Test extends App { def test[T: ClassTag](x: T) { - println(classTag[T].runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[T].runtimeClass}") println(classTag[T].unapply(x)) } { val x = 1.toByte - println(ClassTag.Byte.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Byte].runtimeClass}") println(ClassTag.Byte.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Short].runtimeClass}") + println(ClassTag.Short.unapply(x)) test(x) } { val x = 1.toShort - println(ClassTag.Short.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Short].runtimeClass}") println(ClassTag.Short.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Char].runtimeClass}") + println(ClassTag.Char.unapply(x)) test(x) } { val x = 1.toChar - println(ClassTag.Char.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Char].runtimeClass}") println(ClassTag.Char.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Int].runtimeClass}") + println(ClassTag.Int.unapply(x)) test(x) } { val x = 1.toInt - println(ClassTag.Int.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Int].runtimeClass}") println(ClassTag.Int.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Long].runtimeClass}") + println(ClassTag.Long.unapply(x)) test(x) } { val x = 1.toLong - println(ClassTag.Long.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Long].runtimeClass}") println(ClassTag.Long.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Float].runtimeClass}") + println(ClassTag.Float.unapply(x)) test(x) } { val x = 1.toFloat - println(ClassTag.Float.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Float].runtimeClass}") println(ClassTag.Float.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Double].runtimeClass}") + println(ClassTag.Double.unapply(x)) test(x) } { val x = 1.toDouble - println(ClassTag.Double.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Double].runtimeClass}") println(ClassTag.Double.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Boolean].runtimeClass}") + println(ClassTag.Boolean.unapply(x)) test(x) } { val x = true - println(ClassTag.Boolean.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Boolean].runtimeClass}") println(ClassTag.Boolean.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Unit].runtimeClass}") + println(ClassTag.Unit.unapply(x)) test(x) } { val x = () - println(ClassTag.Unit.runtimeClass.isAssignableFrom(x.getClass)) + println(s"Checking if ${x.getClass} matches ${classTag[Unit].runtimeClass}") println(ClassTag.Unit.unapply(x)) + println(s"Checking if ${x.getClass} matches ${classTag[Byte].runtimeClass}") + println(ClassTag.Byte.unapply(x)) test(x) } -}
\ No newline at end of file +} diff --git a/test/files/run/t6329_repl.check b/test/files/run/t6329_repl.check index 5049426ab4..ad0bb46e5b 100644 --- a/test/files/run/t6329_repl.check +++ b/test/files/run/t6329_repl.check @@ -5,28 +5,28 @@ scala> import scala.reflect.classTag import scala.reflect.classTag scala> classManifest[scala.List[_]] -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details res0: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List[<?>] scala> classTag[scala.List[_]] res1: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List scala> classManifest[scala.collection.immutable.List[_]] -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details res2: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List[<?>] scala> classTag[scala.collection.immutable.List[_]] res3: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List scala> classManifest[Predef.Set[_]] -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details res4: scala.reflect.ClassTag[scala.collection.immutable.Set[_]] = scala.collection.immutable.Set[<?>] scala> classTag[Predef.Set[_]] res5: scala.reflect.ClassTag[scala.collection.immutable.Set[_]] = scala.collection.immutable.Set scala> classManifest[scala.collection.immutable.Set[_]] -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details res6: scala.reflect.ClassTag[scala.collection.immutable.Set[_]] = scala.collection.immutable.Set[<?>] scala> classTag[scala.collection.immutable.Set[_]] diff --git a/test/files/run/t6329_repl_bug.check b/test/files/run/t6329_repl_bug.check index 44c41cfd03..38a8de5606 100644 --- a/test/files/run/t6329_repl_bug.check +++ b/test/files/run/t6329_repl_bug.check @@ -8,7 +8,7 @@ scala> import scala.reflect.runtime._ import scala.reflect.runtime._ scala> classManifest[List[_]] -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details res0: scala.reflect.ClassTag[List[_]] = scala.collection.immutable.List[<?>] scala> scala.reflect.classTag[List[_]] diff --git a/test/files/run/t6329_vanilla_bug.check b/test/files/run/t6329_vanilla_bug.check index 640d168a8a..01bf0636ea 100644 --- a/test/files/run/t6329_vanilla_bug.check +++ b/test/files/run/t6329_vanilla_bug.check @@ -1,3 +1,3 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details scala.collection.immutable.List[<?>] scala.collection.immutable.List diff --git a/test/files/run/t6481.check b/test/files/run/t6481.check index df40722242..4a3f6f7ee9 100644 --- a/test/files/run/t6481.check +++ b/test/files/run/t6481.check @@ -1,4 +1,4 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details delayed init new foo(1, 2) delayed init diff --git a/test/files/run/t6690.check b/test/files/run/t6690.check index a92ddc0e51..a9ecc29fea 100644 --- a/test/files/run/t6690.check +++ b/test/files/run/t6690.check @@ -1 +1 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details diff --git a/test/files/run/t6863.check b/test/files/run/t6863.check index fea22b582f..d4df5f7a74 100644 --- a/test/files/run/t6863.check +++ b/test/files/run/t6863.check @@ -10,4 +10,4 @@ t6863.scala:46: warning: comparing values of types Unit and Unit using `==' will t6863.scala:59: warning: comparing values of types Unit and Unit using `==' will always yield true assert({ () => x }.apply == ()) ^ -warning: there were 4 deprecation warning(s); re-run with -deprecation for details +warning: there were four deprecation warnings; re-run with -deprecation for details diff --git a/test/files/run/t6935.check b/test/files/run/t6935.check index 844ca54682..df1629dd7e 100644 --- a/test/files/run/t6935.check +++ b/test/files/run/t6935.check @@ -1 +1 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details diff --git a/test/files/run/t7096.scala b/test/files/run/t7096.scala index e7a894fc23..872562dd4d 100644 --- a/test/files/run/t7096.scala +++ b/test/files/run/t7096.scala @@ -1,5 +1,5 @@ /* - * filter: inliner warning\(s\); re-run with -Yinline-warnings for details + * filter: inliner warning; re-run with -Yinline-warnings for details */ import scala.tools.partest._ import scala.tools.nsc._ diff --git a/test/files/run/t7319.check b/test/files/run/t7319.check index b7443aa0c4..2ac4142098 100644 --- a/test/files/run/t7319.check +++ b/test/files/run/t7319.check @@ -5,15 +5,15 @@ scala> class M[A] defined class M scala> implicit def ma0[A](a: A): M[A] = null -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details ma0: [A](a: A)M[A] scala> implicit def ma1[A](a: A): M[A] = null -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details ma1: [A](a: A)M[A] scala> def convert[F[X <: F[X]]](builder: F[_ <: F[_]]) = 0 -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details convert: [F[X <: F[X]]](builder: F[_ <: F[_]])Int scala> convert(Some[Int](0)) diff --git a/test/files/run/t7582.check b/test/files/run/t7582.check index 225fb1ace8..cd951d8d4f 100644 --- a/test/files/run/t7582.check +++ b/test/files/run/t7582.check @@ -1,2 +1,2 @@ -warning: there were 1 inliner warning(s); re-run with -Yinline-warnings for details +warning: there was one inliner warning; re-run with -Yinline-warnings for details 2 diff --git a/test/files/run/t7582b.check b/test/files/run/t7582b.check index 225fb1ace8..cd951d8d4f 100644 --- a/test/files/run/t7582b.check +++ b/test/files/run/t7582b.check @@ -1,2 +1,2 @@ -warning: there were 1 inliner warning(s); re-run with -Yinline-warnings for details +warning: there was one inliner warning; re-run with -Yinline-warnings for details 2 diff --git a/test/files/run/t7932.check b/test/files/run/t7932.check index 13d64f1d3c..3f0a0c4f62 100644 --- a/test/files/run/t7932.check +++ b/test/files/run/t7932.check @@ -1,3 +1,3 @@ -warning: there were 1 feature warning(s); re-run with -feature for details +warning: there was one feature warning; re-run with -feature for details public Category<?> C.category() public Category<scala.Tuple2> C.category1() diff --git a/test/files/run/t7974.check b/test/files/run/t7974.check index 0be496d8d0..d8152d3286 100644 --- a/test/files/run/t7974.check +++ b/test/files/run/t7974.check @@ -1,6 +1,5 @@ public class Symbols { - // compiled from: Symbols.scala @@ -18,20 +17,14 @@ public class Symbols { // access flags 0x9 public static <clinit>()V - L0 - LINENUMBER 2 L0 GETSTATIC scala/Symbol$.MODULE$ : Lscala/Symbol$; LDC "Symbolic1" INVOKEVIRTUAL scala/Symbol$.apply (Ljava/lang/String;)Lscala/Symbol; PUTSTATIC Symbols.symbol$1 : Lscala/Symbol; - L1 - LINENUMBER 3 L1 GETSTATIC scala/Symbol$.MODULE$ : Lscala/Symbol$; LDC "Symbolic2" INVOKEVIRTUAL scala/Symbol$.apply (Ljava/lang/String;)Lscala/Symbol; PUTSTATIC Symbols.symbol$2 : Lscala/Symbol; - L2 - LINENUMBER 5 L2 GETSTATIC scala/Symbol$.MODULE$ : Lscala/Symbol$; LDC "Symbolic3" INVOKEVIRTUAL scala/Symbol$.apply (Ljava/lang/String;)Lscala/Symbol; @@ -42,63 +35,41 @@ public class Symbols { // access flags 0x1 public someSymbol1()Lscala/Symbol; - L0 - LINENUMBER 2 L0 GETSTATIC Symbols.symbol$1 : Lscala/Symbol; ARETURN - L1 - LOCALVARIABLE this LSymbols; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 public someSymbol2()Lscala/Symbol; - L0 - LINENUMBER 3 L0 GETSTATIC Symbols.symbol$2 : Lscala/Symbol; ARETURN - L1 - LOCALVARIABLE this LSymbols; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 public sameSymbol1()Lscala/Symbol; - L0 - LINENUMBER 4 L0 GETSTATIC Symbols.symbol$1 : Lscala/Symbol; ARETURN - L1 - LOCALVARIABLE this LSymbols; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 public someSymbol3()Lscala/Symbol; - L0 - LINENUMBER 5 L0 ALOAD 0 GETFIELD Symbols.someSymbol3 : Lscala/Symbol; ARETURN - L1 - LOCALVARIABLE this LSymbols; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x1 public <init>()V - L0 - LINENUMBER 6 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init> ()V - L1 - LINENUMBER 5 L1 ALOAD 0 GETSTATIC Symbols.symbol$3 : Lscala/Symbol; PUTFIELD Symbols.someSymbol3 : Lscala/Symbol; RETURN - L2 - LOCALVARIABLE this LSymbols; L0 L2 0 MAXSTACK = 2 MAXLOCALS = 1 } diff --git a/test/files/run/t7974/Test.scala b/test/files/run/t7974/Test.scala index 433d9061a7..29d2b9cb64 100644 --- a/test/files/run/t7974/Test.scala +++ b/test/files/run/t7974/Test.scala @@ -6,7 +6,7 @@ import scala.tools.nsc.util.stringFromWriter object Test extends BytecodeTest { def show { - val classNode = loadClassNode("Symbols", skipDebugInfo = false) + val classNode = loadClassNode("Symbols", skipDebugInfo = true) val textifier = new Textifier classNode.accept(new TraceClassVisitor(null, textifier, null)) diff --git a/test/files/run/t8196.check b/test/files/run/t8196.check index b32f42cf07..d11dc27e68 100644 --- a/test/files/run/t8196.check +++ b/test/files/run/t8196.check @@ -1,7 +1,7 @@ t8196.scala:26: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses form2.g1 // comment this line in order to make the test pass ^ -warning: there were 2 feature warning(s); re-run with -feature for details +warning: there were two feature warnings; re-run with -feature for details Scope{ final private val f1: Int } 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.check b/test/files/run/t8549.check index a92ddc0e51..a9ecc29fea 100644 --- a/test/files/run/t8549.check +++ b/test/files/run/t8549.check @@ -1 +1 @@ -warning: there were 2 deprecation warning(s); re-run with -deprecation for details +warning: there were two deprecation warnings; re-run with -deprecation for details 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/files/run/t8608-no-format.scala b/test/files/run/t8608-no-format.scala new file mode 100644 index 0000000000..71c369a7ea --- /dev/null +++ b/test/files/run/t8608-no-format.scala @@ -0,0 +1,15 @@ + +import scala.tools.partest.JavapTest + +object Test extends JavapTest { + def code = """ + |f"hello, world" + |:javap -prv - + """.stripMargin + + // no format + override def yah(res: Seq[String]) = { + // note: avoid the word "information" + res forall (!_.contains("StringOps.format")) + } +} diff --git a/test/files/run/t8611a.flags b/test/files/run/t8611a.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/run/t8611a.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/run/t8611a.scala b/test/files/run/t8611a.scala new file mode 100644 index 0000000000..99304df762 --- /dev/null +++ b/test/files/run/t8611a.scala @@ -0,0 +1,16 @@ +trait K +trait L + +object O { + type LK = K with L + val A: LK = new K with L + val B: LK = new K with L +} + +object Test extends App { + val scrut: O.LK = O.B + scrut match { + case O.A => ??? + case O.B => // spurious unreachable + } +} diff --git a/test/files/run/t8611b.flags b/test/files/run/t8611b.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/run/t8611b.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/run/t8611b.scala b/test/files/run/t8611b.scala new file mode 100644 index 0000000000..2df17c9ca0 --- /dev/null +++ b/test/files/run/t8611b.scala @@ -0,0 +1,54 @@ +sealed trait KrafsDescription + +abstract class NotWorkingEnum extends Enumeration { + + type ExtendedValue = Value with KrafsDescription + + def Enum(inDescription: String): ExtendedValue = { + new Val(nextId) with KrafsDescription { + } + } +} + +abstract class WorkingEnum extends Enumeration { + + type ExtendedValue = Value + + def Enum(inDescription: String): ExtendedValue = { + new Val(nextId) { + } + } +} + +object NotWorkingTab extends NotWorkingEnum { + val a = Enum("A") + val b = Enum("B") +} + +object WorkingTab extends WorkingEnum { + val a = Enum("A") + val b = Enum("B") +} + +object Test extends App { + testGris() + testWorking() + + def testGris() { + val pipp = NotWorkingTab.b + pipp match { + case NotWorkingTab.a => ??? + case NotWorkingTab.b => + case _ => ??? + } + } + + def testWorking() { + val stuff = WorkingTab.a + stuff match { + case WorkingTab.a => + case WorkingTab.b => ??? + case _ => ??? + } + } +} diff --git a/test/files/run/t8611c.flags b/test/files/run/t8611c.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/run/t8611c.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/run/t8611c.scala b/test/files/run/t8611c.scala new file mode 100644 index 0000000000..2bd17f29a5 --- /dev/null +++ b/test/files/run/t8611c.scala @@ -0,0 +1,21 @@ +trait K +trait L + +object O { + type LK = K with L +} + +object Test extends App { + local + + def local = { + val A: O.LK = new K with L + val B: O.LK = new K with L + val scrut: O.LK = A + scrut match { + case B if "".isEmpty => ??? + case A => + case B => ??? + } + } +} diff --git a/test/files/run/t8637.check b/test/files/run/t8637.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/t8637.check diff --git a/test/files/run/t8637.scala b/test/files/run/t8637.scala new file mode 100644 index 0000000000..99c8d4c413 --- /dev/null +++ b/test/files/run/t8637.scala @@ -0,0 +1,9 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.currentMirror +import scala.tools.reflect.ToolBox + +object Test extends App { + val tb = currentMirror.mkToolBox() + tb.compile(q"true > true") + tb.typecheck(q"true > true") +}
\ No newline at end of file diff --git a/test/files/run/t8690.check b/test/files/run/t8690.check new file mode 100644 index 0000000000..72f076c4d8 --- /dev/null +++ b/test/files/run/t8690.check @@ -0,0 +1,2 @@ +non-empty iterator +abcdef diff --git a/test/files/run/t8690.scala b/test/files/run/t8690.scala new file mode 100644 index 0000000000..ab8b45b2a7 --- /dev/null +++ b/test/files/run/t8690.scala @@ -0,0 +1,12 @@ +import scala.io.Source +import java.io.ByteArrayInputStream + +object Test extends App { + val txt = "abcdef" + + val in = new ByteArrayInputStream(txt.getBytes()); + val source = Source.fromInputStream(in); + println(source.toString) // forces the BufferedSource to look at the head of the input + + println(source.mkString) // used to return "bcdef" ... +} diff --git a/test/files/run/t8708_b.check b/test/files/run/t8708_b.check new file mode 100644 index 0000000000..30be62a307 --- /dev/null +++ b/test/files/run/t8708_b.check @@ -0,0 +1,8 @@ +Scope{ + def <init>: <?>; + sealed abstract trait T extends ; + def foo: <?> +} +Scope{ + def f: <?> +} diff --git a/test/files/run/t8708_b/A_1.scala b/test/files/run/t8708_b/A_1.scala new file mode 100644 index 0000000000..e767420f9e --- /dev/null +++ b/test/files/run/t8708_b/A_1.scala @@ -0,0 +1,8 @@ +package p + +class C { + + sealed trait T { def f: Int } + + def foo: T = new T { def f = 1 } +} diff --git a/test/files/run/t8708_b/Test_2.scala b/test/files/run/t8708_b/Test_2.scala new file mode 100644 index 0000000000..c978490609 --- /dev/null +++ b/test/files/run/t8708_b/Test_2.scala @@ -0,0 +1,21 @@ +import scala.tools.partest._ +import java.io.{Console => _, _} + +object Test extends DirectTest { + + override def extraSettings: String = "-usejavacp -cp " + testOutput.path + + override def code = "" + + override def show(): Unit = { + val g = newCompiler() + withRun(g)(r => { + val c = g.rootMirror.getRequiredClass("p.C") + println(c.info.decls) + val t = c.info.member(g.newTypeName("T")) + // this test ensrues that the <local child> dummy class symbol is not entered in the + // scope of trait T during unpickling. + println(t.info.decls) + }) + } +} diff --git a/test/files/run/tailcalls.check b/test/files/run/tailcalls.check index 7607921856..92d4f8a3c8 100644 --- a/test/files/run/tailcalls.check +++ b/test/files/run/tailcalls.check @@ -50,6 +50,10 @@ test NonTailCall.f2 test TailCall.b1 was successful test TailCall.b2 was successful test FancyTailCalls.tcTryLocal was successful +test FancyTailCalls.tcInBooleanExprFirstOp was successful +test FancyTailCalls.tcInBooleanExprSecondOp was successful +test FancyTailCalls.tcInIfCond was successful +test FancyTailCalls.tcInPatternGuard was successful test FancyTailCalls.differentInstance was successful test PolyObject.tramp was successful #partest avian @@ -104,5 +108,9 @@ test NonTailCall.f2 test TailCall.b1 was successful test TailCall.b2 was successful test FancyTailCalls.tcTryLocal was successful +test FancyTailCalls.tcInBooleanExprFirstOp was successful +test FancyTailCalls.tcInBooleanExprSecondOp was successful +test FancyTailCalls.tcInIfCond was successful +test FancyTailCalls.tcInPatternGuard was successful test FancyTailCalls.differentInstance was successful test PolyObject.tramp was successful diff --git a/test/files/run/tailcalls.scala b/test/files/run/tailcalls.scala index 1653b14de9..8df2dcfcb6 100644 --- a/test/files/run/tailcalls.scala +++ b/test/files/run/tailcalls.scala @@ -213,6 +213,33 @@ class FancyTailCalls { } finally {} } + def tcInBooleanExprFirstOp(x: Int, v: Int): Boolean = { + { + def loop(n: Int): Int = if (n == 0) v else loop(n - 1) + loop(x) + } == v && true + } + def tcInBooleanExprSecondOp(x: Int, v: Int): Boolean = { + true && { + def loop(n: Int): Int = if (n == 0) v else loop(n - 1) + loop(x) + } == v + } + def tcInIfCond(x: Int, v: Int): Boolean = { + if ({ + def loop(n: Int): Int = if (n == 0) v else loop(n - 1) + loop(x) + } == v) true else false + } + def tcInPatternGuard(x: Int, v: Int): Boolean = + v match { + case _ if + { + def loop(n: Int): Int = if (n == 0) v else loop(n - 1) + loop(x) == v + } => true + } + import FancyTailCalls._ final def differentInstance(n: Int, v: Int): Int = { if (n == 0) v @@ -376,8 +403,12 @@ object Test { check_success_b("TailCall.b2", TailCall.b2(max), true) val FancyTailCalls = new FancyTailCalls; - check_success("FancyTailCalls.tcTryLocal", FancyTailCalls.tcTryLocal(max, max), max) - check_success("FancyTailCalls.differentInstance", FancyTailCalls.differentInstance(max, 42), 42) + check_success("FancyTailCalls.tcTryLocal", FancyTailCalls.tcTryLocal(max, max), max) + check_success_b("FancyTailCalls.tcInBooleanExprFirstOp", FancyTailCalls.tcInBooleanExprFirstOp(max, max), true) + check_success_b("FancyTailCalls.tcInBooleanExprSecondOp", FancyTailCalls.tcInBooleanExprSecondOp(max, max), true) + check_success_b("FancyTailCalls.tcInIfCond", FancyTailCalls.tcInIfCond(max, max), true) + check_success_b("FancyTailCalls.tcInPatternGuard", FancyTailCalls.tcInPatternGuard(max, max), true) + check_success("FancyTailCalls.differentInstance", FancyTailCalls.differentInstance(max, 42), 42) check_success("PolyObject.tramp", PolyObject.tramp[Int](max), 0) } diff --git a/test/files/run/typetags_serialize.check b/test/files/run/typetags_serialize.check index f79436ea5d..22928a2e94 100644 --- a/test/files/run/typetags_serialize.check +++ b/test/files/run/typetags_serialize.check @@ -1,2 +1,3 @@ -java.io.NotSerializableException: scala.reflect.api.TypeTags$PredefTypeCreator -java.io.NotSerializableException: Test$$typecreator1$1 +TypeTag[Int] +TypeTag[String] +TypeTag[Test.C[Double]] diff --git a/test/files/run/typetags_serialize.scala b/test/files/run/typetags_serialize.scala index 3c842e6cc9..a7a7845232 100644 --- a/test/files/run/typetags_serialize.scala +++ b/test/files/run/typetags_serialize.scala @@ -4,6 +4,10 @@ import scala.reflect.runtime.{universe => ru} import scala.reflect.runtime.{currentMirror => cm} object Test extends App { + class C[A] { + def m(a: A): Int = 5 + } + def test(tag: TypeTag[_]) = try { val fout = new ByteArrayOutputStream() @@ -26,4 +30,5 @@ object Test extends App { test(implicitly[TypeTag[Int]]) test(implicitly[TypeTag[String]]) + test(implicitly[TypeTag[C[Double]]]) }
\ No newline at end of file diff --git a/test/files/run/unittest_collection.check b/test/files/run/unittest_collection.check index 844ca54682..df1629dd7e 100644 --- a/test/files/run/unittest_collection.check +++ b/test/files/run/unittest_collection.check @@ -1 +1 @@ -warning: there were 1 deprecation warning(s); re-run with -deprecation for details +warning: there was one deprecation warning; re-run with -deprecation for details diff --git a/test/files/run/virtpatmat_typetag.check b/test/files/run/virtpatmat_typetag.check index cac9d9a4d6..00df8b5e81 100644 --- a/test/files/run/virtpatmat_typetag.check +++ b/test/files/run/virtpatmat_typetag.check @@ -1,9 +1,9 @@ -1 is not a Int; it's a class java.lang.Integer +1 is a Int 1 is a java.lang.Integer 1 is not a java.lang.String; it's a class java.lang.Integer true is a Any woele is a java.lang.String -1 is not a Int; it's a class java.lang.Integer +1 is a Int 1 is a java.lang.Integer 1 is not a java.lang.String; it's a class java.lang.Integer true is a Any diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 45392de582..409f07037e 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -310,4 +310,16 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { val cases = List(cq"a => b", cq"c => d") assertEqAst(q"{ case ..$cases }", "{ case a => b case c => d }") } + + property("SI-8609 a") = test { + val q1 = q"val x = 1" + val q2 = q"..$q1; val y = 2" + assert(q2 ≈ q"{ val x = 1; val y = 2 }") + } + + property("SI-8609 b") = test { + val q1 = q"import foo.bar" + val q2 = q"..$q1; val y = 2" + assert(q2 ≈ q"{ import foo.bar; val y = 2 }") + } } diff --git a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala index 49ffaff630..07e8f3faac 100644 --- a/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermDeconstructionProps.scala @@ -246,4 +246,11 @@ object TermDeconstructionProps extends QuasiquoteProperties("term deconstruction assert(f ≈ `new`) assert(argss.isEmpty) } + + property("SI-8703 extract block with single expression") = test { + val q"{ $a }" = Block(Nil, q"1") + val Literal(Constant(1)) = a + val q"{ $b }" = q"2" + val Literal(Constant(2)) = b + } } 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/IndexedSeqOptimizedTest.scala b/test/junit/scala/collection/IndexedSeqOptimizedTest.scala new file mode 100644 index 0000000000..e5382907af --- /dev/null +++ b/test/junit/scala/collection/IndexedSeqOptimizedTest.scala @@ -0,0 +1,16 @@ +package scala.collection + +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.Assert._ +import org.junit.Test + +@RunWith(classOf[JUnit4]) +class IndexedSeqOptimizedTest { + + @Test + def notThrowsAnExceptionInLastIndexOf() { + assertEquals(0, (Array(2): collection.mutable.WrappedArray[Int]).lastIndexWhere(_ => true, 1)) + assertEquals(2, "abc123".lastIndexWhere(_.isLetter, 6)) + } +} 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/PrintersTest.scala b/test/junit/scala/reflect/internal/PrintersTest.scala index 4587417a99..1458b942dc 100644 --- a/test/junit/scala/reflect/internal/PrintersTest.scala +++ b/test/junit/scala/reflect/internal/PrintersTest.scala @@ -33,10 +33,10 @@ object PrinterHelper { def wrapCode(source: String) = { val context = sm""" |trait PrintersContext { - | class baz extends scala.annotation.StaticAnnotation; - | class foo1[A, B] extends scala.annotation.StaticAnnotation; - | class foo2[A, B](a: scala.Int)(b: scala.Int) extends scala.annotation.StaticAnnotation; - | class foo3[Af, Bf](a: scala.Int)(b: scala.Float, c: PrintersContext.this.foo1[Af, Bf]) extends scala.annotation.StaticAnnotation; + | class baz extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; + | class foo1[A, B] extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; + | class foo2[A, B](a: scala.Int)(b: scala.Int) extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; + | class foo3[Af, Bf](a: scala.Int)(b: scala.Float, c: PrintersContext.this.foo1[Af, Bf]) extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; | trait A1; | trait B1; |${source.trim.lines map {" " + _} mkString s"$LF"} @@ -54,8 +54,12 @@ object PrinterHelper { } } - def assertTreeCode(tree: Tree)(code: String) = { - assertEquals("using quasiquote or given tree"+LF, code.trim, normalizeEOL(showCode(tree))) + def assertTreeCode(tree: Tree, typecheck: Boolean = false)(code: String) = { + if (typecheck) { + assertEquals("using quasiquote or given tree (typechecked)"+LF, code.trim, normalizeEOL(showCode(toolbox.typecheck(tree)))) + } else { + assertEquals("using quasiquote or given tree"+LF, code.trim, normalizeEOL(showCode(tree))) + } } def assertPrintedCode(source: String, checkTypedTree: Boolean = true, wrapCode: Boolean = false) = { @@ -312,17 +316,17 @@ trait BasePrintTests { @Test def testFunc1 = assertResultCode( code = "List(1, 2, 3).map((i: Int) => i - 1)")( parsedCode = "List(1, 2, 3).map(((i: Int) => i.-(1)))", - typedCode = sm"scala.collection.immutable.List.apply(1, 2, 3).map(((i: scala.Int) => i.-(1)))(scala.collection.immutable.List.canBuildFrom)") + typedCode = sm"scala.collection.immutable.List.apply[Int](1, 2, 3).map[Int, List[Int]](((i: scala.Int) => i.-(1)))(scala.collection.immutable.List.canBuildFrom[Int])") @Test def testFunc2 = assertResultCode( code = "val sum: Seq[Int] => Int = _ reduceLeft (_+_)")( parsedCode = "val sum: _root_.scala.Function1[Seq[Int], Int] = ((x$1) => x$1.reduceLeft(((x$2, x$3) => x$2.+(x$3))))", - typedCode = "val sum: _root_.scala.Function1[scala.`package`.Seq[scala.Int], scala.Int] = ((x$1) => x$1.reduceLeft(((x$2, x$3) => x$2.+(x$3))))") + typedCode = "val sum: _root_.scala.Function1[scala.`package`.Seq[scala.Int], scala.Int] = ((x$1: Seq[Int]) => x$1.reduceLeft[Int](((x$2: Int, x$3: Int) => x$2.+(x$3))))") @Test def testFunc3 = assertResultCode( code = "List(1, 2, 3) map (_ - 1)")( parsedCode = "List(1, 2, 3).map(((x$1) => x$1.-(1))) ", - typedCode = "scala.collection.immutable.List.apply(1, 2, 3).map(((x$1) => x$1.-(1)))(scala.collection.immutable.List.canBuildFrom)") + typedCode = "scala.collection.immutable.List.apply[Int](1, 2, 3).map[Int, List[Int]](((x$1: Int) => x$1.-(1)))(scala.collection.immutable.List.canBuildFrom[Int])") @Test def testFunc4 = assertResultCode( code = "val x: String => Int = ((str: String) => 1)")( @@ -401,7 +405,8 @@ trait ClassPrintTests { @Test def testClassWithImplicitParams = assertPrintedCode("class X(var i: scala.Int)(implicit val d: scala.Double, var f: scala.Float)") - @Test def testClassWithEarly = assertPrintedCode(sm""" + @Test def testClassWithEarly = + assertPrintedCode(sm""" |class X(var i: scala.Int) extends { | val a = i; | type B @@ -419,15 +424,22 @@ trait ClassPrintTests { | throw Throw2.this.e |}""") - /* - class Test { - val (a, b) = (1, 2) - } - */ - @Test def testClassWithAssignmentWithTuple1 = assertPrintedCode(sm""" + @Test def testClassWithAssignmentWithTuple1 = assertResultCode(sm""" |class Test { - | private[this] val x$$1 = (scala.Tuple2.apply(1, 2): @scala.unchecked) match { - | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply(a, b) + | val (a, b) = (1, 2) + |}""")( + parsedCode = sm""" + |class Test { + | private[this] val x$$1 = (scala.Tuple2(1, 2): @scala.unchecked) match { + | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2(a, b) + | }; + | val a = x$$1._1; + | val b = x$$1._2 + |}""", + typedCode = sm""" + |class Test { + | private[this] val x$$1 = (scala.Tuple2.apply[Int, Int](1, 2): @scala.unchecked) match { + | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply[Int, Int](a, b) | }; | val a = Test.this.x$$1._1; | val b = Test.this.x$$1._2 @@ -448,8 +460,8 @@ trait ClassPrintTests { |}""", typedCode = sm""" |class Test { - | private[this] val x$$1 = (scala.Predef.ArrowAssoc(1).->(2): @scala.unchecked) match { - | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply(a, b) + | private[this] val x$$1 = (scala.Predef.ArrowAssoc[Int](1).->[Int](2): @scala.unchecked) match { + | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply[Int, Int](a, b) | }; | val a = Test.this.x$$1._1; | val b = Test.this.x$$1._2 @@ -462,8 +474,8 @@ trait ClassPrintTests { */ @Test def testClassWithPatternMatchInAssignment = assertPrintedCode(sm""" |class Test { - | private[this] val x$$1 = (scala.collection.immutable.List.apply(1, 3, 5): @scala.unchecked) match { - | case scala.collection.immutable.List((one @ _), (three @ _), (five @ _)) => scala.Tuple3.apply(one, three, five) + | private[this] val x$$1 = (scala.collection.immutable.List.apply[scala.Int](1, 3, 5): @scala.unchecked) match { + | case scala.collection.immutable.List((one @ _), (three @ _), (five @ _)) => scala.Tuple3.apply[scala.Int, scala.Int, scala.Int](one, three, five) | }; | val one = Test.this.x$$1._1; | val three = Test.this.x$$1._2; @@ -626,7 +638,7 @@ trait ClassPrintTests { @Test def testObjectWithPatternMatch1 = assertPrintedCode(sm""" |object PM1 { - | scala.collection.immutable.List.apply(1, 2) match { + | scala.collection.immutable.List.apply[scala.Int](1, 2) match { | case (i @ _) => i | } |}""") @@ -715,7 +727,7 @@ trait ClassPrintTests { |}""", typedCode = sm""" |object PM5 { - | scala.collection.immutable.List.apply(1, 2) match { + | scala.collection.immutable.List.apply[Int](1, 2) match { | case scala.`package`.::((x @ _), (xs @ _)) => x | } |}""") @@ -756,7 +768,7 @@ trait ClassPrintTests { @Test def testObjectWithPatternMatch8 = assertPrintedCode(sm""" |{ | object Extractor { - | def unapply(i: scala.Int) = scala.Some.apply(i) + | def unapply(i: scala.Int) = scala.Some.apply[scala.Int](i) | }; | object PM9 { | 42 match { @@ -991,7 +1003,7 @@ trait ValAndDefPrintTests { @Test def testDefWithLazyVal2 = assertPrintedCode(sm""" |def a = { - | lazy val test = { + | lazy val test: Unit = { | scala.Predef.println(); | scala.Predef.println() | }; @@ -1161,4 +1173,17 @@ trait QuasiTreesPrintTests { |case class X(x: Int, s: String) { | def y = "test" |}""") + + @Test def testQuasiCaseClassWithTypes1 = assertTreeCode(q"""case class X(x: ${typeOf[Int]}, s: ${typeOf[String]}){ def y = "test" }""")(sm""" + |case class X(x: Int, s: String) { + | def y = "test" + |}""") + + @Test def testQuasiCaseClassWithTypes2 = assertTreeCode(q"""case class X(x: ${typeOf[Int]}, s: ${typeOf[String]}){ def y = "test" }""", typecheck = true)(sm""" + |{ + | case class X(x: Int, s: String) { + | def y = "test" + | }; + | () + |}""") }
\ No newline at end of file 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/TypesTest.scala b/test/junit/scala/reflect/internal/TypesTest.scala new file mode 100644 index 0000000000..95194ef0a4 --- /dev/null +++ b/test/junit/scala/reflect/internal/TypesTest.scala @@ -0,0 +1,35 @@ +package scala.reflect.internal + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import scala.tools.nsc.symtab.SymbolTableForUnitTesting + +@RunWith(classOf[JUnit4]) +class TypesTest { + + object symbolTable extends SymbolTableForUnitTesting + import symbolTable._, definitions._ + + @Test + def testRefinedTypeSI8611(): Unit = { + def stringNarrowed = StringTpe.narrow + assert(stringNarrowed != stringNarrowed) + assert(!(stringNarrowed =:= stringNarrowed)) + + def boolWithString = refinedType(BooleanTpe :: StringTpe :: Nil, NoSymbol) + assert(boolWithString != boolWithString) + assert(boolWithString =:= boolWithString) + + val boolWithString1 = boolWithString + val boolWithString1narrow1 = boolWithString1.narrow + val boolWithString1narrow2 = boolWithString1.narrow + // Two narrowings of the same refinement end up =:=. This was the root + // cause of SI-8611. See `narrowUniquely` in `Logic` for the workaround. + assert(boolWithString1narrow1 =:= boolWithString1narrow2) + val uniquelyNarrowed1 = refinedType(boolWithString1narrow1 :: Nil, NoSymbol) + val uniquelyNarrowed2 = refinedType(boolWithString1narrow2 :: Nil, NoSymbol) + assert(uniquelyNarrowed1 =:= uniquelyNarrowed2) + } +} 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/nsc/symtab/SymbolTableForUnitTesting.scala b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala index 25d8c4667f..91868a5683 100644 --- a/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala +++ b/test/junit/scala/tools/nsc/symtab/SymbolTableForUnitTesting.scala @@ -72,6 +72,18 @@ class SymbolTableForUnitTesting extends SymbolTable { def picklerPhase: scala.reflect.internal.Phase = SomePhase def erasurePhase: scala.reflect.internal.Phase = SomePhase + // Members declared in scala.reflect.internal.Reporting + def reporter = new scala.reflect.internal.ReporterImpl { + protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit = println(msg) + } + + // minimal Run to get Reporting wired + def currentRun = new RunReporting {} + class PerRunReporting extends PerRunReportingBase { + def deprecationWarning(pos: Position, msg: String): Unit = reporter.warning(pos, msg) + } + protected def PerRunReporting = new PerRunReporting + // Members declared in scala.reflect.internal.SymbolTable def currentRunId: Int = 1 def log(msg: => AnyRef): Unit = println(msg) 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/test/scaladoc/run/t8672.check b/test/scaladoc/run/t8672.check new file mode 100644 index 0000000000..d7194c73bf --- /dev/null +++ b/test/scaladoc/run/t8672.check @@ -0,0 +1,4 @@ +Some(Chain(List(Text(New in release 1.2.3.4, it works), Text(.)))) +Some(Text(Sentence no period)) +Some(Chain(List(Text(Sentence period at end), Text(.)))) +Done. diff --git a/test/scaladoc/run/t8672.scala b/test/scaladoc/run/t8672.scala new file mode 100644 index 0000000000..8a9b5086bd --- /dev/null +++ b/test/scaladoc/run/t8672.scala @@ -0,0 +1,32 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + override def code = """ + class C { + + /** + * New in release 1.2.3.4, it works. Next sentence. + * Next Line. + */ + def method1 = 0 + + /** Sentence no period */ + def method2 = 0 + + /** Sentence period at end.*/ + def method3 = 0 + } + """ + + def scaladocSettings = "" + + def testModel(root: Package) = { + import access._ + val ms = List("method1", "method2", "method3") + for (m <- ms) { + val method = root._class("C")._method(m) + println(method.comment.get.body.summary) + } + } +} diff --git a/versions.properties b/versions.properties index 0e56f731ed..c334629d20 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,16 +14,17 @@ 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 +jline.version=2.12 # external modules, used internally (not shipped) partest.version.number=1.0.0 |