diff options
339 files changed, 5934 insertions, 6187 deletions
diff --git a/build.xml b/build.xml index 9c98c34b32..e1c19fd909 100644..100755 --- a/build.xml +++ b/build.xml @@ -1,10 +1,20 @@ <?xml version="1.0" encoding="UTF-8"?> <project name="sabbus" default="build" xmlns:artifact="urn:maven-artifact-ant"> + <include file="test/build-partest.xml" as="partest"/> + <description> SuperSabbus for Scala core, builds the scala library and compiler. It can also package it as a simple distribution, tests it for stable bootstrapping and against the Scala test suite. </description> +<!-- HINTS + + - for faster builds, have a build.properties in the same directory as build.xml that says: + locker.skip=1 + starr.use.released=1 + +--> + <!-- USAGE FROM JENKINS SCRIPTS IS (CURRENTLY) AS FOLLOWS: ant $antArgs $scalacArgs $targets @@ -16,7 +26,20 @@ scalacArgs examples: "-Dscalac.args=\"-Yrangepos\" -Dpartest.scalac_opts=\"-Yrangepos\"" targets exercised: - build-opt nightly test.suite test.continuations.suite test.scaladoc locker.done + locker.done build-opt nightly test.suite test.continuations.suite test.scaladoc +--> + +<!-- To use Zinc with the ant build: + - install zinc and symlink the installed zinc script to ${basedir}/tools/zinc (${basedir} is where build.xml and the rest of your checkout resides) + - make sure to set ZINC_OPTS to match ANT_OPTS! +--> + +<!-- +TODO: + - detect zinc anywhere on PATH + - automatically set ZINC_OPTS + - skip locker (and test.stability) by default to speed up PR validation, still do full build & testing during nightly + - (rework the build to pack locker and build using that when using zinc) --> @@ -44,8 +67,8 @@ targets exercised: <target name="distpack-maven" depends="dist.done, docs.done"> <ant antfile="${src.dir}/build/pack.xml" target="pack-maven.done" inheritall="yes" inheritrefs="yes"/></target> - <target name="distpack-opt" description="Builds an optimised distribution."> <optimize name="distpack"/></target> - <target name="distpack-maven-opt" description="Builds an optimised maven distribution."><optimize name="distpack-maven"/></target> + <target name="distpack-opt" description="Builds an optimised distribution."> <optimized name="distpack"/></target> + <target name="distpack-maven-opt" description="Builds an optimised maven distribution."><optimized name="distpack-maven"/></target> <target name="all.done" depends="dist.done, test.done"/> @@ -57,8 +80,8 @@ targets exercised: <target name="nightly.checkall"> <antcall target="nightly-nopt"> <param name="partest.scalac_opts" value="-Ycheck:all"/></antcall></target> - <target name="clean" depends="locker.clean" description="Removes binaries of compiler and library. Distributions are untouched."/> - <target name="docsclean" depends="docs.clean" description="Removes generated documentation. Distributions are untouched."/> + <target name="clean" depends="quick.clean" description="Removes binaries of compiler and library. Locker and distributions are untouched."/> + <target name="docsclean" depends="docs.clean" description="Removes generated documentation. Distributions are untouched."/> <target name="distclean" depends="dist.clean" description="Removes all distributions. Binaries and documentation are untouched."/> <macrodef name="optimized" > @@ -74,63 +97,68 @@ targets exercised: <property environment="env"/> <!-- Prevents system classpath from being used --> - <property name="build.sysclasspath" value="ignore"/> + <property name="build.sysclasspath" value="ignore"/> <!-- Defines the repository layout --> - <property name="docs.dir" value="${basedir}/docs"/> - <property name="lib.dir" value="${basedir}/lib"/> - <property name="src.dir" value="${basedir}/src"/> - <property name="partest.dir" value="${basedir}/test"/> + <property name="docs.dir" value="${basedir}/docs"/> + <property name="lib.dir" value="${basedir}/lib"/> + <property name="src.dir" value="${basedir}/src"/> + <property name="partest.dir" value="${basedir}/test"/> - <property name="lib-ant.dir" value="${lib.dir}/ant"/> + <property name="lib-ant.dir" value="${lib.dir}/ant"/> <!-- For developers: any jars placed in this dir will be added to the classpath of all targets and copied into quick/pack/etc builds. --> - <property name="lib-extra.dir" value="${lib.dir}/extra"/> + <property name="lib-extra.dir" value="${lib.dir}/extra"/> <!-- Loads custom properties definitions --> <property file="${basedir}/build.properties"/> + <!-- Generating version number --> <property file="${basedir}/build.number"/> + <!-- read starr.version --> + <property file="${basedir}/starr.number"/> + <!-- Sets location of pre-compiled libraries --> - <property name="library.starr.jar" value="${lib.dir}/scala-library.jar"/> - <property name="reflect.starr.jar" value="${lib.dir}/scala-reflect.jar"/> - <property name="compiler.starr.jar" value="${lib.dir}/scala-compiler.jar"/> - <property name="jline.jar" value="${lib.dir}/jline.jar"/> - <property name="ant.jar" value="${ant.home}/lib/ant.jar"/> - <property name="scalacheck.jar" value="${lib.dir}/scalacheck.jar"/> + <property name="library.starr.jar" value="${lib.dir}/scala-library.jar"/> + <property name="reflect.starr.jar" value="${lib.dir}/scala-reflect.jar"/> + <property name="compiler.starr.jar" value="${lib.dir}/scala-compiler.jar"/> + <property name="jline.jar" value="${lib.dir}/jline.jar"/> + <property name="ant.jar" value="${ant.home}/lib/ant.jar"/> + <property name="scalacheck.jar" value="${lib.dir}/scalacheck.jar"/> <!-- Sets location of build folders --> - <property name="build.dir" value="${basedir}/build"/> - <property name="build-asm.dir" value="${build.dir}/asm"/> - <property name="build-locker.dir" value="${build.dir}/locker"/> - <property name="build-palo.dir" value="${build.dir}/palo"/> - <property name="build-quick.dir" value="${build.dir}/quick"/> - <property name="build-pack.dir" value="${build.dir}/pack"/> - <property name="build-osgi.dir" value="${build.dir}/osgi"/> - <property name="build-strap.dir" value="${build.dir}/strap"/> - <property name="build-docs.dir" value="${build.dir}/scaladoc"/> - <property name="build-libs.dir" value="${build.dir}/libs"/> - <property name="build-sbt.dir" value="${build.dir}/sbt-interface"/> - - <property name="test.osgi.src" value="${partest.dir}/osgi/src"/> - <property name="test.osgi.classes" value="${build-osgi.dir}/classes"/> - - <property name="dists.dir" value="${basedir}/dists"/> - - <property name="copyright.string" value="Copyright 2002-2013, LAMP/EPFL"/> - <property name="partest.version.number" value="0.9.2"/> + <property name="build.dir" value="${basedir}/build"/> + <property name="build-libs.dir" value="${build.dir}/libs"/> + <property name="build-asm.dir" value="${build.dir}/asm"/> + <property name="build-forkjoin.dir" value="${build-libs.dir}"/> + <property name="build-locker.dir" value="${build.dir}/locker"/> + <property name="build-palo.dir" value="${build.dir}/palo"/> + <property name="build-quick.dir" value="${build.dir}/quick"/> + <property name="build-pack.dir" value="${build.dir}/pack"/> + <property name="build-osgi.dir" value="${build.dir}/osgi"/> + <property name="build-strap.dir" value="${build.dir}/strap"/> + <property name="build-docs.dir" value="${build.dir}/scaladoc"/> + <property name="build-sbt.dir" value="${build.dir}/sbt-interface"/> + + <property name="test.osgi.src" value="${partest.dir}/osgi/src"/> + <property name="test.osgi.classes" value="${build-osgi.dir}/classes"/> + + <property name="dists.dir" value="${basedir}/dists"/> + + <property name="copyright.string" value="Copyright 2002-2013, LAMP/EPFL"/> + <property name="partest.version.number" value="0.9.3"/> <!-- 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"/> - <property name="jvm.opts" value=""/> + <property name="java.flags" value="-Xmx256M -Xms32M"/> + <property name="jvm.opts" value=""/> <!-- if ANT_OPTS is already set by the environment, it will be unaltered, but if it is unset it will take this default value. --> - <property name="env.ANT_OPTS" value="-Xms1536M -Xmx1536M -Xss1M -XX:MaxPermSize=192M -XX:+UseParallelGC" /> + <property name="env.ANT_OPTS" value="-Xms1536M -Xmx1536M -Xss1M -XX:MaxPermSize=192M -XX:+UseParallelGC" /> - <property name="scalacfork.jvmargs" value="${env.ANT_OPTS} ${jvm.opts}"/> + <property name="scalacfork.jvmargs" value="${env.ANT_OPTS} ${jvm.opts}"/> <!-- =========================================================================== INITIALIZATION @@ -167,11 +195,15 @@ targets exercised: <path id="maven-ant-tasks.classpath" path="${lib-ant.dir}/maven-ant-tasks-2.1.1.jar" /> <typedef resource="org/apache/maven/artifact/ant/antlib.xml" uri="urn:maven-artifact-ant" classpathref="maven-ant-tasks.classpath" /> + <!-- Resolve maven dependencies --> + <!-- work around http://jira.codehaus.org/browse/MANTTASKS-203: java.lang.ClassCastException: org.codehaus.plexus.DefaultPlexusContainer cannot be cast to org.codehaus.plexus.PlexusContainer on repeated use of artifact:dependencies --> <if><not><isset property="maven-deps-done"></isset></not><then> + <mkdir dir="${user.home}/.m2/repository"/> + <!-- This task has an issue where if the user directory does not exist, so we create it above. UGH. --> <artifact:dependencies pathId="extra.tasks.classpath" filesetId="extra.tasks.fileset"> <dependency groupId="biz.aQute" artifactId="bnd" version="1.50.0"/> </artifact:dependencies> @@ -190,25 +222,32 @@ targets exercised: <dependency groupId="org.apache.felix" artifactId="org.apache.felix.framework" version="3.2.2"/> </artifact:dependencies> + <artifact:dependencies pathId="partest.extras.classpath" filesetId="partest.extras.fileset" versionsId="partest.extras.versions"> + <dependency groupId="com.googlecode.java-diff-utils" artifactId="diffutils" version="1.3.0"/> + </artifact:dependencies> + <!-- BND support --> <typedef resource="aQute/bnd/ant/taskdef.properties" classpathref="extra.tasks.classpath" /> - <!-- Resolve maven dependencies --> - <mkdir dir="${user.home}/.m2/repository"/> - <!-- This task has an issue where if the user directory does not exist, so we create it above. UGH. --> - <artifact:dependencies pathId="dependency.classpath" filesetId="dependency.fileset"> - <!--<dependency groupId="com.typesafe" artifactId="config" version="0.4.0"/>--> - </artifact:dependencies> - - <if><isset property="starr.version"/><then> + <!-- Download STARR via maven if `starr.use.released` is set, + and `starr.version` is specified (see the starr.number properties file). + Want to slow down STARR changes, using only released versions. --> + <if><isset property="starr.use.released"/><then> + <echo message="Using Scala ${starr.version} for STARR."/> <artifact:dependencies pathId="starr.core.path"> <dependency groupId="org.scala-lang" artifactId="scala-library" version="${starr.version}"/> <dependency groupId="org.scala-lang" artifactId="scala-reflect" version="${starr.version}"/> <dependency groupId="org.scala-lang" artifactId="scala-compiler" version="${starr.version}"/> - </artifact:dependencies> - </then></if> + </artifact:dependencies></then> + <else> + <path id="starr.core.path"> + <pathelement location="${library.starr.jar}"/> + <pathelement location="${reflect.starr.jar}"/> + <pathelement location="${compiler.starr.jar}"/> + </path></else> + </if> - <property name="maven-deps-done" value="yep!"/> + <property name="maven-deps-done" value="yep!"/> </then></if> @@ -226,30 +265,34 @@ targets exercised: - `version.suffix == maven.version.suffix == ""` --> <if><not><equals arg1="${version.bnum}" arg2="0"/></not><then> - <property name="version.suffix" value="-${version.bnum}"/> + <property name="version.suffix" value="-${version.bnum}"/> </then></if> <if><or><not><isset property="version.suffix"/></not><equals arg1="${version.suffix}" arg2=""/></or><then> <if><isset property="build.release"/><then> - <property name="maven.version.suffix" value=""/> - <property name="version.suffix" value="${maven.version.suffix}"/> + <property name="maven.version.suffix" value=""/> + <property name="version.suffix" value="${maven.version.suffix}"/> <if><equals arg1="${maven.version.suffix}" arg2=""/><then> <property name="osgi.version.suffix" value="-VFINAL"/></then> <else> <property name="osgi.version.suffix" value="${maven.version.suffix}"/></else></if></then></if></then> <else> <!-- version.suffix set and not empty --> - <property name="maven.version.suffix" value="${version.suffix}"/> - <property name="osgi.version.suffix" value="${version.suffix}"/></else></if> + <property name="maven.version.suffix" value="${version.suffix}"/> + <property name="osgi.version.suffix" value="${version.suffix}"/></else></if> + + <!-- if a maven version suffix was set (or inferred), assume we're building a release --> + <if><isset property="maven.version.suffix"/><then> + <property name="build.release" value="1"/></then></if> <!-- not building a release and no version.suffix specified --> - <property name="maven.version.suffix" value="-SNAPSHOT"/> + <property name="maven.version.suffix" value="-SNAPSHOT"/> <if><equals arg1="${maven.version.suffix}" arg2="-SNAPSHOT"/><then> - <property name="osgi.version.suffix" value=""/> - <property name="version.suffix" value=""/></then> + <property name="osgi.version.suffix" value=""/> + <property name="version.suffix" value=""/></then> <else> - <property name="osgi.version.suffix" value="${maven.version.suffix}"/> - <property name="version.suffix" value="${maven.version.suffix}"/></else></if> + <property name="osgi.version.suffix" value="${maven.version.suffix}"/> + <property name="version.suffix" value="${maven.version.suffix}"/></else></if> <exec osfamily="unix" executable="tools/get-scala-commit-sha" outputproperty="git.commit.sha" failifexecutionfails="false" /> @@ -266,8 +309,8 @@ targets exercised: </exec> <!-- some default in case something went wrong getting the revision --> - <property name="git.commit.sha" value="unknown"/> - <property name="git.commit.date" value="unknown"/> + <property name="git.commit.sha" value="unknown"/> + <property name="git.commit.date" value="unknown"/> <!-- We use the git describe to determine the OSGi modifier for our build. --> <property name="maven.version.number" @@ -276,9 +319,9 @@ targets exercised: value="${version.major}.${version.minor}.${version.patch}.v${git.commit.date}${osgi.version.suffix}-${git.commit.sha}"/> <if><isset property="build.release"/><then> - <property name="version.number" value="${maven.version.number}"/> + <property name="version.number" value="${maven.version.number}"/> </then><else> - <property name="version.number" value="${version.major}.${version.minor}.${version.patch}${version.suffix}-${git.commit.date}-${git.commit.sha}"/> + <property name="version.number" value="${version.major}.${version.minor}.${version.patch}${version.suffix}-${git.commit.date}-${git.commit.sha}"/> </else></if> <condition property="has.java6"> @@ -304,27 +347,27 @@ targets exercised: </then></if> <!-- Allow this to be overridden simply --> - <property name="sbt.latest.version" value="0.12.2"/> + <property name="sbt.latest.version" value="0.12.2"/> - <property name="sbt.src.dir" value="${build-sbt.dir}/${sbt.latest.version}/src"/> - <property name="sbt.lib.dir" value="${build-sbt.dir}/${sbt.latest.version}/lib"/> + <property name="sbt.src.dir" value="${build-sbt.dir}/${sbt.latest.version}/src"/> + <property name="sbt.lib.dir" value="${build-sbt.dir}/${sbt.latest.version}/lib"/> - <property name="sbt.interface.jar" value="${sbt.lib.dir}/interface.jar"/> - <property name="sbt.interface.url" value="http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-sbt/interface/${sbt.latest.version}/jars/interface.jar"/> + <property name="sbt.interface.jar" value="${sbt.lib.dir}/interface.jar"/> + <property name="sbt.interface.url" value="http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-sbt/interface/${sbt.latest.version}/jars/interface.jar"/> <property name="sbt.interface.src.jar" value="${sbt.src.dir}/compiler-interface-src.jar"/> <property name="sbt.interface.src.url" value="http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-sbt/compiler-interface/${sbt.latest.version}/jars/compiler-interface-src.jar"/> <!-- Additional command line arguments for scalac. They are added to all build targets --> - <property name="scalac.args" value=""/> - <property name="javac.args" value=""/> + <property name="scalac.args" value=""/> + <property name="javac.args" value=""/> - <property name="scalac.args.always" value="" /> - <property name="scalac.args.optimise" value=""/> <!-- scalac.args.optimise is selectively overridden in certain antcall tasks. --> - <property name="scalac.args.all" value="${scalac.args.always} ${scalac.args} ${scalac.args.optimise}"/> - <property name="scalac.args.locker" value="${scalac.args.all}"/> - <property name="scalac.args.quick" value="${scalac.args.all}"/> - <property name="scalac.args.strap" value="${scalac.args.quick}"/> + <property name="scalac.args.always" value="" /> + <property name="scalac.args.optimise" value=""/> <!-- scalac.args.optimise is selectively overridden in certain antcall tasks. --> + <property name="scalac.args.all" value="${scalac.args.always} ${scalac.args} ${scalac.args.optimise}"/> + <property name="scalac.args.locker" value="${scalac.args.all}"/> + <property name="scalac.args.quick" value="${scalac.args.all}"/> + <property name="scalac.args.strap" value="${scalac.args.quick}"/> <!-- This is the start time for the distribution --> <tstamp prefix="time"> @@ -333,8 +376,8 @@ targets exercised: </tstamp> <!-- some default in case something went wrong getting the revision --> - <property name="version.number" value="-unknown-"/> - <property name="init.avail" value="yes"/> + <property name="version.number" value="-unknown-"/> + <property name="init.avail" value="yes"/> <!-- Local libs (developer use.) --> <mkdir dir="${lib-extra.dir}"/> @@ -347,7 +390,6 @@ targets exercised: <fileset dir="${lib-extra.dir}"> <include name="**/*.jar"/> </fileset> - <path refid="dependency.classpath"/> </path> <!-- And print-out what we are building --> @@ -387,7 +429,9 @@ targets exercised: <fail unless="version.suffixes.consistent" message="Version suffixes inconsistent!"/> <path id="forkjoin.classpath" path="${build-libs.dir}/classes/forkjoin"/> - <path id="asm.classpath" path="${build-asm.dir}/classes/"/> + <path id="asm.classpath" path="${build-asm.dir}/classes"/> + <property name="forkjoin-classes" refid="forkjoin.classpath"/> + <property name="asm-classes" refid="asm.classpath"/> <!-- Compilers to use for the various stages. @@ -399,24 +443,10 @@ targets exercised: <path refid="aux.libs"/> </path> - <!-- Download STARR via maven if `starr.version` is specified. - Want to slow down STARR changes, using only released versions. --> - <if><isset property="starr.version"/><then> - <property name="strr" refid="starr.compiler.path"/> - <echo message="Using Scala ${starr.version} for STARR."/> - <!-- <echo message="STARR classpath: ${strr}"/> --> - </then><else> - <path id="starr.core.path"> - <pathelement location="${library.starr.jar}"/> - <pathelement location="${reflect.starr.jar}"/> - <pathelement location="${compiler.starr.jar}"/> - </path> - </else></if> - - <!-- Skip locker with -Dlocker.skip=YESSIR. Uses STARR instead. --> + <!-- To skip locker, use -Dlocker.skip=1 --> <if><isset property="locker.skip"/><then> - <echo message="Skipping locker! Using STARR instead."/> - <path id="locker.compiler.path"><path refid="starr.compiler.path"/></path> + <echo message="Using STARR to build the quick stage (skipping locker)."/> + <path id="locker.compiler.path" refid="starr.compiler.path"/> <property name="locker.locked" value="locker skipped"/></then> <else> <path id="locker.compiler.path"><path refid="locker.compiler.build.path"/></path></else></if> @@ -440,6 +470,11 @@ targets exercised: <path refid="aux.libs"/> </path> + <path id="locker.actors.build.path"> + <path refid="locker.library.build.path"/> + <pathelement location="${build-locker.dir}/classes/actors"/> + </path> + <path id="locker.reflect.build.path"> <path refid="locker.library.build.path"/> <pathelement location="${build-locker.dir}/classes/reflect"/> @@ -458,7 +493,10 @@ targets exercised: <path refid="aux.libs"/> </path> - <path id="quick.actors.build.path"><path refid="quick.library.build.path"/></path> + <path id="quick.actors.build.path"> + <path refid="quick.library.build.path"/> + <pathelement location="${build-quick.dir}/classes/actors"/> + </path> <path id="quick.reflect.build.path"> <path refid="quick.library.build.path"/> @@ -479,6 +517,7 @@ targets exercised: <path id="quick.swing.build.path"> <path refid="quick.library.build.path"/> + <path refid="quick.actors.build.path"/> <pathelement location="${build-quick.dir}/classes/swing"/> </path> @@ -489,6 +528,7 @@ targets exercised: <path id="quick.scalacheck.build.path"> <pathelement location="${build-quick.dir}/classes/library"/> + <pathelement location="${build-quick.dir}/classes/actors"/> <pathelement location="${build-quick.dir}/classes/scalacheck"/> </path> @@ -500,6 +540,7 @@ targets exercised: <path id="quick.partest.build.path"> <path refid="quick.scalap.build.path"/> + <path refid="partest.extras.classpath"/> <pathelement location="${build-quick.dir}/classes/repl"/> <pathelement location="${scalacheck.jar}"/> </path> @@ -518,11 +559,13 @@ targets exercised: <path id="quick.bin.tool.path"> <path refid="quick.repl.build.path"/> + <path refid="quick.actors.build.path"/> <pathelement location="${build-quick.dir}/classes/scalap"/> <pathelement location="${build-quick.dir}/classes/continuations-library"/> </path> <!-- PACK --> + <!-- also used for docs.* targets TODO: use separate paths for those --> <path id="pack.compiler.path"> <pathelement location="${build-pack.dir}/lib/scala-library.jar"/> <pathelement location="${build-pack.dir}/lib/scala-reflect.jar"/> @@ -531,6 +574,7 @@ targets exercised: <pathelement location="${build-pack.dir}/lib/scalap.jar"/> <pathelement location="${build-pack.dir}/lib/scala-actors.jar"/> <pathelement location="${ant.jar}"/> + <path refid="partest.extras.classpath"/> <path refid="aux.libs"/> </path> @@ -543,6 +587,42 @@ targets exercised: <path refid="aux.libs"/> </path> + <path id="pack.library.files"> + <fileset dir="${build-quick.dir}/classes/library"/> + <fileset dir="${build-quick.dir}/classes/continuations-library"/> + <fileset dir="${forkjoin-classes}"/> + </path> + + <path id="pack.actors.files"> + <fileset dir="${build-quick.dir}/classes/actors"/> + </path> + + <path id="pack.compiler.files"> + <fileset dir="${build-quick.dir}/classes/compiler"/> + <fileset dir="${build-quick.dir}/classes/repl"/> + <fileset dir="${build-quick.dir}/classes/scaladoc"/> + <fileset dir="${build-quick.dir}/classes/interactive"/> + <fileset dir="${asm-classes}"/> + </path> + + <path id="pack.swing.files"> <fileset dir="${build-quick.dir}/classes/swing"/> </path> + <path id="pack.reflect.files"> <fileset dir="${build-quick.dir}/classes/reflect"/> </path> + <path id="pack.plugins.files"> <fileset dir="${build-quick.dir}/classes/continuations-plugin"/> </path> + <path id="pack.scalacheck.files"> <fileset dir="${build-quick.dir}/classes/scalacheck"/> </path> + <path id="pack.scalap.files"> <fileset dir="${build-quick.dir}/classes/scalap"/> + <fileset file="${src.dir}/scalap/decoder.properties"/> </path> + + <path id="pack.partest.files"> + <fileset dir="${build-quick.dir}/classes/partest"> + <exclude name="scala/tools/partest/javaagent/**"/> + </fileset> + </path> + + <path id="pack.partest-javaagent.files"> + <fileset dir="${build-quick.dir}/classes/partest"> + <include name="scala/tools/partest/javaagent/**"/> + </fileset> + </path> <!-- STRAP --> <path id="strap.library.build.path"> @@ -550,7 +630,6 @@ targets exercised: <path refid="forkjoin.classpath"/> <path refid="aux.libs"/> </path> - <path id="strap.actors.build.path"><path refid="strap.library.build.path"/></path> <path id="strap.reflect.build.path"> <path refid="strap.library.build.path"/> @@ -579,9 +658,18 @@ targets exercised: <path id="partest.classpath"> <path refid="pack.compiler.path"/> + <path refid="partest.extras.classpath"/> + </path> + + <!-- obsolete? --> + <!-- TODO - segregate swing tests (there can't be many) --> + <!-- + <path id="partest.build.path"> + <path refid="pack.compiler.path"/> <fileset dir="${partest.dir}/files/lib" includes="*.jar" /> - <pathelement location="${pack.dir}/lib/scala-swing.jar"/> <!-- TODO - segregate swing tests (there can't be many) --> + <pathelement location="${pack.dir}/lib/scala-swing.jar"/> </path> + --> <path id="test.osgi.compiler.build.path"> <pathelement location="${test.osgi.classes}"/> @@ -660,53 +748,39 @@ targets exercised: <delete dir="${build-@{build}.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/> </sequential> </macrodef> - <!-- =========================================================================== - LOCAL DEPENDENCY (Adapted ASM) + LOCAL DEPENDENCIES ============================================================================ --> - <target name="asm.done" depends="init"> - <available file="${build-asm.dir}/asm.complete" property="asm.available"/> - <if><not><isset property="asm.available"/></not><then> - <stopwatch name="asm.lib.timer"/> - <mkdir dir="${build-asm.dir}/classes/"/> - <javac - srcdir="${src.dir}/asm" - destdir="${build-asm.dir}/classes" - classpath="${build-asm.dir}/classes" - includes="**/*.java" - target="1.6" source="1.5"> - <compilerarg line="${javac.args} -XDignore.symbol.file"/> - </javac> - <stopwatch name="asm.lib.timer" action="total"/> - <touch file="${build-asm.dir}/asm.complete" verbose="no"/> - </then></if> - </target> + <macrodef name="simple-javac" > + <attribute name="project"/> <!-- project: asm/forkjoin --> + <attribute name="args" default=""/> + <attribute name="jar" default="yes"/> + <sequential> + <uptodate property="@{project}.available" targetfile="${build-libs.dir}/@{project}.complete"> + <srcfiles dir="${src.dir}/@{project}"/></uptodate> + <if><not><isset property="@{project}.available"/></not><then> + <stopwatch name="@{project}.timer"/> + <mkdir dir="${@{project}-classes}"/> + <javac + srcdir="${src.dir}/@{project}" + destdir="${@{project}-classes}" + classpath="${@{project}-classes}" + includes="**/*.java" + target="1.6" source="1.5" + compiler="javac1.6"> + <compilerarg line="${javac.args} @{args}"/> + </javac> + <if><equals arg1="@{jar}" arg2="yes"/><then> + <jar whenmanifestonly="fail" destfile="${build-libs.dir}/@{project}.jar" basedir="${@{project}-classes}"/></then></if> + <stopwatch name="@{project}.timer" action="total"/> + <mkdir dir="${build-libs.dir}"/> + <touch file="${build-libs.dir}/@{project}.complete" verbose="no"/> + </then></if> + </sequential> + </macrodef> -<!-- =========================================================================== - LOCAL DEPENDENCY (FORKJOIN) -============================================================================ --> - <target name="forkjoin.done" depends="init"> - <uptodate property="forkjoin.available" targetfile="${build-libs.dir}/forkjoin.complete"> - <srcfiles dir="${src.dir}/forkjoin"/></uptodate> - <if><not><isset property="forkjoin.available"/></not><then> - <stopwatch name="forkjoin.lib.timer"/> - <mkdir dir="${build-libs.dir}/classes/forkjoin"/> - <javac - fork="yes" - compiler="javac1.6" - srcdir="${src.dir}/forkjoin" - destdir="${build-libs.dir}/classes/forkjoin" - classpath="${build-libs.dir}/classes/forkjoin" - includes="**/*.java" - debug="true" - target="1.6" source="1.5"> - <compilerarg line="${javac.args} -XDignore.symbol.file"/> - </javac> - <jar whenmanifestonly="fail" destfile="${build-libs.dir}/forkjoin.jar" basedir="${build-libs.dir}/classes/forkjoin"/> - <stopwatch name="forkjoin.lib.timer" action="total"/> - <touch file="${build-libs.dir}/forkjoin.complete" verbose="no"/> - </then></if> - </target> + <target name="asm.done" depends="init"> <simple-javac project="asm" jar="no"/> </target> + <target name="forkjoin.done" depends="init"> <simple-javac project="forkjoin" args="-XDignore.symbol.file"/></target> <!-- =========================================================================== STAGED COMPILATION MACROS @@ -731,6 +805,49 @@ targets exercised: </sequential> </macrodef> + <!-- Zinc assumes a one-to-one correspondence of output folder to set of source files. + When compiling different sets of source files in multiple compilations to the same output directory, + Zinc thinks source files that appeared in an earlier compilation but are absent in the current one, + were deleted and thus deletes the corresponding output files. + + Note that zinc also requires each arg to scalac to be prefixed by -S. + --> + <macrodef name="zinc"> + <attribute name="compilerpathref" /> + <attribute name="destdir" /> + <attribute name="srcdir" /> + <attribute name="srcpath" default="NOT SET"/> <!-- needed to compile the library, "NOT SET" is just a convention to denote an optional attribute --> + <attribute name="buildpathref" /> + <attribute name="params" default="" /> + <attribute name="java-excludes" default=""/> + + <sequential> + <local name="sources"/> + <pathconvert pathsep=" " property="sources"> + <fileset dir="@{srcdir}"> + <include name="**/*.java"/> + <include name="**/*.scala"/> + <exclude name="@{java-excludes}"/> + </fileset> + </pathconvert> + <local name="args"/> + <local name="sargs"/> + <if><not><equals arg1="@{srcpath}" arg2="NOT SET"/></not><then> + <property name="args" value="@{params} -sourcepath @{srcpath}"/> + </then></if> + <property name="args" value="@{params}"/> <!-- default --> + + <!-- HACK: prefix scalac args by -S --> + <script language="javascript"> + project.setProperty("sargs", project.getProperty("args").trim().replaceAll(" ", " -S")); + </script> + + <exec osfamily="unix" executable="tools/zinc" failifexecutionfails="true" failonerror="true"> + <arg line="-nailed -compile-order JavaThenScala -scala-path ${ant.refid:@{compilerpathref}} -d @{destdir} -classpath ${toString:@{buildpathref}} ${sargs} ${sources}"/> + </exec> + </sequential> + </macrodef> + <macrodef name="staged-scalac" > <attribute name="with"/> <!-- will use path `@{with}.compiler.path` to locate scalac --> <attribute name="stage"/> <!-- current stage (locker, quick, strap) --> @@ -739,33 +856,46 @@ targets exercised: <attribute name="args" default=""/> <!-- additional args --> <attribute name="destproject" default="@{project}"/> <!-- overrides the output directory; used when building multiple projects into the same directory--> <attribute name="srcdir" default="@{project}"/> - <element name="args" implicit="true" optional="true"/> + <attribute name="java-excludes" default=""/> <sequential> + <!-- TODO: detect zinc anywhere on PATH + use zinc for the quick stage if it's available; + would use it for locker but something is iffy in sbt: get a class cast error on global phase --> + <if><and> <available file="tools/zinc"/> + <equals arg1="@{stage}" arg2="quick"/> + <not><equals arg1="@{project}" arg2="plugins"/></not> <!-- doesn't work in zinc because it requires the quick compiler, which isn't jarred up--> + </and><then> + <zinc taskname="Z.@{stage}.@{project}" + compilerpathref="@{with}.compiler.path" + destdir="${build-@{stage}.dir}/classes/@{destproject}" + srcdir="${src.dir}/@{srcdir}" + srcpath="@{srcpath}" + buildpathref="@{stage}.@{project}.build.path" + params="${scalac.args.@{stage}} @{args}" + java-excludes="@{java-excludes}"/></then> + <else> <if><equals arg1="@{srcpath}" arg2="NOT SET"/><then> <scalacfork taskname="@{stage}.@{project}" - destdir="${build-@{stage}.dir}/classes/@{destproject}" + jvmargs="${scalacfork.jvmargs}" compilerpathref="@{with}.compiler.path" - params="${scalac.args.@{stage}} @{args}" + destdir="${build-@{stage}.dir}/classes/@{destproject}" srcdir="${src.dir}/@{srcdir}" - jvmargs="${scalacfork.jvmargs}"> + params="${scalac.args.@{stage}} @{args}"> <include name="**/*.scala"/> - <compilationpath refid="@{stage}.@{project}.build.path"/> - <args/> - </scalacfork></then> + <compilationpath refid="@{stage}.@{project}.build.path"/></scalacfork></then> <else> <scalacfork taskname="@{stage}.@{project}" - destdir="${build-@{stage}.dir}/classes/@{destproject}" + jvmargs="${scalacfork.jvmargs}" compilerpathref="@{with}.compiler.path" - srcpath="@{srcpath}" - params="${scalac.args.@{stage}} @{args}" + destdir="${build-@{stage}.dir}/classes/@{destproject}" srcdir="${src.dir}/@{srcdir}" - jvmargs="${scalacfork.jvmargs}"> + srcpath="@{srcpath}" + params="${scalac.args.@{stage}} @{args}"> <include name="**/*.scala"/> - <compilationpath refid="@{stage}.@{project}.build.path"/> - <args/> - </scalacfork></else> + <compilationpath refid="@{stage}.@{project}.build.path"/></scalacfork></else> </if> + </else></if> </sequential> </macrodef> @@ -793,20 +923,17 @@ targets exercised: <attribute name="srcpath" default="NOT SET"/> <!-- needed to compile the library --> <attribute name="args" default=""/> <!-- additional args --> <attribute name="includes" default="comp.includes"/> + <attribute name="java-excludes" default=""/> <attribute name="version" default=""/> <!-- non-empty for partest and scaladoc: use @{version}.version.number in property file--> - <element name="pre" optional="true"/> - <element name="post" optional="true"/> - <sequential> <staged-uptodate stage="@{stage}" project="@{project}"> <check><srcfiles dir="${src.dir}/@{project}"/></check> <do> <stopwatch name="@{stage}.@{project}.timer"/> <mkdir dir="${build-@{stage}.dir}/classes/@{project}"/> - <pre/> - <staged-scalac with="@{with}" stage="@{stage}" project="@{project}" srcpath="@{srcpath}" args="@{args}"/> - <post/> + <staged-javac stage="@{stage}" project="@{project}" excludes="@{java-excludes}"/> <!-- always compile with javac for simplicity and regularity; it's cheap --> + <staged-scalac with="@{with}" stage="@{stage}" project="@{project}" srcpath="@{srcpath}" args="@{args}" java-excludes="@{java-excludes}"/> <if><equals arg1="@{version}" arg2=""/><then> <propertyfile file = "${build-@{stage}.dir}/classes/@{project}/@{project}.properties"> <entry key = "version.number" value="${version.number}"/> @@ -871,15 +998,33 @@ targets exercised: <attribute name="project"/> <attribute name="targetdir" default="lib"/> <attribute name="targetjar" default="scala-@{project}.jar"/> - <element name="do" implicit="true"/> + <attribute name="destfile" default="${build-pack.dir}/@{targetdir}/@{targetjar}"/> + <attribute name="manifest" default=""/> + <element name="pre" optional="true"/> + <element name="jar-opts" optional="true"/> <sequential> - <uptodate property="pack.@{project}.available" - targetfile="${build-pack.dir}/@{targetdir}/@{targetjar}" - srcfile="${build-quick.dir}/@{project}.complete"/> + <uptodate property="pack.@{project}.available" targetfile="@{destfile}"> + <srcresources> + <resources refid="pack.@{project}.files"/> + <!-- <path><pathelement location="${build-quick.dir}/@{project}.complete"/></path> --> + </srcresources> + </uptodate> <if><not><isset property="pack.@{project}.available"/></not><then> <mkdir dir="${build-pack.dir}/@{targetdir}"/> - <do/> + <pre/> + + <if><not><equals arg1="@{manifest}" arg2=""/></not><then> + <jar whenmanifestonly="fail" destfile="@{destfile}" manifest="@{manifest}"> <!-- update="true" makes no difference on my machine, so starting from scratch--> + <jar-opts/> + <path refid="pack.@{project}.files"/> + </jar></then> + <else> + <jar whenmanifestonly="fail" destfile="@{destfile}"> + <jar-opts/> + <path refid="pack.@{project}.files"/> + </jar> + </else></if> </then></if> </sequential> </macrodef> @@ -897,7 +1042,8 @@ targets exercised: <do> <stopwatch name="docs.@{project}.timer"/> <mkdir dir="${build-docs.dir}/@{project}"/> - <if><equals arg1="@{classpathref}" arg2="NOT SET"/><then> + <if><equals arg1="@{docroot}" arg2="NOT SET"/><then> + <!-- TODO: introduce docs.@{project}.build.path for classpathref --> <scaladoc destdir="${build-docs.dir}/@{project}" doctitle="@{title}" @@ -940,16 +1086,20 @@ targets exercised: <!-- =========================================================================== LOCAL REFERENCE BUILD (LOCKER) ============================================================================ --> - <target name="locker.start" depends="asm.done, forkjoin.done"> + <target name="locker.start" depends="asm.done, forkjoin.done"> <condition property="locker.locked"><available file="${build-locker.dir}/locker.locked"/></condition></target> - <target name="locker.lib" depends="locker.start" unless="locker.locked"> - <staged-build with="starr" stage="locker" project="library" srcpath="${src.dir}/library" includes="lib.includes"> - <pre><staged-javac stage="locker" project="library" args="-XDignore.symbol.file"/></pre></staged-build></target> - <!-- TODO: args="-XDignore.symbol.file" necessary?? --> + <target name="locker.lib" depends="locker.start" unless="locker.locked"> + <staged-build with="starr" stage="locker" project="library" srcpath="${src.dir}/library" includes="lib.includes"/></target> + + <target name="locker.actors" depends="locker.lib" unless="locker.locked"> + <staged-build with="starr" stage="locker" project="actors"/> </target> - <target name="locker.reflect" depends="locker.lib" unless="locker.locked"> <staged-build with="starr" stage="locker" project="reflect"/></target> - <target name="locker.comp" depends="locker.reflect" unless="locker.locked"> <staged-build with="starr" stage="locker" project="compiler"/></target> + <target name="locker.reflect" depends="locker.lib" unless="locker.locked"> + <staged-build with="starr" stage="locker" project="reflect"/></target> + + <target name="locker.comp" depends="locker.reflect" unless="locker.locked"> + <staged-build with="starr" stage="locker" project="compiler"/></target> <target name="locker.done" depends="locker.comp"> <mkdir dir="${build-locker.dir}"/> @@ -961,62 +1111,43 @@ targets exercised: <!-- =========================================================================== QUICK BUILD (QUICK) ============================================================================ --> - <target name="quick.start" depends="locker.done"/> + <target name="quick.start" depends="locker.done"/> - <target name="quick.lib" depends="quick.start"> - <staged-build with="locker" stage="quick" project="library" srcpath="${src.dir}/library" includes="lib.rootdoc.includes"> - <pre> - <staged-javac stage="quick" project="library" args="-XDignore.symbol.file"/> <!-- TODO: args="-XDignore.symbol.file" necessary?? --> - <staged-javac stage="quick" project="actors" destproject="library"/> - </pre> - <post> - <staged-scalac with="locker" stage="quick" project="actors" destproject="library"/> - </post> - </staged-build> - </target> + <target name="quick.lib" depends="quick.start"> + <staged-build with="locker" stage="quick" project="library" srcpath="${src.dir}/library" includes="lib.rootdoc.includes"/></target> - <target name="quick.reflect" depends="quick.lib"> <staged-build with="locker" stage="quick" project="reflect"/> </target> + <target name="quick.actors" depends="quick.lib"> + <staged-build with="locker" stage="quick" project="actors"/> </target> - <target name="quick.comp" depends="quick.reflect"> - <staged-build with="locker" stage="quick" project="compiler"/> - </target> + <target name="quick.reflect" depends="quick.lib"> + <staged-build with="locker" stage="quick" project="reflect"/> </target> - <target name="quick.repl" depends="quick.comp"> - <staged-build with="locker" stage="quick" project="repl"> - <pre> - <staged-javac stage="quick" project="repl"/> - </pre> - </staged-build> - </target> + <target name="quick.comp" depends="quick.reflect"> + <staged-build with="locker" stage="quick" project="compiler"/> </target> - <target name="quick.scalacheck" depends="quick.lib"> - <staged-build with="locker" stage="quick" project="scalacheck" args="-nowarn"/> - </target> + <target name="quick.repl" depends="quick.comp"> + <staged-build with="locker" stage="quick" project="repl"/> </target> - <target name="quick.scalap" depends="quick.repl"> - <staged-build with="locker" stage="quick" project="scalap"/> - </target> + <target name="quick.scalacheck" depends="quick.actors, quick.lib"> + <staged-build with="locker" stage="quick" project="scalacheck" args="-nowarn"/> </target> - <target name="quick.partest" depends="quick.scalap, quick.repl, asm.done"> - <staged-build with="locker" stage="quick" project="partest" version="partest"> - <pre><staged-javac stage="quick" project="partest"/></pre> - </staged-build> - </target> + <target name="quick.scalap" depends="quick.repl"> + <staged-build with="locker" stage="quick" project="scalap"/> </target> + + <target name="quick.partest" depends="quick.scalap, quick.repl, asm.done"> + <staged-build with="locker" stage="quick" project="partest" version="partest"/> </target> <target name="quick.scaladoc" depends="quick.comp, quick.partest"> - <staged-build with="locker" stage="quick" project="scaladoc" version="scaladoc"/> - </target> + <staged-build with="locker" stage="quick" project="scaladoc" version="scaladoc"/> </target> <target name="quick.interactive" depends="quick.comp, quick.scaladoc"> - <staged-build with="locker" stage="quick" project="interactive"/> - </target> + <staged-build with="locker" stage="quick" project="interactive"/> </target> - <target name="quick.swing" depends="quick.lib" if="has.java6"> - <staged-build with="locker" stage="quick" project="swing"/> - </target> + <target name="quick.swing" depends="quick.actors, quick.lib" if="has.java6"> + <staged-build with="locker" stage="quick" project="swing"/> </target> - <target name="quick.plugins" depends="quick.comp"> - <staged-uptodate stage="quick" project="plugins"> + <target name="quick.plugins" depends="quick.comp"> + <staged-uptodate stage="quick" project="plugins"> <check><srcfiles dir="${src.dir}/continuations"/></check> <do> <stopwatch name="quick.plugins.timer"/> @@ -1033,158 +1164,99 @@ targets exercised: <!-- might split off library part into its own ant target --> <mkdir dir="${build-quick.dir}/classes/continuations-library"/> - <!-- have to compile with quick compiler here! --> + <!-- TODO: must build with quick to avoid + [quick.plugins] error: java.lang.NoClassDefFoundError: scala/tools/nsc/transform/patmat/PatternMatching + [quick.plugins] at scala.tools.selectivecps.SelectiveCPSTransform.newTransformer(SelectiveCPSTransform.scala:29) + + WHY OH WHY!? scala/tools/nsc/transform/patmat/PatternMatching should be on locker.compiler.path + --> <staged-scalac with="quick" stage="quick" project="plugins" srcdir="continuations/library" destproject="continuations-library" - args="-Xplugin-require:continuations -P:continuations:enable"> - <compilerarg value="-Xpluginsdir"/> - <compilerarg file="${build-quick.dir}/misc/scala-devel/plugins"/> - </staged-scalac> + args="-Xplugin-require:continuations -P:continuations:enable -Xpluginsdir ${build-quick.dir}/misc/scala-devel/plugins"/> <stopwatch name="quick.plugins.timer" action="total"/> </do> </staged-uptodate> </target> - <target name="quick.bin" depends="init"> + <target name="quick.bin" depends="quick.lib, quick.reflect, quick.comp, quick.repl, quick.scalacheck, quick.scalap, quick.interactive, quick.swing, quick.plugins, quick.partest, quick.scaladoc"> <staged-bin stage="quick" classpathref="quick.bin.tool.path"/> </target> - <target name="quick.done" depends="quick.lib, quick.reflect, quick.comp, quick.repl, quick.scalacheck, quick.scalap, quick.interactive, quick.swing, quick.plugins, quick.partest, quick.scaladoc, quick.bin"/> + <target name="quick.done" depends="quick.bin"/> <target name="quick-opt" description="Optimized version of quick.done."> <optimized name="quick.done"/></target> <!-- =========================================================================== PACKED QUICK BUILD (PACK) ============================================================================ --> - <target name="pack.lib" depends="quick.lib, quick.plugins, forkjoin.done"> - <staged-pack project="library"> - <!-- First copy maven dependencies --> - <copy todir="${build-pack.dir}/lib"> - <fileset refid="dependency.fileset" /> - <mapper type="flatten" /> - </copy> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scala-library.jar"> - <fileset dir="${build-quick.dir}/classes/library"> - <exclude name="scala/swing/**"/> - <exclude name="scala/actors/**"/> - </fileset> - <fileset dir="${build-quick.dir}/classes/continuations-library"/> - <fileset dir="${build-libs.dir}/classes/forkjoin"/> - </jar> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scala-actors.jar"> - <fileset dir="${build-quick.dir}/classes/library"> - <include name="scala/actors/**"/> - </fileset> - </jar> - </staged-pack> - </target> - - <target name="pack.swing" depends="quick.swing" if="has.java6"> - <staged-pack project="swing’"> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scala-swing.jar" basedir="${build-quick.dir}/classes/swing"/> - </staged-pack> - </target> - - <target name="pack.reflect" depends="quick.reflect"> - <staged-pack project="reflect’"> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scala-reflect.jar" basedir="${build-quick.dir}/classes/reflect"/> - </staged-pack> - </target> - - <target name="pack.comp" depends="quick.comp, quick.scaladoc, quick.interactive, quick.repl, asm.done"> - <staged-pack project="compiler"> - <mkdir dir="${build-pack.dir}/META-INF"/> - <copy file="META-INF/MANIFEST.MF" toDir="${build-pack.dir}/META-INF"/> - <manifest file="${build-pack.dir}/META-INF/MANIFEST.MF" mode="update"> - <attribute name="Bundle-Version" value="${version.number}"/> - <attribute name="Class-Path" value="scala-reflect.jar scala-library.jar"/> - </manifest> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scala-compiler.jar" manifest="${build-pack.dir}/META-INF/MANIFEST.MF"> + <target name="pack.lib" depends="quick.lib, quick.plugins, forkjoin.done"> + <staged-pack project="library"/></target> + + <target name="pack.actors" depends="quick.lib"> <staged-pack project="actors"/> </target> + <target name="pack.swing" if="has.java6" depends="quick.swing"> <staged-pack project="swing"/> </target> + <target name="pack.reflect" depends="quick.reflect"> <staged-pack project="reflect"/> </target> + + <target name="pack.comp" depends="quick.comp, quick.scaladoc, quick.interactive, quick.repl, asm.done"> + <staged-pack project="compiler" manifest="${build-pack.dir}/META-INF/MANIFEST.MF"> + <pre> <!-- TODO the files copied here do not influence actuality of this target (nor does the manifest) --> + <copy file="${jline.jar}" toDir="${build-pack.dir}/lib"/> + <copy todir="${build-pack.dir}/lib"> + <fileset dir="${lib-extra.dir}"> + <include name="**/*.jar"/> + </fileset> + </copy> + <mkdir dir="${build-pack.dir}/META-INF"/> + <copy file="${basedir}/META-INF/MANIFEST.MF" toDir="${build-pack.dir}/META-INF"/> + <manifest file="${build-pack.dir}/META-INF/MANIFEST.MF" mode="update"> + <attribute name="Bundle-Version" value="${version.number}"/> + <attribute name="Class-Path" value="scala-reflect.jar scala-library.jar"/> + </manifest> + </pre> + <jar-opts> <service type="javax.script.ScriptEngineFactory" provider="scala.tools.nsc.interpreter.IMain$Factory"/> - <fileset dir="${build-quick.dir}/classes/compiler"/> - <fileset dir="${build-quick.dir}/classes/scaladoc"/> - <fileset dir="${build-quick.dir}/classes/interactive"/> - <fileset dir="${build-quick.dir}/classes/repl"/> - <fileset dir="${build-asm.dir}/classes"/> - </jar> - <copy file="${jline.jar}" toDir="${build-pack.dir}/lib"/> - <copy todir="${build-pack.dir}/lib"> - <fileset dir="${lib-extra.dir}"> - <include name="**/*.jar"/> - </fileset> - </copy> + </jar-opts> </staged-pack> </target> - <target name="pack.plugins" depends="quick.plugins"> - <staged-pack project="plugins" targetdir="misc/scala-devel/plugins" targetjar="continuations.jar"> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/misc/scala-devel/plugins/continuations.jar" basedir="${build-quick.dir}/classes/continuations-plugin"/> - </staged-pack> - </target> - - <target name="pack.scalacheck" depends="quick.scalacheck"> - <uptodate property="pack.scalacheck.available" - targetfile="${build-pack.dir}/lib/scalacheck.jar"> - <srcfiles dir="${build-quick.dir}/classes/scalacheck"/></uptodate> - <if><not><isset property="pack.scalacheck.available"/></not><then> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scalacheck.jar" basedir="${build-quick.dir}/classes/scalacheck"/> - </then></if> - </target> + <target name="pack.plugins" depends="quick.plugins"> <staged-pack project="plugins" targetdir="misc/scala-devel/plugins" targetjar="continuations.jar"/> </target> + <target name="pack.scalacheck" depends="quick.scalacheck"> <staged-pack project="scalacheck" targetjar="scalacheck.jar"/> </target> <target name="pack.partest" depends="quick.partest"> - <staged-pack project="partest"> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scala-partest.jar"> - <fileset dir="${build-quick.dir}/classes/partest"> - <exclude name="scala/tools/partest/javaagent/**"/> - </fileset> - </jar> - <jar whenmanifestonly="fail" destfile = "${build-pack.dir}/lib/scala-partest-javaagent.jar" - manifest = "${src.dir}/partest/scala/tools/partest/javaagent/MANIFEST.MF"> - <fileset dir = "${build-quick.dir}/classes/partest"> - <include name = "scala/tools/partest/javaagent/**"/> - </fileset> - </jar> - </staged-pack> + <staged-pack project="partest"/> + <!-- TODO the manifest should influence actuality of this target --> + <staged-pack project="partest-javaagent" manifest="${src.dir}/partest/scala/tools/partest/javaagent/MANIFEST.MF"/> </target> - <target name="pack.scalap" depends="quick.scalap"> - <staged-pack project="plugins" targetjar="scalap.jar"> - <jar whenmanifestonly="fail" destfile="${build-pack.dir}/lib/scalap.jar"> - <fileset dir="${build-quick.dir}/classes/scalap"/> - <fileset file="${src.dir}/scalap/decoder.properties"/> - </jar> - </staged-pack> - </target> + <target name="pack.scalap" depends="quick.scalap"> <staged-pack project="scalap" targetjar="scalap.jar"/> </target> - <target name="pack.bin" depends="pack.comp, pack.lib, pack.partest, pack.plugins, pack.reflect, pack.scalacheck, pack.scalap, pack.swing"> + <target name="pack.bin" depends="pack.comp, pack.lib, pack.actors, pack.partest, pack.plugins, pack.reflect, pack.scalacheck, pack.scalap, pack.swing"> <staged-bin stage="pack"/> </target> - <target name="pack.done" depends="pack.bin"> + <!-- depend on quick.done so quick.bin is run when pack.done is --> + <target name="pack.done" depends="quick.done, pack.bin"> + <!-- copy dependencies to build/pack/lib, it only takes a second so don't bother with uptodate checks --> + <copy todir="${build-pack.dir}/lib"> + <resources refid="partest.extras.fileset"/> + <mapper classpathref="maven-ant-tasks.classpath" classname="org.apache.maven.artifact.ant.VersionMapper" + from="${partest.extras.versions}" to="flatten"/> + </copy> + <taskdef resource="scala/tools/ant/antlib.xml" classpathref="pack.compiler.path"/> - <taskdef resource="scala/tools/partest/antlib.xml" classpathref="pack.compiler.path"/> + <taskdef resource="scala/tools/partest/antlib.xml" classpathref="partest.classpath"/> </target> <!-- =========================================================================== BOOTSTRAPPING BUILD (STRAP) ============================================================================ --> - <target name="strap.lib" depends="quick.done"> - <staged-build with="quick" stage="strap" project="library" srcpath="${src.dir}/library" includes="lib.rootdoc.includes"> - <pre> - <staged-javac stage="strap" project="library" args="-XDignore.symbol.file"/> <!-- TODO: args="-XDignore.symbol.file" necessary?? --> - <staged-javac stage="strap" project="actors" destproject="library"/> - </pre> - <post> - <staged-scalac with="quick" stage="strap" project="actors" destproject="library"/> - </post> - </staged-build> + <target name="strap.done" depends="pack.done"> + <staged-build with="pack" stage="strap" project="library" srcpath="${src.dir}/library" includes="lib.rootdoc.includes"/> + <staged-build with="pack" stage="strap" project="reflect"/> + <staged-build with="pack" stage="strap" project="compiler"/> </target> - <target name="strap.reflect" depends="strap.lib"> <staged-build with="pack" stage="strap" project="reflect"/> </target> - <target name="strap.comp" depends="strap.reflect"> <staged-build with="pack" stage="strap" project="compiler"/> </target> - <target name="strap.done" depends="strap.comp"/> <target name="strap-opt" description="Optimized version of strap.done."> <optimized name="strap.done"/></target> @@ -1195,13 +1267,13 @@ targets exercised: <mkdir dir="${build-palo.dir}/lib"/> <jar whenmanifestonly="fail" destfile="${build-palo.dir}/lib/scala-library.jar"> <fileset dir="${build-locker.dir}/classes/library"/> - <fileset dir="${build-libs.dir}/classes/forkjoin"/> + <fileset dir="${forkjoin-classes}"/> </jar> <jar whenmanifestonly="fail" destfile="${build-palo.dir}/lib/scala-reflect.jar" manifest="${basedir}/META-INF/MANIFEST.MF" basedir="${build-locker.dir}/classes/reflect"/> <jar whenmanifestonly="fail" destfile="${build-palo.dir}/lib/scala-compiler.jar" manifest="${basedir}/META-INF/MANIFEST.MF"> <fileset dir="${build-locker.dir}/classes/compiler"/> - <fileset dir="${build-asm.dir}/classes/"/> + <fileset dir="${asm-classes}"/> </jar> <copy file="${jline.jar}" toDir="${build-palo.dir}/lib"/> </target> @@ -1279,7 +1351,7 @@ targets exercised: TEST SUITE ============================================================================ --> <!-- bootstrapping stability: compare {quick,strap}/(lib|reflect|comp) --> - <target name="test.stability" depends="quick.lib, quick.reflect, quick.comp, strap.lib, strap.reflect, strap.comp"> + <target name="test.stability" depends="strap.done"> <exec osfamily="unix" vmlauncher="false" executable="${basedir}/tools/stability-test.sh" failonerror="true" /> <!-- I think doing it this way means it will auto-pass on windows... that's the idea. If not, something like this. --> <!-- <exec osfamily="windows" executable="foo" failonerror="false" failifexecutionfails="false" /> --> @@ -1361,121 +1433,109 @@ targets exercised: <stopwatch name="quick.sbt-interface.timer" action="total"/> </target> - <property name="partest.srcdir" value="files" /> <!-- TODO: make targets for `pending` and other subdirs --> + <!-- See test/build-partest.xml for the macro(s) being used here. --> - <target name="test.run" depends="pack.done"> - <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java" - timeout="1200000" - srcdir="${partest.srcdir}" - scalacopts="${scalac.args.optimise}"> - - <compilationpath refid="partest.classpath"/> - <runtests dir="${partest.dir}/${partest.srcdir}/run" includes="*.scala"/> - <jvmtests dir="${partest.dir}/${partest.srcdir}/jvm" includes="*.scala"/> - </partest> + <target name="test.suite" depends="pack.done"> + <testSuite/> </target> - <target name="test.suite" depends="pack.done"> - <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java" - timeout="2400000" - srcdir="${partest.srcdir}" - scalacopts="${scalac.args.optimise}"> - <compilationpath refid="partest.classpath"/> - <postests dir="${partest.dir}/${partest.srcdir}/pos" includes="*.scala"/> - <negtests dir="${partest.dir}/${partest.srcdir}/neg" includes="*.scala"/> - <runtests dir="${partest.dir}/${partest.srcdir}/run" includes="*.scala"/> - <jvmtests dir="${partest.dir}/${partest.srcdir}/jvm" includes="*.scala"/> - <residenttests dir="${partest.dir}/${partest.srcdir}/res" includes="*.res"/> - <scalaptests dir="${partest.dir}/${partest.srcdir}/scalap" includes="**/*.scala"/> - <scalachecktests dir="${partest.dir}/${partest.srcdir}/scalacheck"> - <include name="*.scala"/> - </scalachecktests> - <specializedtests dir="${partest.dir}/${partest.srcdir}/specialized"> - <include name="*.scala"/> - </specializedtests> - <instrumentedtests dir="${partest.dir}/${partest.srcdir}/instrumented"> - <include name="*.scala"/> - </instrumentedtests> - </partest> + <target name="test.suite.color" depends="pack.done"> + <testSuite colors="8"/> + </target> + + <target name="test.run" depends="pack.done"> + <testSuite kinds="run jvm"/> </target> <target name="test.continuations.suite" depends="pack.done"> - <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java" - timeout="2400000" - srcdir="${partest.srcdir}" - scalacopts="${scalac.args.optimise} -Xplugin-require:continuations -P:continuations:enable"> - <compilerarg value="-Xpluginsdir"/> - <compilerarg file="${build-quick.dir}/misc/scala-devel/plugins"/> - <compilationpath refid="partest.classpath"/> - <negtests dir="${partest.dir}/${partest.srcdir}/continuations-neg" includes="*.scala"/> - <runtests dir="${partest.dir}/${partest.srcdir}/continuations-run" includes="*.scala"/> - </partest> + <testSuite kinds="continuations-neg continuations-run" + scalacOpts="${scalac.args.optimise} -Xpluginsdir ${build-quick.dir}/misc/scala-devel/plugins -Xplugin-require:continuations -P:continuations:enable" + /> </target> <target name="test.scaladoc" depends="pack.done"> - <partest erroronfailed="yes" scalacopts="${scalac.args.optimise}" showlog="yes"> - <compilationpath refid="partest.classpath"/> - <runtests dir="${partest.dir}/scaladoc/run" includes="*.scala" /> - <scalachecktests dir="${partest.dir}/scaladoc/scalacheck" includes="*.scala" /> - </partest> + <testSuite kinds="run scalacheck" srcdir="scaladoc"/> </target> <target name="test.interactive" depends="pack.done"> - <partest erroronfailed="yes" scalacopts="${scalac.args.optimise}" showlog="yes"> - <compilationpath refid="partest.classpath"/> - <presentationtests dir="${partest.dir}/${partest.srcdir}/presentation"> - <include name="*/*.scala"/> - </presentationtests> - </partest> + <testSuite kinds="presentation"/> </target> -<!-- currently disabled: test.ant, test.bc, test.positions, test.classload --> - <target name="test.done" depends="test.osgi, test.sbt, test.interactive, test.continuations.suite, test.suite, test.scaladoc, test.stability"/> - + <!-- for use in PR validation, where stability is rarely broken, so we're going to use starr for locker, + and skip test.stability (which requires locker == quick) --> + <target name="test.core" depends="test.osgi, test.sbt, test.bc, test.interactive, test.continuations.suite, test.scaladoc, test.suite"/> + <target name="test.done" depends="test.core, test.stability"/> <!-- =========================================================================== BINARY COMPATIBILITY TESTING ============================================================================ --> <target name="bc.init" depends="init" unless="maven-deps-done-mima"> + <property name="bc-reference-version" value="2.11.0"/> + <property name="bc-build.dir" value="${build.dir}/bc"/> <!-- Obtain mima --> <mkdir dir="${bc-build.dir}"/> <!-- Pull down MIMA --> <artifact:dependencies pathId="mima.classpath"> - <dependency groupId="com.typesafe" artifactId="mima-reporter_2.9.2" version="0.1.4"/> + <dependency groupId="com.typesafe" artifactId="mima-reporter_2.9.2" version="0.1.5"/> </artifact:dependencies> <artifact:dependencies pathId="old.bc.classpath"> - <dependency groupId="org.scala-lang" artifactId="scala-library" version="2.10.0-RC2"/> - <dependency groupId="org.scala-lang" artifactId="scala-reflect" version="2.10.0-RC2"/> + <dependency groupId="org.scala-lang" artifactId="scala-swing" version="${bc-reference-version}"/> + <dependency groupId="org.scala-lang" artifactId="scala-library" version="${bc-reference-version}"/> + <dependency groupId="org.scala-lang" artifactId="scala-reflect" version="${bc-reference-version}"/> </artifact:dependencies> <property name="maven-deps-done-mima" value="true"/> </target> - <target name="test.bc" depends="bc.init, pack.lib"> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--prev"/> - <arg value="${org.scala-lang:scala-library:jar}"/> - <arg value="--curr"/> - <arg value="${build-pack.dir}/lib/scala-library.jar"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> - <java - fork="true" - failonerror="true" - classname="com.typesafe.tools.mima.cli.Main"> - <arg value="--prev"/> - <arg value="${org.scala-lang:scala-reflect:jar}"/> - <arg value="--curr"/> - <arg value="${build-pack.dir}/lib/scala-reflect.jar"/> - <classpath> - <path refid="mima.classpath"/> - </classpath> - </java> + <macrodef name="bc.run-mima"> + <attribute name="jar-name"/> + <attribute name="prev"/> + <attribute name="curr"/> + <attribute name="direction"/> + <sequential> + <echo message="Checking @{direction} binary compatibility for @{jar-name} (against ${bc-reference-version})"/> + <java taskname="mima" + fork="true" + failonerror="true" + classname="com.typesafe.tools.mima.cli.Main"> + <arg value="--prev"/> + <arg value="@{prev}"/> + <arg value="--curr"/> + <arg value="@{curr}"/> + <arg value="--filters"/> + <arg value="${basedir}/bincompat-@{direction}.whitelist.conf"/> + <arg value="--generate-filters"/> + <classpath> + <path refid="mima.classpath"/> + </classpath> + </java> + </sequential> + </macrodef> + + <macrodef name="bc.check"> + <attribute name="jar-name"/> + <sequential> + <bc.run-mima + jar-name="@{jar-name}" + prev="${org.scala-lang:@{jar-name}:jar}" + curr="${build-pack.dir}/lib/@{jar-name}.jar" + direction="backward"/> + <bc.run-mima + jar-name="@{jar-name}" + prev="${build-pack.dir}/lib/@{jar-name}.jar" + curr="${org.scala-lang:@{jar-name}:jar}" + direction="forward"/> + </sequential> + </macrodef> + + <target name="test.bc-opt" description="Optimized version of test.bc."> <optimized name="test.bc"/></target> + + <!-- Enable after the release of the 2.11.0 or a prior RC that estabilishes the new baseline. --> + <target name="test.bc"></target> + <target name="test.bc.disabled" depends="bc.init, pack.lib, pack.reflect, pack.swing"> + <bc.check jar-name="scala-library"/> + <bc.check jar-name="scala-reflect"/> + <bc.check jar-name="scala-swing"/> </target> <!-- =========================================================================== @@ -1509,6 +1569,7 @@ targets exercised: <include name="swing/**"/> <include name="actors/**"/> <include name="reflect/**"/> + <include name="continuations/library/**"/> </srcfiles></check> <do> <stopwatch name="docs.lib.timer"/> @@ -1656,6 +1717,13 @@ targets exercised: <include name="scalap.jar"/> </fileset> </copy> + + <copy todir="${dist.dir}/lib"> + <resources refid="partest.extras.fileset"/> + <mapper classpathref="maven-ant-tasks.classpath" classname="org.apache.maven.artifact.ant.VersionMapper" + from="${partest.extras.versions}" to="flatten"/> + </copy> + <mkdir dir="${dist.dir}/bin"/> <!-- TODO - Stop being inefficient and don't copy OSGi bundles overtop other jars. --> <copy-bundle name="scala-library"/> @@ -1804,7 +1872,7 @@ targets exercised: </target> <target name="replacestarr-opt" description="Replaces the Starr compiler and library by fresh, optimised ones built from current sources and tests them."> - <optimize name="replacestarr"/></target> + <optimized name="replacestarr"/></target> <!-- Ant on Windows is not able to delete jar files that are referenced in any <path>. See ticket 1290 on trac. --> @@ -1821,13 +1889,13 @@ targets exercised: </target> <target name="replacestarrwin-opt" description="Creates a new Starr on Windows. Manually execute 'ant locker.clean build' first!"> - <optimize name="replacestarrwin"/></target> + <optimized name="replacestarrwin"/></target> <target name="replacelocker" description="Replaces the Locker compiler and library by fresh ones built from current sources." depends="palo.clean, locker.unlock, palo.done"/> <target name="replacelocker-opt" description="Replaces the Locker compiler and library by fresh, optimised ones built from current sources."> - <optimize name="replacelocker"/></target> + <optimized name="replacelocker"/></target> <target name="buildlocker" description="Does the same for locker as build does for quick." depends="locker.unlock, palo.bin"/> <target name="unlocklocker" description="Same as buildlocker." depends="buildlocker"/> <!-- REMOVE --> @@ -1846,105 +1914,6 @@ targets exercised: <target name="fastlocker" description="Buildlocker without extra fuss" depends="fastlocker.comp"/> - -<!-- =========================================================================== - TODO: FIX OR REMOVE -============================================================================ --> - - <!-- not called by test target --> - <target name="test.classload" depends="pack.done"> - <!-- TODO - Add actors + reflect to this --> - <classloadVerify classpath="${build-pack.dir}/lib/scala-library.jar" /> - </target> - - <!-- not called by test target --> - <target name="test.ant" depends="pack.done"> - <partest showlog="yes" erroronfailed="yes" javacmd="${java.home}/bin/java" - srcdir="${partest.srcdir}" - scalacopts="${scalac.args.optimise}"> - <compilationpath refid="partest.classpath"/> - <anttests dir="${partest.dir}/${partest.srcdir}/ant" includes="*build.xml"/> - </partest> - </target> - - <!-- not called by test target --> - <target name="test.positions" depends="quick.comp"> - <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.dir}/files/positions"/> - </antcall> - <antcall target="test.positions.sub" inheritRefs="true"> - <param name="test.srcs" value="${src.dir}/compiler"/> - </antcall> - <antcall target="test.positions.sub" inheritRefs="true"> - <param name="test.srcs" value="${src.dir}/library"/> - </antcall> - <antcall target="test.positions.sub" inheritRefs="true"> - <param name="test.srcs" value="${src.dir}/actors"/> - </antcall> - <antcall target="test.positions.sub" inheritRefs="true"> - <param name="test.srcs" value="${src.dir}/dbc"/> - </antcall> - <antcall target="test.positions.sub" inheritRefs="true"> - <param name="test.srcs" value="${src.dir}/swing"/> - </antcall> - <antcall target="test.positions.sub" inheritRefs="true"> - <param name="test.srcs" value="${src.dir}/partest"/> - </antcall> - <antcall target="test.positions.sub" inheritRefs="true"> - <param name="test.srcs" value="${src.dir}/scalap"/> - </antcall> - <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.dir}/files/pos"/> - </antcall> - <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.dir}/files/run"/> - </antcall> - <antcall target="test.positions.tests.sub" inheritRefs="true"> - <param name="test.tests.srcs" value="${partest.dir}/files/neg"/> - </antcall> - </target> - - <target name="test.positions.sub"> - <echo message="Validating positions for: ${test.srcs}"/> - <if> - <isfileselected file="${test.srcs}"> - <type type="dir"/> - </isfileselected> - <then> - <property name="srcdir" value="${test.srcs}"/> - <property name="srcs" value="**/*.scala"/> - </then> - <else> - <dirname property="srcdir" file="${test.srcs}"/> - <basename property="srcs" file="${test.srcs}"/> - </else> - </if> - <scalacfork - destdir="" - compilerpathref="locker.compiler.path" - srcpath="${srcdir}" - params="-Xprint-pos -Yide-debug" - srcdir="${srcdir}" - jvmargs="${scalacfork.jvmargs}"> - <include name="${srcs}"/> - <compilationpath refid="test.positions.sub.build.path"/> - </scalacfork> - </target> - - <target name="test.positions.tests.sub"> - <foreach target="test.positions.sub" - inheritAll="true" - inheritRefs="true" - param="test.srcs"> - <path> - <fileset dir="${test.tests.srcs}" includes="*.scala"/> - <dirset dir="${test.tests.srcs}"> - <include name="*"/> - </dirset> - </path> - </foreach> - </target> - <!-- =========================================================================== VISUALIZATION ============================================================================ --> diff --git a/src/compiler/scala/reflect/macros/runtime/Reifiers.scala b/src/compiler/scala/reflect/macros/runtime/Reifiers.scala index 8bb388be8f..7ec3457c6a 100644 --- a/src/compiler/scala/reflect/macros/runtime/Reifiers.scala +++ b/src/compiler/scala/reflect/macros/runtime/Reifiers.scala @@ -60,15 +60,15 @@ trait Reifiers { def logFreeVars(symtab: SymbolTable): Unit = // logging free vars only when they are untyped prevents avalanches of duplicate messages symtab.syms map (sym => symtab.symDef(sym)) foreach { - case FreeTermDef(_, _, binding, _, origin) if universe.settings.logFreeTerms.value && binding.tpe == null => + case FreeTermDef(_, _, binding, _, origin) if universe.settings.logFreeTerms && binding.tpe == null => reporter.echo(position, "free term: %s %s".format(showRaw(binding), origin)) - case FreeTypeDef(_, _, binding, _, origin) if universe.settings.logFreeTypes.value && binding.tpe == null => + case FreeTypeDef(_, _, binding, _, origin) if universe.settings.logFreeTypes && binding.tpe == null => reporter.echo(position, "free type: %s %s".format(showRaw(binding), origin)) case _ => // do nothing } - if (universe.settings.logFreeTerms.value || universe.settings.logFreeTypes.value) + if (universe.settings.logFreeTerms || universe.settings.logFreeTypes) reification match { case ReifiedTree(_, _, symtab, _, _, _, _) => logFreeVars(symtab) case ReifiedType(_, _, symtab, _, _, _) => logFreeVars(symtab) diff --git a/src/compiler/scala/reflect/reify/Phases.scala b/src/compiler/scala/reflect/reify/Phases.scala index d43532090c..4572caeb36 100644 --- a/src/compiler/scala/reflect/reify/Phases.scala +++ b/src/compiler/scala/reflect/reify/Phases.scala @@ -25,7 +25,7 @@ trait Phases extends Reshape if (reifyDebug) println("[reshape phase]") tree = reshape.transform(tree) if (reifyDebug) println("[interlude]") - if (reifyDebug) println("reifee = " + (if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)) + if (reifyDebug) println("reifee = " + (if (settings.Xshowtrees || settings.XshowtreesCompact || settings.XshowtreesStringified) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString)) if (reifyDebug) println("[calculate phase]") calculate.traverse(tree) diff --git a/src/compiler/scala/reflect/reify/Reifier.scala b/src/compiler/scala/reflect/reify/Reifier.scala index 9cf069fe98..11857e2172 100644 --- a/src/compiler/scala/reflect/reify/Reifier.scala +++ b/src/compiler/scala/reflect/reify/Reifier.scala @@ -57,7 +57,7 @@ abstract class Reifier extends States val result = reifee match { case tree: Tree => - reifyTrace("reifying = ")(if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) + reifyTrace("reifying = ")(if (settings.Xshowtrees || settings.XshowtreesCompact || settings.XshowtreesStringified) "\n" + nodePrinters.nodeToString(tree).trim else tree.toString) reifyTrace("reifee is located at: ")(tree.pos) reifyTrace("universe = ")(universe) reifyTrace("mirror = ")(mirror) @@ -140,4 +140,4 @@ abstract class Reifier extends States throw new UnexpectedReificationException(defaultErrorPosition, "reification crashed", ex) } } -}
\ No newline at end of file +} diff --git a/src/compiler/scala/reflect/reify/phases/Reshape.scala b/src/compiler/scala/reflect/reify/phases/Reshape.scala index bb5cb53d7d..bc2dbeed3e 100644 --- a/src/compiler/scala/reflect/reify/phases/Reshape.scala +++ b/src/compiler/scala/reflect/reify/phases/Reshape.scala @@ -254,7 +254,7 @@ trait Reshape { case _ => rhs // unit or trait case } val DefDef(mods0, name0, _, _, tpt0, rhs0) = ddef - val name1 = nme.dropLocalSuffix(name0) + val name1 = name0.dropLocal val Modifiers(flags0, privateWithin0, annotations0) = mods0 val flags1 = (flags0 & GetterFlags) & ~(STABLE | ACCESSOR | METHOD) val mods1 = Modifiers(flags1, privateWithin0, annotations0) setPositions mods0.positions @@ -273,7 +273,9 @@ trait Reshape { if (defdef.name.startsWith(prefix)) { val name = defdef.name.toString.substring(prefix.length) def uncapitalize(s: String) = if (s.length == 0) "" else { val chars = s.toCharArray; chars(0) = chars(0).toLower; new String(chars) } - def findValDef(name: String) = (symdefs.values collect { case vdef: ValDef if nme.dropLocalSuffix(vdef.name).toString == name => vdef }).headOption + def findValDef(name: String) = symdefs.values collectFirst { + case vdef: ValDef if vdef.name.dropLocal string_== name => vdef + } val valdef = findValDef(name).orElse(findValDef(uncapitalize(name))).orNull if (valdef != null) accessors(valdef) = accessors.getOrElse(valdef, Nil) :+ defdef } @@ -297,7 +299,7 @@ trait Reshape { mods } val mods2 = toPreTyperModifiers(mods1, vdef.symbol) - val name1 = nme.dropLocalSuffix(name) + val name1 = name.dropLocal val vdef1 = ValDef(mods2, name1.toTermName, tpt, rhs) if (reifyDebug) println("resetting visibility of field: %s => %s".format(vdef, vdef1)) Some(vdef1) // no copyAttrs here, because new ValDef and old symbols are now out of sync diff --git a/src/compiler/scala/reflect/reify/utils/Extractors.scala b/src/compiler/scala/reflect/reify/utils/Extractors.scala index d57188bf6e..7338df1f72 100644 --- a/src/compiler/scala/reflect/reify/utils/Extractors.scala +++ b/src/compiler/scala/reflect/reify/utils/Extractors.scala @@ -164,51 +164,30 @@ trait Extractors { } } - object FreeDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case FreeTermDef(uref, name, binding, flags, origin) => - Some((uref, name, binding, flags, origin)) - case FreeTypeDef(uref, name, binding, flags, origin) => - Some((uref, name, binding, flags, origin)) - case _ => - None - } - } - - object FreeTermDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case - ValDef(_, name, _, Apply( - Select(Select(uref1 @ Ident(_), build1), newFreeTerm), - List( - _, - _, - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), - Literal(Constant(origin: String))))) - if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newFreeTerm == nme.newFreeTerm && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => - Some((uref1, name, reifyBinding(tree), flags, origin)) - case _ => - None - } - } - - object FreeTypeDef { - def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = tree match { - case - ValDef(_, name, _, Apply( - Select(Select(uref1 @ Ident(_), build1), newFreeType), - List( - _, - Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))), - Literal(Constant(origin: String))))) - if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && newFreeType == nme.newFreeType && - uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => - Some((uref1, name, reifyBinding(tree), flags, origin)) - case _ => - None + sealed abstract class FreeDefExtractor(acceptTerms: Boolean, acceptTypes: Boolean) { + def unapply(tree: Tree): Option[(Tree, TermName, Tree, Long, String)] = { + def acceptFreeTermFactory(name: Name) = { + (acceptTerms && name == nme.newFreeTerm) || + (acceptTypes && name == nme.newFreeType) + } + tree match { + case + ValDef(_, name, _, Apply( + Select(Select(uref1 @ Ident(_), build1), freeTermFactory), + _ :+ + Apply(Select(Select(uref2 @ Ident(_), build2), flagsFromBits), List(Literal(Constant(flags: Long)))) :+ + Literal(Constant(origin: String)))) + if uref1.name == nme.UNIVERSE_SHORT && build1 == nme.build && acceptFreeTermFactory(freeTermFactory) && + uref2.name == nme.UNIVERSE_SHORT && build2 == nme.build && flagsFromBits == nme.flagsFromBits => + Some((uref1, name, reifyBinding(tree), flags, origin)) + case _ => + None + } } } + object FreeDef extends FreeDefExtractor(acceptTerms = true, acceptTypes = true) + object FreeTermDef extends FreeDefExtractor(acceptTerms = true, acceptTypes = false) + object FreeTypeDef extends FreeDefExtractor(acceptTerms = false, acceptTypes = true) object FreeRef { def unapply(tree: Tree): Option[(Tree, TermName)] = tree match { diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala index c756a1b0d9..64f6758e73 100644 --- a/src/compiler/scala/tools/nsc/CompileClient.scala +++ b/src/compiler/scala/tools/nsc/CompileClient.scala @@ -26,12 +26,12 @@ class StandardCompileClient extends HasCompileSocket with CompileOutputCommon { val settings = new FscSettings(Console.println) val command = new OfflineCompilerCommand(args.toList, settings) val shutdown = settings.shutdown.value - val extraVmArgs = if (settings.preferIPv4.value) List("-D%s=true".format(preferIPv4Stack.key)) else Nil + val extraVmArgs = if (settings.preferIPv4) List("-D%s=true".format(preferIPv4Stack.key)) else Nil val vmArgs = settings.jvmargs.unparse ++ settings.defines.unparse ++ extraVmArgs val fscArgs = args.toList ++ command.extraFscArgs - if (settings.version.value) { + if (settings.version) { Console println versionMsg return true } diff --git a/src/compiler/scala/tools/nsc/Driver.scala b/src/compiler/scala/tools/nsc/Driver.scala index fc247600f6..fbfed6110f 100644 --- a/src/compiler/scala/tools/nsc/Driver.scala +++ b/src/compiler/scala/tools/nsc/Driver.scala @@ -41,7 +41,7 @@ abstract class Driver { command = new CompilerCommand(args.toList, ss) settings = command.settings - if (settings.version.value) { + if (settings.version) { reporter.echo(versionMsg) } else if (processSettingsHook()) { val compiler = newCompiler() diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index c53d6cd11e..8b78ddd59d 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -221,7 +221,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def inform(msg: String) = reporter.echo(msg) override def globalError(msg: String) = reporter.error(NoPosition, msg) override def warning(msg: String) = - if (settings.fatalWarnings.value) globalError(msg) + if (settings.fatalWarnings) globalError(msg) else reporter.warning(NoPosition, msg) // Getting in front of Predef's asserts to supplement with more info. @@ -252,7 +252,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } @inline final def ifDebug(body: => Unit) { - if (settings.debug.value) + if (settings.debug) body } /** This is for WARNINGS which should reach the ears of scala developers @@ -262,7 +262,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) * to make them visually distinct. */ @inline final override def devWarning(msg: => String) { - if (settings.developer.value || settings.debug.value) + if (settings.developer || settings.debug) warning("!!! " + msg) else log("!!! " + msg) // such warnings always at least logged @@ -272,7 +272,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) msg + " in " + (currentTime - start) + "ms" def informComplete(msg: String): Unit = reporter.withoutTruncating(inform(msg)) - def informProgress(msg: String) = if (settings.verbose.value) inform("[" + msg + "]") + def informProgress(msg: String) = if (settings.verbose) inform("[" + msg + "]") def informTime(msg: String, start: Long) = informProgress(elapsedMessage(msg, start)) def logError(msg: String, t: Throwable): Unit = () @@ -287,7 +287,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } @inline final override def debuglog(msg: => String) { - if (settings.debug.value) + if (settings.debug) log(msg) } @@ -332,7 +332,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } - if (settings.verbose.value || settings.Ylogcp.value) { + 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 + "]") @@ -402,7 +402,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if ((unit ne null) && unit.exists) lastSeenSourceFile = unit.source - if (settings.debug.value && (settings.verbose.value || currentRun.size < 5)) + if (settings.debug && (settings.verbose || currentRun.size < 5)) inform("[running phase " + name + " on " + unit + "]") val unit0 = currentUnit @@ -736,7 +736,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val maxName = (0 /: phaseNames)(_ max _.length) val width = maxName min Limit val maxDesc = MaxCol - (width + 6) // descriptions not novels - val fmt = if (settings.verbose.value) s"%${maxName}s %2s %s%n" + val fmt = if (settings.verbose) s"%${maxName}s %2s %s%n" else s"%${width}.${width}s %2s %.${maxDesc}s%n" val line1 = fmt.format("phase name", "id", "description") @@ -1097,7 +1097,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) val info3: List[String] = ( ( List("== Enclosing template or block ==", nodePrinters.nodeToString(enclosing).trim) ) ++ ( if (tpe eq null) Nil else List("== Expanded type of tree ==", typeDeconstruct.show(tpe)) ) - ++ ( if (!settings.debug.value) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) ) + ++ ( if (!settings.debug) Nil else List("== Current unit body ==", nodePrinters.nodeToString(currentUnit.body)) ) ++ ( List(errorMessage) ) ) @@ -1113,7 +1113,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def echoPhaseSummary(ph: Phase) = { /* Only output a summary message under debug if we aren't echoing each file. */ - if (settings.debug.value && !(settings.verbose.value || currentRun.size < 5)) + if (settings.debug && !(settings.verbose || currentRun.size < 5)) inform("[running phase " + ph.name + " on " + currentRun.size + " compilation units]") } @@ -1121,10 +1121,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter) class ConditionalWarning(what: String, option: Settings#BooleanSetting) { val warnings = mutable.LinkedHashMap[Position, String]() def warn(pos: Position, msg: String) = - if (option.value) reporter.warning(pos, msg) + if (option) reporter.warning(pos, msg) else if (!(warnings contains pos)) warnings += ((pos, msg)) def summarize() = - if (warnings.nonEmpty && (option.isDefault || settings.fatalWarnings.value)) + 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)) } @@ -1240,7 +1240,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def resetProjectClasses(root: Symbol): Unit = try { def unlink(sym: Symbol) = if (sym != NoSymbol) root.info.decls.unlink(sym) - if (settings.verbose.value) inform("[reset] recursing in "+root) + if (settings.verbose) inform("[reset] recursing in "+root) val toReload = mutable.Set[String]() for (sym <- root.info.decls) { if (sym.isInitialized && clearOnNextRun(sym)) @@ -1260,7 +1260,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) for (fullname <- toReload) classPath.findClass(fullname) match { case Some(classRep) => - if (settings.verbose.value) inform("[reset] reinit "+fullname) + if (settings.verbose) inform("[reset] reinit "+fullname) loaders.initializeFromClassPath(root, classRep) case _ => } @@ -1453,7 +1453,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } def reportCompileErrors() { - if (!reporter.hasErrors && reporter.hasWarnings && settings.fatalWarnings.value) + if (!reporter.hasErrors && reporter.hasWarnings && settings.fatalWarnings) globalError("No warnings can be incurred under -Xfatal-warnings.") if (reporter.hasErrors) { @@ -1492,7 +1492,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) def compileUnits(units: List[CompilationUnit], fromPhase: Phase) { try compileUnitsInternal(units, fromPhase) catch { case ex: Throwable => - val shown = if (settings.verbose.value) + val shown = if (settings.verbose) stackTraceString(ex) else stackTraceHeadString(ex) // note that error stacktraces do not print in fsc @@ -1526,14 +1526,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter) if (shouldWriteIcode) { // Write *.icode files when -Xprint-icode or -Xprint:<some-optimiz-phase> was given. writeICode() - } else if ((settings.Xprint containsPhase globalPhase) || settings.printLate.value && runIsAt(cleanupPhase)) { + } else if ((settings.Xprint containsPhase globalPhase) || settings.printLate && runIsAt(cleanupPhase)) { // print trees - if (settings.Xshowtrees.value || settings.XshowtreesCompact.value || settings.XshowtreesStringified.value) nodePrinters.printAll() + if (settings.Xshowtrees || settings.XshowtreesCompact || settings.XshowtreesStringified) nodePrinters.printAll() else printAllUnits() } // print the symbols presently attached to AST nodes - if (settings.Yshowsyms.value) + if (settings.Yshowsyms) trackerFactory.snapshot() // print members @@ -1552,7 +1552,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) runCheckers() // output collected statistics - if (settings.Ystatistics.value) + if (settings.Ystatistics) statistics.print(phase) advancePhase() @@ -1697,7 +1697,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) informProgress("wrote " + file) } catch { case ex: IOException => - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() globalError("could not write file " + file) } }) diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala index 00c6c37dfd..9f6f483ad8 100644 --- a/src/compiler/scala/tools/nsc/Main.scala +++ b/src/compiler/scala/tools/nsc/Main.scala @@ -17,7 +17,7 @@ class MainClass extends Driver with EvalLoop { override def newCompiler(): Global = Global(settings, reporter) override def doCompile(compiler: Global) { - if (settings.resident.value) resident(compiler) + if (settings.resident) resident(compiler) else super.doCompile(compiler) } } diff --git a/src/compiler/scala/tools/nsc/MainTokenMetric.scala b/src/compiler/scala/tools/nsc/MainTokenMetric.scala index 584805b37e..e2893204e0 100644 --- a/src/compiler/scala/tools/nsc/MainTokenMetric.scala +++ b/src/compiler/scala/tools/nsc/MainTokenMetric.scala @@ -42,7 +42,7 @@ object MainTokenMetric { tokenMetric(compiler, command.files) } catch { case ex @ FatalError(msg) => - if (command.settings.debug.value) + if (command.settings.debug) ex.printStackTrace() reporter.error(null, "fatal error: " + msg) } diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index 821e88e52e..5f8bc71449 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -95,7 +95,7 @@ class ScriptRunner extends HasCompileSocket { settings.outdir.value = compiledPath.path - if (settings.nc.value) { + if (settings.nc) { /* Setting settings.script.value informs the compiler this is not a * self contained compilation unit. */ @@ -114,7 +114,7 @@ class ScriptRunner extends HasCompileSocket { * not take place until there are no non-daemon threads running. Tickets #1955, #2006. */ util.waitingForThreads { - if (settings.save.value) { + if (settings.save) { val jarFile = jarFileFor(scriptFile) def jarOK = jarFile.canRead && (jarFile isFresher File(scriptFile)) diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 602366a201..caab299635 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -32,7 +32,7 @@ abstract class NodePrinters { } trait DefaultPrintAST extends PrintAST { - val printPos = settings.Xprintpos.value || settings.Yposdebug.value + val printPos = settings.Xprintpos || settings.Yposdebug def showNameAndPos(tree: NameTree) = showPosition(tree) + showName(tree.name) def showDefTreeName(tree: DefTree) = showName(tree.name) @@ -100,9 +100,9 @@ abstract class NodePrinters { def stringify(tree: Tree): String = { buf.clear() - if (settings.XshowtreesStringified.value) buf.append(tree.toString + EOL) - if (settings.XshowtreesCompact.value) { - buf.append(showRaw(tree, printIds = settings.uniqid.value, printTypes = settings.printtypes.value)) + if (settings.XshowtreesStringified) buf.append(tree.toString + EOL) + if (settings.XshowtreesCompact) { + buf.append(showRaw(tree, printIds = settings.uniqid, printTypes = settings.printtypes)) } else { level = 0 traverse(tree) diff --git a/src/compiler/scala/tools/nsc/ast/Positions.scala b/src/compiler/scala/tools/nsc/ast/Positions.scala index 66d75969e9..beab801edf 100644 --- a/src/compiler/scala/tools/nsc/ast/Positions.scala +++ b/src/compiler/scala/tools/nsc/ast/Positions.scala @@ -25,6 +25,6 @@ trait Positions extends scala.reflect.internal.Positions { } override protected[this] lazy val posAssigner: PosAssigner = - if (settings.Yrangepos.value && settings.debug.value || settings.Yposdebug.value) new ValidatingPosAssigner + if (settings.Yrangepos && settings.debug || settings.Yposdebug) new ValidatingPosAssigner else new DefaultPosAssigner } diff --git a/src/compiler/scala/tools/nsc/ast/Printers.scala b/src/compiler/scala/tools/nsc/ast/Printers.scala index 7fefb2ce0c..72538bac08 100644 --- a/src/compiler/scala/tools/nsc/ast/Printers.scala +++ b/src/compiler/scala/tools/nsc/ast/Printers.scala @@ -200,15 +200,15 @@ trait Printers extends scala.reflect.internal.Printers { this: Global => override def printTree(tree: Tree) { print(safe(tree)) } } - def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) - def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes.value, settings.uniqid.value, settings.Yshowsymkinds.value) + def asString(t: Tree): String = render(t, newStandardTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymkinds) + def asCompactString(t: Tree): String = render(t, newCompactTreePrinter, settings.printtypes, settings.uniqid, settings.Yshowsymkinds) def asCompactDebugString(t: Tree): String = render(t, newCompactTreePrinter, true, true, true) def newStandardTreePrinter(writer: PrintWriter): TreePrinter = new TreePrinter(writer) def newCompactTreePrinter(writer: PrintWriter): CompactTreePrinter = new CompactTreePrinter(writer) override def newTreePrinter(writer: PrintWriter): TreePrinter = - if (settings.Ycompacttrees.value) newCompactTreePrinter(writer) + if (settings.Ycompacttrees) newCompactTreePrinter(writer) else newStandardTreePrinter(writer) override def newTreePrinter(stream: OutputStream): TreePrinter = newTreePrinter(new PrintWriter(stream)) override def newTreePrinter(): TreePrinter = newTreePrinter(new PrintWriter(ConsoleWriter)) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 68e198d071..7671912651 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2085,7 +2085,7 @@ self => val name = ident() var bynamemod = 0 val tpt = - if (settings.YmethodInfer.value && !owner.isTypeName && in.token != COLON) { + if (settings.YmethodInfer && !owner.isTypeName && in.token != COLON) { TypeTree() } else { // XX-METHOD-INFER accept(COLON) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index b485e862fd..eb31f7a66e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -446,7 +446,7 @@ trait Scanners extends ScannersCommon { * there a realistic situation where one would need it? */ if (isDigit(ch)) { - if (settings.future.value) syntaxError("Non-zero numbers may not have a leading zero.") + if (settings.future) syntaxError("Non-zero numbers may not have a leading zero.") else deprecationWarning("Treating numbers with a leading zero as octal is deprecated.") } base = 8 @@ -990,7 +990,7 @@ trait Scanners extends ScannersCommon { /* As of scala 2.11, it isn't a number unless c here is a digit, so * settings.future.value excludes the rest of the logic. */ - if (settings.future.value && !isDigit(c)) + if (settings.future && !isDigit(c)) return setStrVal() val isDefinitelyNumber = (c: @switch) match { @@ -1217,7 +1217,7 @@ trait Scanners extends ScannersCommon { */ class SourceFileScanner(val source: SourceFile) extends Scanner { val buf = source.content - override val decodeUni: Boolean = !settings.nouescape.value + override val decodeUni: Boolean = !settings.nouescape // suppress warnings, throw exception on errors def deprecationWarning(off: Offset, msg: String): Unit = () diff --git a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala index 7cf5a07291..80d70e6428 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SyntaxAnalyzer.scala @@ -32,7 +32,7 @@ abstract class SyntaxAnalyzer extends SubComponent with Parsers with MarkupParse else newUnitParser(unit).smartParse() } - if (settings.Yrangepos.value && !reporter.hasErrors) + if (settings.Yrangepos && !reporter.hasErrors) validatePositions(unit.body) } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala index 89682e91d2..94fbba8066 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/BasicBlocks.scala @@ -331,7 +331,7 @@ trait BasicBlocks { assert(!closed || ignore, this) if (ignore) { - if (settings.debug.value) { + if (settings.debug) { /* Trying to pin down what it's likely to see after a block has been * put into ignore mode so we hear about it if there's a problem. */ @@ -405,11 +405,11 @@ trait BasicBlocks { * is discovered to be unreachable. */ def killIf(cond: Boolean) { - if (!settings.YdisableUnreachablePrevention.value && cond) { + if (!settings.YdisableUnreachablePrevention && cond) { debuglog(s"Killing block $this") assert(instructionList.isEmpty, s"Killing a non empty block $this") // only checked under debug because fetching predecessor list is moderately expensive - if (settings.debug.value) + if (settings.debug) assert(predecessors.isEmpty, s"Killing block $this which is referred to from ${predecessors.mkString}") close() diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 8ba75fbb46..31028e64d3 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -39,7 +39,7 @@ abstract class GenICode extends SubComponent { override def newPhase(prev: Phase) = new ICodePhase(prev) @inline private def debugassert(cond: => Boolean, msg: => Any) { - if (settings.debug.value) + if (settings.debug) assert(cond, msg) } @@ -129,7 +129,7 @@ abstract class GenICode extends SubComponent { case Return(_) => () case EmptyTree => globalError("Concrete method has no definition: " + tree + ( - if (settings.debug.value) "(found: " + m.symbol.owner.info.decls.toList.mkString(", ") + ")" + if (settings.debug) "(found: " + m.symbol.owner.info.decls.toList.mkString(", ") + ")" else "") ) case _ => if (ctx1.bb.isEmpty) @@ -710,7 +710,7 @@ abstract class GenICode extends SubComponent { debuglog("BOX : " + fun.symbol.fullName) val ctx1 = genLoad(expr, ctx, toTypeKind(expr.tpe)) val nativeKind = toTypeKind(expr.tpe) - if (settings.Xdce.value) { + if (settings.Xdce) { // we store this boxed value to a local, even if not really needed. // boxing optimization might use it, and dead code elimination will // take care of unnecessary stores @@ -1483,7 +1483,7 @@ abstract class GenICode extends SubComponent { if (mustUseAnyComparator) { // when -optimise is on we call the @inline-version of equals, found in ScalaRunTime val equalsMethod = - if (!settings.optimise.value) { + if (!settings.optimise) { def default = platform.externalEquals platform match { case x: JavaPlatform => @@ -1507,7 +1507,7 @@ abstract class GenICode extends SubComponent { val ctx2 = genLoad(r, ctx1, ObjectReference) val branchesReachable = !ctx2.bb.ignore ctx2.bb.emitOnly( - CALL_METHOD(equalsMethod, if (settings.optimise.value) Dynamic else Static(onInstance = false)), + CALL_METHOD(equalsMethod, if (settings.optimise) Dynamic else Static(onInstance = false)), CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL) ) branchesReachable @@ -2032,7 +2032,7 @@ abstract class GenICode extends SubComponent { // Generate the catch-all exception handler that deals with uncaught exceptions coming // from the try or exception handlers. It catches the exception, runs the finally code, then rethrows // the exception - if (settings.YdisableUnreachablePrevention.value || !outerCtx.bb.ignore) { + if (settings.YdisableUnreachablePrevention || !outerCtx.bb.ignore) { if (finalizer != EmptyTree) { val exh = outerCtx.newExceptionHandler(NoSymbol, finalizer.pos) // finalizer covers exception handlers this.addActiveHandler(exh) // .. and body aswell diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala index b7b07a579f..0cdf629ce1 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala @@ -94,7 +94,7 @@ abstract class ICodeCheckers { } def checkICodes(): Unit = { - if (settings.verbose.value) + if (settings.verbose) println("[[consistency check at the beginning of phase " + globalPhase.name + "]]") classes.values foreach check } @@ -444,7 +444,7 @@ abstract class ICodeCheckers { def checkBool(cond: Boolean, msg: String) = if (!cond) icodeError(msg) - if (settings.debug.value) { + if (settings.debug) { log("PC: " + instr) log("stack: " + stack) log("================") diff --git a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala index e2d387c65d..3f2141782a 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ICodes.scala @@ -35,7 +35,7 @@ abstract class ICodes extends AnyRef /** Debugging flag */ def shouldCheckIcode = settings.check contains global.genicode.phaseName - def checkerDebug(msg: String) = if (shouldCheckIcode && global.settings.debug.value) println(msg) + def checkerDebug(msg: String) = if (shouldCheckIcode && global.settings.debug) println(msg) /** The ICode linearizer. */ val linearizer: Linearizer = settings.Xlinearizer.value match { diff --git a/src/compiler/scala/tools/nsc/backend/icode/Members.scala b/src/compiler/scala/tools/nsc/backend/icode/Members.scala index e471f4256b..c1cda2c863 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Members.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Members.scala @@ -61,7 +61,7 @@ trait Members { startBlock = newBlock() def removeBlock(b: BasicBlock) { - if (settings.debug.value) { + if (settings.debug) { // only do this sanity check when debug is turned on because it's moderately expensive val referers = blocks filter (_.successors contains b) assert(referers.isEmpty, s"Trying to removing block $b (with preds ${b.predecessors.mkString}) but it is still refered to from block(s) ${referers.mkString}") @@ -172,6 +172,7 @@ trait Members { var returnType: TypeKind = _ var recursive: Boolean = false var bytecodeHasEHs = false // set by ICodeReader only, used by Inliner to prevent inlining (SI-6188) + var bytecodeHasInvokeDynamic = false // set by ICodeReader only, used by Inliner to prevent inlining until we have proper invoke dynamic support /** local variables and method parameters */ var locals: List[Local] = Nil diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index d8aac8e9db..ff118be3c4 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -394,6 +394,25 @@ trait Opcodes { self: ICodes => override def category = mthdsCat } + + /** + * A place holder entry that allows us to parse class files with invoke dynamic + * instructions. Because the compiler doesn't yet really understand the + * behavior of invokeDynamic, this op acts as a poison pill. Any attempt to analyze + * this instruction will cause a failure. The only optimization that + * should ever look at non-Scala generated icode is the inliner, and it + * has been modified to not examine any method with invokeDynamic + * instructions. So if this poison pill ever causes problems then + * there's been a serious misunderstanding + */ + // TODO do the real thing + case class INVOKE_DYNAMIC(poolEntry: Char) extends Instruction { + private def error = sys.error("INVOKE_DYNAMIC is not fully implemented and should not be analyzed") + override def consumed = error + override def produced = error + override def producedTypes = error + override def category = error + } case class BOX(boxType: TypeKind) extends Instruction { assert(boxType.isValueType && (boxType ne UNIT)) // documentation diff --git a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala index 5b47e3cfff..1fe33f78e7 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala @@ -110,7 +110,7 @@ trait Printers { self: ICodes => print(bb.label) if (bb.loopHeader) print("[loop header]") print(": ") - if (settings.debug.value) print("pred: " + bb.predecessors + " succs: " + bb.successors + " flags: " + bb.flagsString) + if (settings.debug) print("pred: " + bb.predecessors + " succs: " + bb.successors + " flags: " + bb.flagsString) indent(); println() bb.toList foreach printInstruction undent(); println() diff --git a/src/compiler/scala/tools/nsc/backend/icode/Repository.scala b/src/compiler/scala/tools/nsc/backend/icode/Repository.scala index e92e61c957..10d57df4a3 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Repository.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Repository.scala @@ -39,7 +39,7 @@ trait Repository { } catch { case e: Throwable => // possible exceptions are MissingRequirementError, IOException and TypeError -> no better common supertype log("Failed to load %s. [%s]".format(sym.fullName, e.getMessage)) - if (settings.debug.value) { e.printStackTrace } + if (settings.debug) { e.printStackTrace } false } diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala index 152a11ab1a..338a07c872 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala @@ -199,7 +199,7 @@ abstract class CopyPropagation { override def run() { forwardAnalysis(blockTransfer) - if (settings.debug.value) { + if (settings.debug) { linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(in(b) != lattice.bottom, "Block " + b + " in " + this.method + " has input equal to bottom -- not visited?")) diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala index 14b57f287f..60f7857d0c 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/Liveness.scala @@ -74,7 +74,7 @@ abstract class Liveness { override def run() { backwardAnalysis(blockTransfer) - if (settings.debug.value) { + if (settings.debug) { linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(lattice.bottom != in(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited?")) diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala index 2d29e6b14f..26b7bc50d8 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/ReachingDefinitions.scala @@ -51,7 +51,7 @@ abstract class ReachingDefinitions { // it'd be nice not to call zip with mismatched sequences because // it makes it harder to spot the real problems. val result = (a.stack, b.stack).zipped map (_ ++ _) - if (settings.debug.value && (a.stack.length != b.stack.length)) + if (settings.debug && (a.stack.length != b.stack.length)) devWarning(s"Mismatched stacks in ReachingDefinitions#lub2: ${a.stack}, ${b.stack}, returning $result") result } @@ -141,7 +141,7 @@ abstract class ReachingDefinitions { override def run() { forwardAnalysis(blockTransfer) - if (settings.debug.value) { + if (settings.debug) { linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(lattice.bottom != in(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? " + in(b) diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala index 227c1064ea..57380db7e7 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/TypeFlowAnalysis.scala @@ -136,7 +136,7 @@ abstract class TypeFlowAnalysis { // icodes.lubs0 = 0 forwardAnalysis(blockTransfer) timer.stop - if (settings.debug.value) { + if (settings.debug) { linearizer.linearize(method).foreach(b => if (b != method.startBlock) assert(visited.contains(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited)) @@ -167,7 +167,7 @@ abstract class TypeFlowAnalysis { val bindings = out.vars val stack = out.stack - if (settings.debug.value) { + if (settings.debug) { // Console.println("[before] Stack: " + stack); // Console.println(i); } @@ -367,7 +367,7 @@ abstract class TypeFlowAnalysis { preCandidates += rc._2.bb } - if (settings.debug.value) { + if (settings.debug) { for(b <- callerLin; if (b != method.startBlock) && preCandidates(b)) { assert(visited.contains(b), "Block " + b + " in " + this.method + " has input equal to bottom -- not visited? .." + visited) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 2aa874b567..66a58870cc 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -86,10 +86,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { override def run() { - if (settings.debug.value) + if (settings.debug) inform("[running phase " + name + " on icode]") - if (settings.Xdce.value) + if (settings.Xdce) for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) { log(s"Optimizer eliminated ${sym.fullNameString}") icodes.classes -= sym @@ -290,7 +290,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def inameToSymbol(iname: String): Symbol = { val name = global.newTypeName(iname) val res0 = - if (nme.isModuleName(name)) rootMirror.getModule(nme.stripModuleSuffix(name)) + if (nme.isModuleName(name)) rootMirror.getModule(name.dropModule) else rootMirror.getClassByName(name.replace('/', '.')) // TODO fails for inner classes (but this hasn't been tested). assert(res0 != NoSymbol) val res = jsymbol(res0) @@ -804,7 +804,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // without it. This is particularly bad because the availability of // generic information could disappear as a consequence of a seemingly // unrelated change. - settings.Ynogenericsig.value + settings.Ynogenericsig || sym.isArtifact || sym.isLiftedMethod || sym.isBridge @@ -834,7 +834,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { catch { case _: Throwable => false } } - if (settings.Xverify.value) { + if (settings.Xverify) { // Run the signature parser to catch bogus signatures. val isValidSignature = wrap { // Alternative: scala.tools.reflect.SigParser (frontend to sun.reflect.generics.parser.SignatureParser) @@ -1362,7 +1362,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { for (constructor <- c.lookupStaticCtor) { addStaticInit(Some(constructor)) } - val skipStaticForwarders = (c.symbol.isInterface || settings.noForwarders.value) + val skipStaticForwarders = (c.symbol.isInterface || settings.noForwarders) if (!skipStaticForwarders) { val lmoc = c.symbol.companionModule // add static forwarders if there are no name conflicts; see bugs #363 and #1735 @@ -2251,16 +2251,16 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { // info calls so that types are up to date; erasure may add lateINTERFACE to traits hostSymbol.info ; methodOwner.info - def isInterfaceCall(sym: Symbol) = ( - sym.isInterface && methodOwner != ObjectClass + def needsInterfaceCall(sym: Symbol) = ( + sym.isInterface || sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass) ) // whether to reference the type of the receiver or - // the type of the method owner (if not an interface!) + // the type of the method owner val useMethodOwner = ( style != Dynamic - || !isInterfaceCall(hostSymbol) && isAccessibleFrom(methodOwner, siteSymbol) || hostSymbol.isBottomClass + || methodOwner == ObjectClass ) val receiver = if (useMethodOwner) methodOwner else hostSymbol val jowner = javaName(receiver) @@ -2283,11 +2283,11 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } style match { - case Static(true) => dbg("invokespecial"); jcode.invokespecial (jowner, jname, jtype) - case Static(false) => dbg("invokestatic"); jcode.invokestatic (jowner, jname, jtype) - case Dynamic if isInterfaceCall(receiver) => dbg("invokinterface"); jcode.invokeinterface(jowner, jname, jtype) - case Dynamic => dbg("invokevirtual"); jcode.invokevirtual (jowner, jname, jtype) - case SuperCall(_) => + case Static(true) => dbg("invokespecial"); jcode.invokespecial (jowner, jname, jtype) + case Static(false) => dbg("invokestatic"); jcode.invokestatic (jowner, jname, jtype) + case Dynamic if needsInterfaceCall(receiver) => dbg("invokinterface"); jcode.invokeinterface(jowner, jname, jtype) + case Dynamic => dbg("invokevirtual"); jcode.invokevirtual (jowner, jname, jtype) + case SuperCall(_) => dbg("invokespecial") jcode.invokespecial(jowner, jname, jtype) initModule() @@ -3220,7 +3220,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { val detour = computeDetour rephraseGotos(detour) - if (settings.debug.value) { + if (settings.debug) { val (remappings, cycles) = detour partition {case (source, target) => source != target} for ((source, target) <- remappings) { debuglog(s"Will elide jump only block $source because it can be jumped around to get to $target.") @@ -3273,7 +3273,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } // remove the unusued exception handler references - if (settings.debug.value) + if (settings.debug) for (exh <- unusedExceptionHandlers) debuglog(s"eliding exception handler $exh because it does not cover any reachable blocks") m.exh = m.exh filterNot unusedExceptionHandlers @@ -3287,7 +3287,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { def normalize(m: IMethod) { if(!m.hasCode) { return } collapseJumpOnlyBlocks(m) - if (settings.optimise.value) + if (settings.optimise) elimUnreachableBlocks(m) icodes checkValid m } diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala index 8f439fc800..aaf2c55dcd 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala @@ -82,7 +82,7 @@ abstract class ClosureElimination extends SubComponent { * */ class ClosureElim { - def analyzeClass(cls: IClass): Unit = if (settings.Xcloselim.value) { + def analyzeClass(cls: IClass): Unit = if (settings.Xcloselim) { log(s"Analyzing ${cls.methods.size} methods in $cls.") cls.methods foreach { m => analyzeMethod(m) diff --git a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala index 7187bacb06..ff93206ffd 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala @@ -41,7 +41,7 @@ abstract class ConstantOptimization extends SubComponent { def name = phaseName override def apply(c: IClass) { - if (settings.YconstOptimization.value) { + if (settings.YconstOptimization) { val analyzer = new ConstantOptimizer analyzer optimizeClass c } diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala index 7042d7a042..1026e95fac 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala @@ -33,7 +33,7 @@ abstract class DeadCodeElimination extends SubComponent { val dce = new DeadCode() override def apply(c: IClass) { - if (settings.Xdce.value) + if (settings.Xdce) dce.analyzeClass(c) } } diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala index dcf0590951..cecabda171 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala @@ -88,7 +88,7 @@ abstract class InlineExceptionHandlers extends SubComponent { /** Apply exception handler inlining to a class */ override def apply(c: IClass): Unit = - if (settings.inlineHandlers.value) { + if (settings.inlineHandlers) { val startTime = System.currentTimeMillis currentClass = c diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 38040d921f..a6eedbd07e 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -265,7 +265,7 @@ abstract class Inliners extends SubComponent { } def analyzeClass(cls: IClass): Unit = - if (settings.inline.value) { + if (settings.inline) { inlineLog("class", s"${cls.symbol.decodedName}", s"analyzing ${cls.methods.size} methods in $cls") this.currentIClazz = cls @@ -319,7 +319,7 @@ abstract class Inliners extends SubComponent { * */ def analyzeMethod(m: IMethod): Unit = { // m.normalize - if (settings.debug.value) + if (settings.debug) inlineLog("caller", ownedName(m.symbol), "in " + m.symbol.owner.fullName) val sizeBeforeInlining = m.code.blockCount @@ -382,8 +382,8 @@ abstract class Inliners extends SubComponent { val shouldWarn = hasInline(i.method) def warnNoInline(reason: String): Boolean = { - def msg = "Could not inline required method %s because %s.".format(i.method.originalName.decode, reason) - if (settings.debug.value) + def msg = "Could not inline required method %s because %s.".format(i.method.unexpandedName.decode, reason) + if (settings.debug) inlineLog("fail", i.method.fullName, reason) if (shouldWarn) warn(i.pos, msg) @@ -565,7 +565,7 @@ abstract class Inliners extends SubComponent { while (retry && count < MAX_INLINE_RETRY) for(inlFail <- tfa.warnIfInlineFails) { - warn(inlFail.pos, "At the end of the day, could not inline @inline-marked method " + inlFail.method.originalName.decode) + warn(inlFail.pos, "At the end of the day, could not inline @inline-marked method " + inlFail.method.unexpandedName.decode) } m.normalize() @@ -935,7 +935,7 @@ abstract class Inliners extends SubComponent { // add exception handlers of the callee caller addHandlers (inc.handlers map translateExh) assert(pending.isEmpty, "Pending NEW elements: " + pending) - if (settings.debug.value) icodes.checkValid(caller.m) + if (settings.debug) icodes.checkValid(caller.m) } def isStampedForInlining(stackLength: Int): InlineSafetyInfo = { @@ -958,6 +958,7 @@ abstract class Inliners extends SubComponent { if(isInlineForbidden) { rs ::= "is annotated @noinline" } if(inc.isSynchronized) { rs ::= "is synchronized method" } if(inc.m.bytecodeHasEHs) { rs ::= "bytecode contains exception handlers / finally clause" } // SI-6188 + if(inc.m.bytecodeHasInvokeDynamic) { rs ::= "bytecode contains invoke dynamic" } if(rs.isEmpty) null else rs.mkString("", ", and ", "") } diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala index cf40fe90fa..f1b1d1a9a7 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala @@ -755,13 +755,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners { val pos = in.currentPos val name = identForType() val (statics, body) = typeBody(AT, name) - def getValueMethodType(tree: Tree) = tree match { - case DefDef(_, nme.value, _, _, tpt, _) => Some(tpt.duplicate) - case _ => None - } - var templ = makeTemplate(annotationParents, body) - for (stat <- templ.body; tpt <- getValueMethodType(stat)) - templ = makeTemplate(annotationParents, makeConstructor(List(tpt)) :: templ.body) + val templ = makeTemplate(annotationParents, body) addCompanionObject(statics, atPos(pos) { ClassDef(mods, name, List(), templ) }) diff --git a/src/compiler/scala/tools/nsc/plugins/Plugins.scala b/src/compiler/scala/tools/nsc/plugins/Plugins.scala index 71b97e86a6..a591482392 100644 --- a/src/compiler/scala/tools/nsc/plugins/Plugins.scala +++ b/src/compiler/scala/tools/nsc/plugins/Plugins.scala @@ -62,7 +62,7 @@ trait Plugins { def withPlug = plug :: pick(tail, plugNames + plug.name, phaseNames ++ plugPhaseNames) lazy val commonPhases = phaseNames intersect plugPhaseNames - def note(msg: String): Unit = if (settings.verbose.value) inform(msg format plug.name) + def note(msg: String): Unit = if (settings.verbose) inform(msg format plug.name) def fail(msg: String) = { note(msg) ; withoutPlug } if (plugNames contains plug.name) diff --git a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala index 44670ea578..a67c207820 100644 --- a/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/AbstractReporter.scala @@ -43,7 +43,7 @@ abstract class AbstractReporter extends Reporter { if (!hidden || isPromptSet) { severity.count += 1 display(pos, msg, severity) - } else if (settings.debug.value) { + } else if (settings.debug) { severity.count += 1 display(pos, "[ suppressed ] " + msg, severity) } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 61ac07d18f..250feb69bf 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -31,7 +31,7 @@ abstract class SymbolLoaders { } protected def signalError(root: Symbol, ex: Throwable) { - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() globalError(ex.getMessage() match { case null => "i/o error while loading " + root.name case msg => "error while loading " + root.name + ", " + msg @@ -104,8 +104,15 @@ abstract class SymbolLoaders { val clazz = enterClass(root, name, completer) val module = enterModule(root, name, completer) if (!clazz.isAnonymousClass) { - assert(clazz.companionModule == module, module) - assert(module.companionClass == clazz, clazz) + // Diagnostic for SI-7147 + def msg: String = { + def symLocation(sym: Symbol) = if (sym == null) "null" else s"${clazz.fullLocationString} (from ${clazz.associatedFile})" + sm"""Inconsistent class/module symbol pair for `$name` loaded from ${symLocation(root)}. + |clazz = ${symLocation(clazz)}; clazz.companionModule = ${clazz.companionModule} + |module = ${symLocation(module)}; module.companionClass = ${module.companionClass}""" + } + assert(clazz.companionModule == module, msg) + assert(module.companionClass == clazz, msg) } } @@ -136,10 +143,10 @@ abstract class SymbolLoaders { ((classRep.binary, classRep.source) : @unchecked) match { case (Some(bin), Some(src)) if platform.needCompile(bin, src) && !binaryOnly(owner, classRep.name) => - if (settings.verbose.value) inform("[symloader] picked up newer source file for " + src.path) + if (settings.verbose) inform("[symloader] picked up newer source file for " + src.path) global.loaders.enterToplevelsFromSource(owner, classRep.name, src) case (None, Some(src)) => - if (settings.verbose.value) inform("[symloader] no class, picked up source file for " + src.path) + if (settings.verbose) inform("[symloader] no class, picked up source file for " + src.path) global.loaders.enterToplevelsFromSource(owner, classRep.name, src) case (Some(bin), _) => global.loaders.enterClassAndModule(owner, classRep.name, platform.newClassLoader(bin)) diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala index 035244e421..daaa625164 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTrackers.scala @@ -127,7 +127,7 @@ trait SymbolTrackers { else " (" + Flags.flagsToString(masked) + ")" } def symString(sym: Symbol) = ( - if (settings.debug.value && sym.hasCompleteInfo) { + if (settings.debug && sym.hasCompleteInfo) { val s = sym.defString take 240 if (s.length == 240) s + "..." else s } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index d26a61f187..a8a47205dd 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -12,9 +12,11 @@ import java.lang.Integer.toHexString import scala.collection.{ mutable, immutable } import scala.collection.mutable.{ ListBuffer, ArrayBuffer } import scala.annotation.switch +import scala.reflect.internal.{ JavaAccFlags } import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs} import scala.tools.nsc.io.AbstractFile + /** This abstract class implements a class file parser. * * @author Martin Odersky @@ -23,6 +25,7 @@ import scala.tools.nsc.io.AbstractFile abstract class ClassfileParser { val global: Global import global._ + import definitions._ import scala.reflect.internal.ClassfileConstants._ import Flags._ @@ -40,21 +43,41 @@ abstract class ClassfileParser { protected var classTParams = Map[Name,Symbol]() protected var srcfile0 : Option[AbstractFile] = None protected def moduleClass: Symbol = staticModule.moduleClass + private var sawPrivateConstructor = false + + private def ownerForFlags(jflags: JavaAccFlags) = if (jflags.isStatic) moduleClass else clazz def srcfile = srcfile0 - private def currentIsTopLevel = currentClass.toString.indexOf('$') < 0 + private def optimized = global.settings.optimise.value + private def currentIsTopLevel = !(currentClass.decodedName containsChar '$') + + // u1, u2, and u4 are what these data types are called in the JVM spec. + // They are an unsigned byte, unsigned char, and unsigned int respectively. + // We bitmask u1 into an Int to make sure it's 0-255 (and u1 isn't used + // for much beyond tags) but leave u2 alone as it's already unsigned. + protected final def u1(): Int = in.nextByte & 0xFF + protected final def u2(): Char = in.nextChar + protected final def u4(): Int = in.nextInt + + private def readInnerClassFlags() = readClassFlags() + private def readClassFlags() = JavaAccFlags classFlags u2 + private def readMethodFlags() = JavaAccFlags methodFlags u2 + private def readFieldFlags() = JavaAccFlags fieldFlags u2 + private def readTypeName() = readName().toTypeName + private def readName() = pool getName u2 + private def readType() = pool getType u2 private object unpickler extends scala.reflect.internal.pickling.UnPickler { val global: ClassfileParser.this.global.type = ClassfileParser.this.global } private def handleMissing(e: MissingRequirementError) = { - if (settings.debug.value) e.printStackTrace + if (settings.debug) e.printStackTrace throw new IOException("Missing dependency '" + e.req + "', required by " + in.file) } private def handleError(e: Exception) = { - if (settings.debug.value) e.printStackTrace() + if (settings.debug) e.printStackTrace() throw new IOException("class file '%s' is broken\n(%s/%s)".format( in.file, e.getClass, @@ -106,65 +129,60 @@ abstract class ClassfileParser { } private def parseHeader() { - val magic = in.nextInt + val magic = u4 if (magic != JAVA_MAGIC) - throw new IOException("class file '" + in.file + "' " - + "has wrong magic number 0x" + toHexString(magic) - + ", should be 0x" + toHexString(JAVA_MAGIC)) - val minorVersion = in.nextChar.toInt - val majorVersion = in.nextChar.toInt - if ((majorVersion < JAVA_MAJOR_VERSION) || - ((majorVersion == JAVA_MAJOR_VERSION) && - (minorVersion < JAVA_MINOR_VERSION))) - throw new IOException("class file '" + in.file + "' " - + "has unknown version " - + majorVersion + "." + minorVersion - + ", should be at least " - + JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION) + abort(s"class file ${in.file} has wrong magic number 0x${toHexString(magic)}") + + val minor, major = u2 + if (major < JAVA_MAJOR_VERSION || major == JAVA_MAJOR_VERSION && minor < JAVA_MINOR_VERSION) + abort(s"class file ${in.file} has unknown version $major.$minor, should be at least $JAVA_MAJOR_VERSION.$JAVA_MINOR_VERSION") } class ConstantPool { - private val len = in.nextChar - private val starts = new Array[Int](len) - private val values = new Array[AnyRef](len) + private val len = u2 + private val starts = new Array[Int](len) + private val values = new Array[AnyRef](len) private val internalized = new Array[Name](len) { var i = 1 while (i < starts.length) { starts(i) = in.bp i += 1 - (in.nextByte.toInt: @switch) match { - case CONSTANT_UTF8 | CONSTANT_UNICODE => - in.skip(in.nextChar) - case CONSTANT_CLASS | CONSTANT_STRING => - in.skip(2) - case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF - | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => - in.skip(4) - case CONSTANT_LONG | CONSTANT_DOUBLE => - in.skip(8) - i += 1 - case _ => - errorBadTag(in.bp - 1) + (u1.toInt: @switch) match { + case CONSTANT_UTF8 | CONSTANT_UNICODE => in skip u2 + case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE => in skip 2 + case CONSTANT_METHODHANDLE => in skip 3 + case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF => in skip 4 + case CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT => in skip 4 + case CONSTANT_INVOKEDYNAMIC => in skip 4 + case CONSTANT_LONG | CONSTANT_DOUBLE => in skip 8 ; i += 1 + case _ => errorBadTag(in.bp - 1) } } } - /** Return the name found at given index. */ - def getName(index: Int): Name = { - if (index <= 0 || len <= index) - errorBadIndex(index) + def recordAtIndex[T <: AnyRef](value: T, idx: Int): T = { + values(idx) = value + value + } - values(index) match { + def firstExpecting(index: Int, expected: Int): Int = { + val start = starts(index) + val first = in.buf(start).toInt + if (first == expected) start + 1 + else this errorBadTag start + } + + /** Return the name found at given index. */ + def getName(index: Int): Name = ( + if (index <= 0 || len <= index) errorBadIndex(index) + else values(index) match { case name: Name => name - case null => - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val name = newTermName(in.buf, start + 3, in.getChar(start + 1)) - values(index) = name - name + case _ => + val start = firstExpecting(index, CONSTANT_UTF8) + recordAtIndex(newTermName(in.buf, start + 2, in.getChar(start)), index) } - } + ) /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ def getExternalName(index: Int): Name = { @@ -179,28 +197,23 @@ abstract class ClassfileParser { def getClassSymbol(index: Int): Symbol = { if (index <= 0 || len <= index) errorBadIndex(index) - var c = values(index).asInstanceOf[Symbol] - if (c eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (nme.isModuleName(name)) - c = rootMirror.getModuleByName(nme.stripModuleSuffix(name)) - else - c = classNameToSymbol(name) - - values(index) = c + values(index) match { + case sym: Symbol => sym + case _ => + val result = getClassName(index) match { + case name if nme.isModuleName(name) => rootMirror getModuleByName name.dropModule + case name => classNameToSymbol(name) + } + recordAtIndex(result, index) } - c } /** Return the external name of the class info structure found at 'index'. * Use 'getClassSymbol' if the class is sure to be a top-level class. */ def getClassName(index: Int): Name = { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - getExternalName(in.getChar(start + 1)) + val start = firstExpecting(index, CONSTANT_CLASS) + getExternalName(in getChar start) } /** Return the symbol of the class member at `index`. @@ -238,7 +251,7 @@ abstract class ClassfileParser { if (f == NoSymbol) f = rootMirror.getModuleByName(name dropRight 1) } else { - val origName = nme.originalName(name) + val origName = nme.unexpandedName(name) val owner = if (static) ownerTpe.typeSymbol.linkedClassOfClass else ownerTpe.typeSymbol // println("\t" + owner.info.member(name).tpe.widen + " =:= " + tpe) f = owner.info.findMember(origName, 0, 0, stableOnly = false).suchThat(_.tpe.widen =:= tpe) @@ -276,94 +289,66 @@ abstract class ClassfileParser { */ private def getNameAndType(index: Int, ownerTpe: Type): (Name, Type) = { if (index <= 0 || len <= index) errorBadIndex(index) - var p = values(index).asInstanceOf[(Name, Type)] - if (p eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_NAMEANDTYPE) errorBadTag(start) - val name = getName(in.getChar(start + 1).toInt) - // create a dummy symbol for method types - val dummySym = ownerTpe.typeSymbol.newMethod(name.toTermName, ownerTpe.typeSymbol.pos) - var tpe = getType(dummySym, in.getChar(start + 3).toInt) - - // fix the return type, which is blindly set to the class currently parsed - if (name == nme.CONSTRUCTOR) - tpe match { - case MethodType(formals, restpe) => - tpe = MethodType(formals, ownerTpe) + (values(index): @unchecked) match { + case p: ((Name, Type)) => p + case _ => + val start = firstExpecting(index, CONSTANT_NAMEANDTYPE) + val name = getName(in.getChar(start).toInt) + // create a dummy symbol for method types + val dummy = ownerTpe.typeSymbol.newMethod(name.toTermName, ownerTpe.typeSymbol.pos) + val tpe = getType(dummy, in.getChar(start + 2).toInt) + // fix the return type, which is blindly set to the class currently parsed + val restpe = tpe match { + case MethodType(formals, _) if name == nme.CONSTRUCTOR => MethodType(formals, ownerTpe) + case _ => tpe } - - p = (name, tpe) + ((name, restpe)) } - p } /** Return the type of a class constant entry. Since * arrays are considered to be class types, they might * appear as entries in 'newarray' or 'cast' opcodes. */ - def getClassOrArrayType(index: Int): Type = { + def getClassOrArrayType(index: Int): Type = ( if (index <= 0 || len <= index) errorBadIndex(index) - val value = values(index) - var c: Type = null - if (value eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) - val name = getExternalName(in.getChar(start + 1)) - if (name.charAt(0) == ARRAY_TAG) { - c = sigToType(null, name) - values(index) = c - } else { - val sym = classNameToSymbol(name) - /*if (name.endsWith("$")) definitions.getModule(name.subName(0, name.length - 1)) - else if (name.endsWith("$class")) definitions.getModule(name) - else definitions.getClass(name)*/ - values(index) = sym - c = sym.tpe - } - } else c = value match { - case tp: Type => tp - case cls: Symbol => cls.tpe + else values(index) match { + case tp: Type => tp + case cls: Symbol => cls.tpe_* + case _ => + val name = getClassName(index) + name charAt 0 match { + case ARRAY_TAG => recordAtIndex(sigToType(null, name), index) + case _ => recordAtIndex(classNameToSymbol(name), index).tpe_* + } } - c - } - - def getType(index: Int): Type = getType(null, index) - - def getType(sym: Symbol, index: Int): Type = - sigToType(sym, getExternalName(index)) + ) - def getSuperClass(index: Int): Symbol = - if (index == 0) definitions.AnyClass else getClassSymbol(index) + def getType(index: Int): Type = getType(null, index) + def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index)) + def getSuperClass(index: Int): Symbol = if (index == 0) AnyClass else getClassSymbol(index) - def getConstant(index: Int): Constant = { + private def createConstant(index: Int): Constant = { + val start = starts(index) + Constant((in.buf(start).toInt: @switch) match { + case CONSTANT_STRING => getName(in.getChar(start + 1).toInt).toString + case CONSTANT_INTEGER => in.getInt(start + 1) + case CONSTANT_FLOAT => in.getFloat(start + 1) + case CONSTANT_LONG => in.getLong(start + 1) + case CONSTANT_DOUBLE => in.getDouble(start + 1) + case CONSTANT_CLASS => getClassOrArrayType(index).typeSymbol.tpe_* // !!! Is this necessary or desirable? + case _ => errorBadTag(start) + }) + } + def getConstant(index: Int): Constant = ( if (index <= 0 || len <= index) errorBadIndex(index) - var value = values(index) - if (value eq null) { - val start = starts(index) - value = (in.buf(start).toInt: @switch) match { - case CONSTANT_STRING => - Constant(getName(in.getChar(start + 1).toInt).toString) - case CONSTANT_INTEGER => - Constant(in.getInt(start + 1)) - case CONSTANT_FLOAT => - Constant(in.getFloat(start + 1)) - case CONSTANT_LONG => - Constant(in.getLong(start + 1)) - case CONSTANT_DOUBLE => - Constant(in.getDouble(start + 1)) - case CONSTANT_CLASS => - getClassOrArrayType(index).typeSymbol - case _ => - errorBadTag(start) - } - values(index) = value + else values(index) match { + case const: Constant => const + case sym: Symbol => Constant(sym.tpe_*) + case tpe: Type => Constant(tpe) + case _ => recordAtIndex(createConstant(index), index) } - value match { - case ct: Constant => ct - case cls: Symbol => Constant(cls.tpe_*) - case arr: Type => Constant(arr) - } - } + ) private def getSubArray(bytes: Array[Byte]): Array[Byte] = { val decodedLength = ByteCodecs.decode(bytes) @@ -372,46 +357,41 @@ abstract class ClassfileParser { arr } - def getBytes(index: Int): Array[Byte] = { + def getBytes(index: Int): Array[Byte] = ( if (index <= 0 || len <= index) errorBadIndex(index) - var value = values(index).asInstanceOf[Array[Byte]] - if (value eq null) { - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val len = in.getChar(start + 1) - val bytes = new Array[Byte](len) - System.arraycopy(in.buf, start + 3, bytes, 0, len) - value = getSubArray(bytes) - values(index) = value + else values(index) match { + case xs: Array[Byte] => xs + case _ => + val start = firstExpecting(index, CONSTANT_UTF8) + val len = in getChar start + val bytes = new Array[Byte](len) + System.arraycopy(in.buf, start + 2, bytes, 0, len) + recordAtIndex(getSubArray(bytes), index) } - value - } + ) def getBytes(indices: List[Int]): Array[Byte] = { - assert(!indices.isEmpty, indices) - var value = values(indices.head).asInstanceOf[Array[Byte]] - if (value eq null) { - val bytesBuffer = ArrayBuffer.empty[Byte] - for (index <- indices) { - if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index) - val start = starts(index) - if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) - val len = in.getChar(start + 1) - bytesBuffer ++= in.buf.view(start + 3, start + 3 + len) - } - value = getSubArray(bytesBuffer.toArray) - values(indices.head) = value + val head = indices.head + values(head) match { + case xs: Array[Byte] => xs + case _ => + val arr: Array[Byte] = indices.toArray flatMap { index => + if (index <= 0 || ConstantPool.this.len <= index) errorBadIndex(index) + val start = firstExpecting(index, CONSTANT_UTF8) + val len = in getChar start + in.buf drop start + 2 take len + } + recordAtIndex(getSubArray(arr), head) } - value } /** Throws an exception signaling a bad constant index. */ private def errorBadIndex(index: Int) = - throw new RuntimeException("bad constant pool index: " + index + " at pos: " + in.bp) + abort(s"bad constant pool index: $index at pos: ${in.bp}") /** Throws an exception signaling a bad tag at given address. */ private def errorBadTag(start: Int) = - throw new RuntimeException("bad constant pool tag " + in.buf(start) + " at byte " + start) + abort("bad constant pool tag ${in.buf(start)} at byte $start") } /** Try to force the chain of enclosing classes for the given name. Otherwise @@ -435,89 +415,82 @@ abstract class ClassfileParser { sym } - /** Return the class symbol of the given name. */ - def classNameToSymbol(name: Name): Symbol = { - def loadClassSymbol(name: Name): Symbol = { - val file = global.classPath findSourceFile ("" +name) getOrElse { - // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented - // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects - // that are not in their correct place (see bug for details) - if (!settings.isScaladoc) - warning("Class " + name + " not found - continuing with a stub.") - return NoSymbol.newClass(name.toTypeName) - } - val completer = new global.loaders.ClassfileLoader(file) - var owner: Symbol = rootMirror.RootClass - var sym: Symbol = NoSymbol - var ss: Name = null - var start = 0 - var end = name indexOf '.' - - while (end > 0) { - ss = name.subName(start, end) - sym = owner.info.decls lookup ss - if (sym == NoSymbol) { - sym = owner.newPackage(ss.toTermName) setInfo completer - sym.moduleClass setInfo completer - owner.info.decls enter sym - } - owner = sym.moduleClass - start = end + 1 - end = name.indexOf('.', start) - } - ss = name.subName(0, start) - owner.info.decls lookup ss orElse { - sym = owner.newClass(ss.toTypeName) setInfoAndEnter completer - debuglog("loaded "+sym+" from file "+file) - sym + private def loadClassSymbol(name: Name): Symbol = { + val file = global.classPath findSourceFile ("" +name) getOrElse { + // SI-5593 Scaladoc's current strategy is to visit all packages in search of user code that can be documented + // therefore, it will rummage through the classpath triggering errors whenever it encounters package objects + // that are not in their correct place (see bug for details) + if (!settings.isScaladoc) + warning(s"Class $name not found - continuing with a stub.") + return NoSymbol.newClass(name.toTypeName) + } + val completer = new global.loaders.ClassfileLoader(file) + var owner: Symbol = rootMirror.RootClass + var sym: Symbol = NoSymbol + var ss: Name = null + var start = 0 + var end = name indexOf '.' + + while (end > 0) { + ss = name.subName(start, end) + sym = owner.info.decls lookup ss + if (sym == NoSymbol) { + sym = owner.newPackage(ss.toTermName) setInfo completer + sym.moduleClass setInfo completer + owner.info.decls enter sym } + owner = sym.moduleClass + start = end + 1 + end = name.indexOf('.', start) } - - def lookupClass(name: Name) = try { - if (name.pos('.') == name.length) - definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName) - else - rootMirror.getClassByName(name) // see tickets #2464, #3756 - } catch { - case _: FatalError => loadClassSymbol(name) - } - - innerClasses.get(name) match { - case Some(entry) => - //println("found inner class " + name) - val res = innerClasses.classSymbol(entry.externalName) - //println("\trouted to: " + res) - res - case None => - //if (name.toString.contains("$")) println("No inner class: " + name + innerClasses + " while parsing " + in.file.name) - lookupClass(name) + ss = name.subName(0, start) + owner.info.decls lookup ss orElse { + sym = owner.newClass(ss.toTypeName) setInfoAndEnter completer + debuglog("loaded "+sym+" from file "+file) + sym } } - var sawPrivateConstructor = false + /** FIXME - we shouldn't be doing ad hoc lookups in the empty package. + * The method called "getClassByName" should either return the class or not. + */ + private def lookupClass(name: Name) = ( + if (name containsChar '.') + rootMirror getClassByName name // see tickets #2464, #3756 + else + definitions.getMember(rootMirror.EmptyPackageClass, name.toTypeName) + ) + + /** Return the class symbol of the given name. */ + def classNameToSymbol(name: Name): Symbol = { + if (innerClasses contains name) + innerClasses innerSymbol name + else + try lookupClass(name) + catch { case _: FatalError => loadClassSymbol(name) } + } def parseClass() { - val jflags = in.nextChar - val isAnnotation = hasAnnotation(jflags) - val sflags = toScalaClassFlags(jflags) - val nameIdx = in.nextChar - currentClass = pool.getClassName(nameIdx) + val jflags = readClassFlags() + val sflags = jflags.toScalaFlags + val nameIdx = u2 + currentClass = pool.getClassName(nameIdx) /* Parse parents for Java classes. For Scala, return AnyRef, since the real type will be unpickled. * Updates the read pointer of 'in'. */ def parseParents: List[Type] = { if (isScala) { - in.nextChar // skip superclass - val ifaces = in.nextChar - in.bp += ifaces * 2 // .. and iface count interfaces - List(definitions.AnyRefClass.tpe) // dummy superclass, will be replaced by pickled information + u2 // skip superclass + val ifaces = u2 + in.bp += ifaces * 2 // .. and iface count interfaces + List(AnyRefClass.tpe) // dummy superclass, will be replaced by pickled information } else raiseLoaderLevel { - val superType = if (isAnnotation) { in.nextChar; definitions.AnnotationClass.tpe } - else pool.getSuperClass(in.nextChar).tpe_* - val ifaceCount = in.nextChar - var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(in.nextChar).tpe_* - if (isAnnotation) ifaces = definitions.ClassfileAnnotationClass.tpe :: ifaces + val superType = if (jflags.isAnnotation) { u2; AnnotationClass.tpe } + else pool.getSuperClass(u2).tpe_* + val ifaceCount = u2 + var ifaces = for (i <- List.range(0, ifaceCount)) yield pool.getSuperClass(u2).tpe_* + if (jflags.isAnnotation) ifaces ::= ClassfileAnnotationClass.tpe superType :: ifaces } } @@ -547,21 +520,20 @@ abstract class ClassfileParser { skipMembers() // methods if (!isScala) { clazz setFlag sflags - importPrivateWithinFromJavaFlags(clazz, jflags) - importPrivateWithinFromJavaFlags(staticModule, jflags) - clazz.setInfo(classInfo) + propagatePackageBoundary(jflags, clazz, staticModule) + clazz setInfo classInfo moduleClass setInfo staticInfo - staticModule.setInfo(moduleClass.tpe) - staticModule.setFlag(JAVA) - staticModule.moduleClass.setFlag(JAVA) + staticModule setInfo moduleClass.tpe + staticModule setFlag JAVA + staticModule.moduleClass setFlag JAVA // attributes now depend on having infos set already parseAttributes(clazz, classInfo) def queueLoad() { in.bp = curbp - 0 until in.nextChar foreach (_ => parseField()) + 0 until u2 foreach (_ => parseField()) sawPrivateConstructor = false - 0 until in.nextChar foreach (_ => parseMethod()) + 0 until u2 foreach (_ => parseMethod()) val needsConstructor = ( !sawPrivateConstructor && !(instanceScope containsName nme.CONSTRUCTOR) @@ -597,26 +569,28 @@ abstract class ClassfileParser { } def parseField() { - val jflags = in.nextChar - val sflags = toScalaFieldFlags(jflags) - if ((sflags & PRIVATE) != 0L && !global.settings.optimise.value) { + val jflags = readFieldFlags() + val sflags = jflags.toScalaFlags + + if ((sflags & PRIVATE) != 0L && !optimized) { in.skip(4); skipAttributes() } else { - val name = pool.getName(in.nextChar) - val info = pool.getType(in.nextChar) - val sym = getOwner(jflags).newValue(name.toTermName, NoPosition, sflags) - val isEnum = (jflags & JAVA_ACC_ENUM) != 0 + val name = readName() + val info = readType() + val sym = ownerForFlags(jflags).newValue(name.toTermName, NoPosition, sflags) + // Note: the info may be overrwritten later with a generic signature + // parsed from SignatureATTR sym setInfo { - if (isEnum) ConstantType(Constant(sym)) + if (jflags.isEnum) ConstantType(Constant(sym)) else info } - importPrivateWithinFromJavaFlags(sym, jflags) + propagatePackageBoundary(jflags, sym) parseAttributes(sym, info) - getScope(jflags).enter(sym) + getScope(jflags) enter sym // sealed java enums - if (isEnum) { + if (jflags.isEnum) { val enumClass = sym.owner.linkedClassOfClass if (!enumClass.isSealed) enumClass setFlag (SEALED | ABSTRACT) @@ -627,26 +601,27 @@ abstract class ClassfileParser { } def parseMethod() { - val jflags = in.nextChar.toInt - val sflags = toScalaMethodFlags(jflags) - if (isPrivate(jflags) && !global.settings.optimise.value) { - val name = pool.getName(in.nextChar) + val jflags = readMethodFlags() + val sflags = jflags.toScalaFlags + if (jflags.isPrivate && !optimized) { + val name = readName() if (name == nme.CONSTRUCTOR) sawPrivateConstructor = true in.skip(2); skipAttributes() - } else { - if ((sflags & PRIVATE) != 0L && global.settings.optimise.value) { + } + else { + if ((sflags & PRIVATE) != 0L && optimized) { in.skip(4); skipAttributes() } else { - val name = pool.getName(in.nextChar) - val sym = getOwner(jflags).newMethod(name.toTermName, NoPosition, sflags) - var info = pool.getType(sym, (in.nextChar)) + val name = readName() + val sym = ownerForFlags(jflags).newMethod(name.toTermName, NoPosition, sflags) + var info = pool.getType(sym, u2) if (name == nme.CONSTRUCTOR) info match { case MethodType(params, restpe) => // if this is a non-static inner class, remove the explicit outer parameter - val newParams = innerClasses.get(currentClass) match { - case Some(entry) if !isScalaRaw && !isStatic(entry.jflags) => + val newParams = innerClasses getEntry currentClass match { + case Some(entry) if !isScalaRaw && !entry.jflags.isStatic => /* About `clazz.owner.isPackage` below: SI-5957 * For every nested java class A$B, there are two symbols in the scala compiler. * 1. created by SymbolLoader, because of the existence of the A$B.class file, owner: package @@ -661,13 +636,15 @@ abstract class ClassfileParser { } info = MethodType(newParams, clazz.tpe) } - sym.setInfo(info) - importPrivateWithinFromJavaFlags(sym, jflags) + // Note: the info may be overrwritten later with a generic signature + // parsed from SignatureATTR + sym setInfo info + propagatePackageBoundary(jflags, sym) parseAttributes(sym, info) - if ((jflags & JAVA_ACC_VARARGS) != 0) { - sym.setInfo(arrayToRepeated(sym.info)) - } - getScope(jflags).enter(sym) + if (jflags.isVarargs) + sym modifyInfo arrayToRepeated + + getScope(jflags) enter sym } } } @@ -687,15 +664,15 @@ abstract class ClassfileParser { def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean): Type = { val tag = sig.charAt(index); index += 1 tag match { - case BYTE_TAG => definitions.ByteClass.tpe - case CHAR_TAG => definitions.CharClass.tpe - case DOUBLE_TAG => definitions.DoubleClass.tpe - case FLOAT_TAG => definitions.FloatClass.tpe - case INT_TAG => definitions.IntClass.tpe - case LONG_TAG => definitions.LongClass.tpe - case SHORT_TAG => definitions.ShortClass.tpe - case VOID_TAG => definitions.UnitClass.tpe - case BOOL_TAG => definitions.BooleanClass.tpe + case BYTE_TAG => ByteClass.tpe + case CHAR_TAG => CharClass.tpe + case DOUBLE_TAG => DoubleClass.tpe + case FLOAT_TAG => FloatClass.tpe + case INT_TAG => IntClass.tpe + case LONG_TAG => LongClass.tpe + case SHORT_TAG => ShortClass.tpe + case VOID_TAG => UnitClass.tpe + case BOOL_TAG => BooleanClass.tpe case 'L' => def processInner(tp: Type): Type = tp match { case TypeRef(pre, sym, args) if (!sym.isStatic) => @@ -720,7 +697,7 @@ abstract class ClassfileParser { val tp = sig2type(tparams, skiptvs) // sig2type seems to return AnyClass regardless of the situation: // we don't want Any as a LOWER bound. - if (tp.typeSymbol == definitions.AnyClass) TypeBounds.empty + if (tp.typeSymbol == AnyClass) TypeBounds.empty else TypeBounds.lower(tp) case '*' => TypeBounds.empty } @@ -741,9 +718,9 @@ abstract class ClassfileParser { // or we'll create a boatload of needless existentials. else if (classSym.isMonomorphicType || classSym.unsafeTypeParams.isEmpty) tp // raw type - existentially quantify all type parameters - else logResult(s"raw type from $classSym")(definitions.unsafeClassExistentialType(classSym)) + else logResult(s"raw type from $classSym")(unsafeClassExistentialType(classSym)) case tp => - assert(sig.charAt(index) != '<', tp) + assert(sig.charAt(index) != '<', s"sig=$sig, index=$index, tp=$tp") tp } @@ -754,7 +731,9 @@ abstract class ClassfileParser { accept('.') val name = subName(c => c == ';' || c == '<' || c == '.').toTypeName val clazz = tpe.member(name) - tpe = processClassType(processInner(clazz.tpe)) + val dummyArgs = Nil // the actual arguments are added in processClassType + val inner = typeRef(pre = tpe, sym = clazz, args = dummyArgs) + tpe = processClassType(inner) } accept(';') tpe @@ -767,11 +746,11 @@ abstract class ClassfileParser { // NOTE that the comparison to Object only works for abstract types bounded by classes that are strict subclasses of Object // if the bound is exactly Object, it will have been converted to Any, and the comparison will fail // see also RestrictJavaArraysMap (when compiling java sources directly) - if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< definitions.ObjectClass.tpe)) { - elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe)) + if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< ObjectClass.tpe)) { + elemtp = intersectionType(List(elemtp, ObjectClass.tpe)) } - definitions.arrayType(elemtp) + arrayType(elemtp) case '(' => // we need a method symbol. given in line 486 by calling getType(methodSym, ..) assert(sym ne null, sig) @@ -789,7 +768,7 @@ abstract class ClassfileParser { case 'T' => val n = subName(';'.==).toTypeName index += 1 - if (skiptvs) definitions.AnyClass.tpe + if (skiptvs) AnyClass.tpe else tparams(n).typeConstructor } } // sig2type(tparams, skiptvs) @@ -842,27 +821,23 @@ abstract class ClassfileParser { GenPolyType(ownTypeParams, tpe) } // sigToType - class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter { - override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } - } - def parseAttributes(sym: Symbol, symtype: Type) { def convertTo(c: Constant, pt: Type): Constant = { - if (pt.typeSymbol == definitions.BooleanClass && c.tag == IntTag) + if (pt.typeSymbol == BooleanClass && c.tag == IntTag) Constant(c.value != 0) else c convertTo pt } def parseAttribute() { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt + val attrName = readTypeName() + val attrLen = u4 attrName match { case tpnme.SignatureATTR => if (!isScala && !isScalaRaw) { - val sig = pool.getExternalName(in.nextChar) + val sig = pool.getExternalName(u2) val newType = sigToType(sym, sig) sym.setInfo(newType) - if (settings.debug.value && settings.verbose.value) + if (settings.debug && settings.verbose) println("" + sym + "; signature = " + sig + " type = " + newType) } else in.skip(attrLen) @@ -874,10 +849,10 @@ abstract class ClassfileParser { in.skip(attrLen) case tpnme.DeprecatedATTR => val arg = Literal(Constant("see corresponding Javadoc for more information.")) - sym.addAnnotation(definitions.DeprecatedAttr, arg, Literal(Constant(""))) + sym.addAnnotation(DeprecatedAttr, arg, Literal(Constant(""))) in.skip(attrLen) case tpnme.ConstantValueATTR => - val c = pool.getConstant(in.nextChar) + val c = pool.getConstant(u2) val c1 = convertTo(c, symtype) if (c1 ne null) sym.setInfo(ConstantType(c1)) else println("failure to convert " + c + " to " + symtype); //debug @@ -891,7 +866,7 @@ abstract class ClassfileParser { isScalaRaw = true // Attribute on methods of java annotation classes when that method has a default case tpnme.AnnotationDefaultATTR => - sym.addAnnotation(definitions.AnnotationDefaultAttr) + sym.addAnnotation(AnnotationDefaultAttr) in.skip(attrLen) // Java annotations on classes / methods / fields with RetentionPolicy.RUNTIME case tpnme.RuntimeAnnotationATTR => @@ -921,7 +896,7 @@ abstract class ClassfileParser { parseExceptions(attrLen) case tpnme.SourceFileATTR => - val srcfileLeaf = pool.getName(in.nextChar).toString.trim + val srcfileLeaf = readName().toString.trim val srcpath = sym.enclosingPackage match { case NoSymbol => srcfileLeaf case rootMirror.EmptyPackage => srcfileLeaf @@ -934,8 +909,8 @@ abstract class ClassfileParser { } def parseAnnotArg: Option[ClassfileAnnotArg] = { - val tag = in.nextByte.toChar - val index = in.nextChar + val tag = u1 + val index = u2 tag match { case STRING_TAG => Some(LiteralAnnotArg(Constant(pool.getName(index).toString))) @@ -946,7 +921,7 @@ abstract class ClassfileParser { Some(LiteralAnnotArg(Constant(pool.getType(index)))) case ENUM_TAG => val t = pool.getType(index) - val n = pool.getName(in.nextChar) + val n = readName() val s = t.typeSymbol.companionModule.info.decls.lookup(n) assert(s != NoSymbol, t) Some(LiteralAnnotArg(Constant(s))) @@ -966,20 +941,20 @@ abstract class ClassfileParser { } def parseScalaSigBytes: Option[ScalaSigBytes] = { - val tag = in.nextByte.toChar + val tag = u1 assert(tag == STRING_TAG, tag) - Some(ScalaSigBytes(pool getBytes in.nextChar)) + Some(ScalaSigBytes(pool getBytes u2)) } def parseScalaLongSigBytes: Option[ScalaSigBytes] = { - val tag = in.nextByte.toChar + val tag = u1 assert(tag == ARRAY_TAG, tag) - val stringCount = in.nextChar + val stringCount = u2 val entries = for (i <- 0 until stringCount) yield { - val stag = in.nextByte.toChar + val stag = u1 assert(stag == STRING_TAG, stag) - in.nextChar.toInt + u2.toInt } Some(ScalaSigBytes(pool.getBytes(entries.toList))) } @@ -989,20 +964,20 @@ abstract class ClassfileParser { */ def parseAnnotation(attrNameIndex: Char): Option[AnnotationInfo] = try { val attrType = pool.getType(attrNameIndex) - val nargs = in.nextChar + val nargs = u2 val nvpairs = new ListBuffer[(Name, ClassfileAnnotArg)] var hasError = false for (i <- 0 until nargs) { - val name = pool.getName(in.nextChar) + val name = readName() // The "bytes: String" argument of the ScalaSignature attribute is parsed specially so that it is // available as an array of bytes (the pickled Scala signature) instead of as a string. The pickled signature // is encoded as a string because of limitations in the Java class file format. - if ((attrType == definitions.ScalaSignatureAnnotation.tpe) && (name == nme.bytes)) + if ((attrType == ScalaSignatureAnnotation.tpe) && (name == nme.bytes)) parseScalaSigBytes match { case Some(c) => nvpairs += ((name, c)) case None => hasError = true } - else if ((attrType == definitions.ScalaLongSignatureAnnotation.tpe) && (name == nme.bytes)) + else if ((attrType == ScalaLongSignatureAnnotation.tpe) && (name == nme.bytes)) parseScalaLongSigBytes match { case Some(c) => nvpairs += ((name, c)) case None => hasError = true @@ -1025,7 +1000,7 @@ abstract class ClassfileParser { // with a `FatalError` exception, handled above. Here you'd end up after a NPE (for example), // and that should never be swallowed silently. warning("Caught: " + ex + " while parsing annotations in " + in.file) - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() None // ignore malformed annotations } @@ -1035,10 +1010,10 @@ abstract class ClassfileParser { * thrown by a method. */ def parseExceptions(len: Int) { - val nClasses = in.nextChar + val nClasses = u2 for (n <- 0 until nClasses) { // FIXME: this performs an equivalent of getExceptionTypes instead of getGenericExceptionTypes (SI-7065) - val cls = pool.getClassSymbol(in.nextChar.toInt) + val cls = pool.getClassSymbol(u2) // we call initialize due to the fact that we call Symbol.isMonomorphicType in addThrowsAnnotation // and that method requires Symbol to be forced to give the right answers, see SI-7107 for details cls.initialize @@ -1049,13 +1024,13 @@ abstract class ClassfileParser { /* Parse a sequence of annotations and attaches them to the * current symbol sym, except for the ScalaSignature annotation that it returns, if it is available. */ def parseAnnotations(len: Int): Option[AnnotationInfo] = { - val nAttr = in.nextChar + val nAttr = u2 var scalaSigAnnot: Option[AnnotationInfo] = None for (n <- 0 until nAttr) - parseAnnotation(in.nextChar) match { - case Some(scalaSig) if (scalaSig.atp == definitions.ScalaSignatureAnnotation.tpe) => + parseAnnotation(u2) match { + case Some(scalaSig) if (scalaSig.atp == ScalaSignatureAnnotation.tpe) => scalaSigAnnot = Some(scalaSig) - case Some(scalaSig) if (scalaSig.atp == definitions.ScalaLongSignatureAnnotation.tpe) => + case Some(scalaSig) if (scalaSig.atp == ScalaLongSignatureAnnotation.tpe) => scalaSigAnnot = Some(scalaSig) case Some(annot) => sym.addAnnotation(annot) @@ -1065,7 +1040,7 @@ abstract class ClassfileParser { } // begin parseAttributes - for (i <- 0 until in.nextChar) parseAttribute() + for (i <- 0 until u2) parseAttribute() } /** Enter own inner classes in the right scope. It needs the scopes to be set up, @@ -1075,11 +1050,12 @@ abstract class ClassfileParser { def className(name: Name): Name = name.subName(name.lastPos('.') + 1, name.length) - def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) { + def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile) { + def jflags = entry.jflags val completer = new global.loaders.ClassfileLoader(file) val name = entry.originalName - val sflags = toScalaClassFlags(jflags) - val owner = getOwner(jflags) + val sflags = jflags.toScalaFlags + val owner = ownerForFlags(jflags) val scope = getScope(jflags) val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer @@ -1102,13 +1078,13 @@ abstract class ClassfileParser { unlinkIfPresent(cName.toTypeName) } - for (entry <- innerClasses.values) { + for (entry <- innerClasses.entries) { // create a new class member for immediate inner classes if (entry.outerName == currentClass) { val file = global.classPath.findSourceFile(entry.externalName.toString) getOrElse { throw new AssertionError(entry.externalName) } - enterClassAndModule(entry, file, entry.jflags) + enterClassAndModule(entry, file) } } } @@ -1121,10 +1097,10 @@ abstract class ClassfileParser { skipSuperclasses() skipMembers() // fields skipMembers() // methods - val attrs = in.nextChar + val attrs = u2 for (i <- 0 until attrs) { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt + val attrName = readTypeName() + val attrLen = u4 attrName match { case tpnme.SignatureATTR => in.skip(attrLen) @@ -1138,16 +1114,12 @@ abstract class ClassfileParser { case tpnme.ScalaATTR => isScalaRaw = true case tpnme.InnerClassesATTR if !isScala => - val entries = in.nextChar.toInt + val entries = u2 for (i <- 0 until entries) { - val innerIndex = in.nextChar.toInt - val outerIndex = in.nextChar.toInt - val nameIndex = in.nextChar.toInt - val jflags = in.nextChar.toInt - if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) { - val entry = InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) - innerClasses += (pool.getClassName(innerIndex) -> entry) - } + val innerIndex, outerIndex, nameIndex = u2 + val jflags = readInnerClassFlags() + if (innerIndex != 0 && outerIndex != 0 && nameIndex != 0) + innerClasses add InnerClassEntry(innerIndex, outerIndex, nameIndex, jflags) } case _ => in.skip(attrLen) @@ -1157,65 +1129,67 @@ abstract class ClassfileParser { } /** An entry in the InnerClasses attribute of this class file. */ - case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: Int) { + case class InnerClassEntry(external: Int, outer: Int, name: Int, jflags: JavaAccFlags) { def externalName = pool getClassName external def outerName = pool getClassName outer def originalName = pool getName name - - override def toString = - originalName + " in " + outerName + "(" + externalName +")" + def isModule = originalName.isTermName + def scope = if (jflags.isStatic) staticScope else instanceScope + def enclosing = if (jflags.isStatic) enclModule else enclClass + + // The name of the outer class, without its trailing $ if it has one. + private def strippedOuter = nme stripModuleSuffix outerName + private def isInner = innerClasses contains strippedOuter + private def enclClass = if (isInner) innerClasses innerSymbol strippedOuter else classNameToSymbol(strippedOuter) + private def enclModule = enclClass.companionModule } - object innerClasses extends mutable.HashMap[Name, InnerClassEntry] { - /** Return the class symbol for `externalName`. It looks it up in its outer class. - * Forces all outer class symbols to be completed. - * - * If the given name is not an inner class, it returns the symbol found in `definitions`. - */ - def classSymbol(externalName: Name): Symbol = { - /* Return the symbol of `innerName`, having the given `externalName`. */ - def innerSymbol(externalName: Name, innerName: Name, static: Boolean): Symbol = { - def getMember(sym: Symbol, name: Name): Symbol = - if (static) - if (sym == clazz) staticScope.lookup(name) - else sym.companionModule.info.member(name) - else - if (sym == clazz) instanceScope.lookup(name) - else sym.info.member(name) - - innerClasses.get(externalName) match { - case Some(entry) => - val outerName = nme.stripModuleSuffix(entry.outerName) - val sym = classSymbol(outerName) - val s = - // if loading during initialization of `definitions` typerPhase is not yet set. - // in that case we simply load the member at the current phase - if (currentRun.typerPhase != null) - enteringTyper(getMember(sym, innerName.toTypeName)) - else - getMember(sym, innerName.toTypeName) - - assert(s ne NoSymbol, - "" + ((externalName, outerName, innerName, sym.fullLocationString)) + " / " + - " while parsing " + ((in.file, busy)) + - sym + "." + innerName + " linkedModule: " + sym.companionModule + sym.companionModule.info.members - ) - s - - case None => - classNameToSymbol(externalName) - } - } - - get(externalName) match { - case Some(entry) => - innerSymbol(entry.externalName, entry.originalName, isStatic(entry.jflags)) - case None => - classNameToSymbol(externalName) - } + /** Return the class symbol for the given name. It looks it up in its outer class. + * Forces all outer class symbols to be completed. + * + * If the given name is not an inner class, it returns the symbol found in `definitions`. + */ + object innerClasses { + private val inners = mutable.HashMap[Name, InnerClassEntry]() + + def contains(name: Name) = inners contains name + def getEntry(name: Name) = inners get name + def entries = inners.values + + def add(entry: InnerClassEntry): Unit = { + inners get entry.externalName foreach (existing => + debugwarn(s"Overwriting inner class entry! Was $existing, now $entry") + ) + inners(entry.externalName) = entry + } + def innerSymbol(externalName: Name): Symbol = this getEntry externalName match { + case Some(entry) => innerSymbol(entry) + case _ => NoSymbol + } + // if loading during initialization of `definitions` typerPhase is not yet set. + // in that case we simply load the member at the current phase + @inline private def enteringTyperIfPossible(body: => Symbol): Symbol = + if (currentRun.typerPhase eq null) body else enteringTyper(body) + + private def innerSymbol(entry: InnerClassEntry): Symbol = { + val name = entry.originalName.toTypeName + val enclosing = entry.enclosing + def getMember = ( + if (enclosing == clazz) entry.scope lookup name + else enclosing.info member name + ) + enteringTyperIfPossible(getMember) + /** There used to be an assertion that this result is not NoSymbol; changing it to an error + * revealed it had been going off all the time, but has been swallowed by a catch t: Throwable + * in Repository.scala. Since it has been accomplishing nothing except misleading anyone who + * thought it wasn't triggering, I removed it entirely. + */ } } + class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter { + override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } + } class LazyAliasType(alias: Symbol) extends LazyType with FlagAgnosticCompleter { override def complete(sym: Symbol) { sym setInfo createFromClonedSymbols(alias.initialize.typeParams, alias.tpe)(typeFun) @@ -1223,16 +1197,16 @@ abstract class ClassfileParser { } def skipAttributes() { - var attrCount: Int = in.nextChar + var attrCount: Int = u2 while (attrCount > 0) { in skip 2 - in skip in.nextInt + in skip u4 attrCount -= 1 } } def skipMembers() { - var memberCount: Int = in.nextChar + var memberCount: Int = u2 while (memberCount > 0) { in skip 6 skipAttributes() @@ -1242,17 +1216,10 @@ abstract class ClassfileParser { def skipSuperclasses() { in.skip(2) // superclass - val ifaces = in.nextChar + val ifaces = u2 in.skip(2 * ifaces) } - protected def getOwner(flags: Int): Symbol = - if (isStatic(flags)) moduleClass else clazz - - protected def getScope(flags: Int): Scope = - if (isStatic(flags)) staticScope else instanceScope - - private def isPrivate(flags: Int) = (flags & JAVA_ACC_PRIVATE) != 0 - private def isStatic(flags: Int) = (flags & JAVA_ACC_STATIC) != 0 - private def hasAnnotation(flags: Int) = (flags & JAVA_ACC_ANNOTATION) != 0 + protected def getScope(flags: JavaAccFlags): Scope = + if (flags.isStatic) staticScope else instanceScope } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala index 86f034223d..50487ad123 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala @@ -10,6 +10,7 @@ package classfile import scala.collection.{ mutable, immutable } import mutable.ListBuffer import ClassfileConstants._ +import scala.reflect.internal.JavaAccFlags /** ICode reader from Java bytecode. * @@ -45,26 +46,19 @@ abstract class ICodeReader extends ClassfileParser { (staticCode, instanceCode) } - /** If we're parsing a scala module, the owner of members is always - * the module symbol. - */ - override def getOwner(jflags: Int): Symbol = - if (isScalaModule) this.staticModule - else super.getOwner(jflags) - override def parseClass() { this.instanceCode = new IClass(clazz) this.staticCode = new IClass(staticModule) - in.nextChar - pool getClassSymbol in.nextChar + u2 + pool getClassSymbol u2 parseInnerClasses() in.skip(2) // super class - in.skip(2 * in.nextChar) // interfaces - val fieldCount = in.nextChar + in.skip(2 * u2) // interfaces + val fieldCount = u2 for (i <- 0 until fieldCount) parseField() - val methodCount = in.nextChar + val methodCount = u2 for (i <- 0 until methodCount) parseMethod() instanceCode.methods = instanceCode.methods.reverse staticCode.methods = staticCode.methods.reverse @@ -76,25 +70,31 @@ abstract class ICodeReader extends ClassfileParser { skipAttributes() } - private def parseMember(field: Boolean): (Int, Symbol) = { - val jflags = in.nextChar - val name = pool getName in.nextChar - val owner = getOwner(jflags) - val dummySym = owner.newMethod(name.toTermName, owner.pos, toScalaMethodFlags(jflags)) + private def parseMember(field: Boolean): (JavaAccFlags, Symbol) = { + val jflags = JavaAccFlags(u2) + val name = pool getName u2 + /** If we're parsing a scala module, the owner of members is always + * the module symbol. + */ + val owner = ( + if (isScalaModule) staticModule + else if (jflags.isStatic) moduleClass + else clazz + ) + val dummySym = owner.newMethod(name.toTermName, owner.pos, jflags.toScalaFlags) try { - val ch = in.nextChar + val ch = u2 val tpe = pool.getType(dummySym, ch) if ("<clinit>" == name.toString) (jflags, NoSymbol) else { - val owner = getOwner(jflags) var sym = owner.info.findMember(name, 0, 0, stableOnly = false).suchThat(old => sameType(old.tpe, tpe)) if (sym == NoSymbol) sym = owner.info.findMember(newTermName(name + nme.LOCAL_SUFFIX_STRING), 0, 0, stableOnly = false).suchThat(_.tpe =:= tpe) if (sym == NoSymbol) { - sym = if (field) owner.newValue(name.toTermName, owner.pos, toScalaFieldFlags(jflags)) else dummySym + sym = if (field) owner.newValue(name.toTermName, owner.pos, jflags.toScalaFlags) else dummySym sym setInfoAndEnter tpe log(s"ICodeReader could not locate ${name.decode} in $owner. Created ${sym.defString}.") } @@ -126,9 +126,9 @@ abstract class ICodeReader extends ClassfileParser { this.method = new IMethod(sym) this.method.returnType = toTypeKind(sym.tpe.resultType) getCode(jflags).addMethod(this.method) - if ((jflags & JAVA_ACC_NATIVE) != 0) + if (jflags.isNative) this.method.native = true - val attributeCount = in.nextChar + val attributeCount = u2 for (i <- 0 until attributeCount) parseAttribute() } else { debuglog("Skipping non-existent method.") @@ -142,8 +142,8 @@ abstract class ICodeReader extends ClassfileParser { } def parseAttribute() { - val attrName = pool.getName(in.nextChar).toTypeName - val attrLen = in.nextInt + val attrName = pool.getName(u2).toTypeName + val attrLen = u4 attrName match { case tpnme.CodeATTR => parseByteCode() @@ -164,7 +164,7 @@ abstract class ICodeReader extends ClassfileParser { rootMirror.getClassByName(name) } else if (nme.isModuleName(name)) { - val strippedName = nme.stripModuleSuffix(name) + val strippedName = name.dropModule forceMangledName(newTermName(strippedName.decode), module = true) orElse rootMirror.getModuleByName(strippedName) } else { @@ -187,9 +187,9 @@ abstract class ICodeReader extends ClassfileParser { /** Parse java bytecode into ICode */ def parseByteCode() { - maxStack = in.nextChar - maxLocals = in.nextChar - val codeLength = in.nextInt + maxStack = u2 + maxLocals = u2 + val codeLength = u4 val code = new LinearCode def parseInstruction() { @@ -200,7 +200,7 @@ abstract class ICodeReader extends ClassfileParser { /* Parse 16 bit jump target. */ def parseJumpTarget = { size += 2 - val offset = in.nextChar.toShort + val offset = u2.toShort val target = pc + offset assert(target >= 0 && target < codeLength, "Illegal jump target: " + target) target @@ -209,14 +209,13 @@ abstract class ICodeReader extends ClassfileParser { /* Parse 32 bit jump target. */ def parseJumpTargetW: Int = { size += 4 - val offset = in.nextInt + val offset = u4 val target = pc + offset assert(target >= 0 && target < codeLength, "Illegal jump target: " + target + "pc: " + pc + " offset: " + offset) target } - val instr = toUnsignedByte(in.nextByte) - instr match { + u1 match { case JVM.nop => parseInstruction() case JVM.aconst_null => code emit CONSTANT(Constant(null)) case JVM.iconst_m1 => code emit CONSTANT(Constant(-1)) @@ -235,17 +234,17 @@ abstract class ICodeReader extends ClassfileParser { case JVM.dconst_0 => code emit CONSTANT(Constant(0.0)) case JVM.dconst_1 => code emit CONSTANT(Constant(1.0)) - case JVM.bipush => code.emit(CONSTANT(Constant(in.nextByte))); size += 1 - case JVM.sipush => code.emit(CONSTANT(Constant(in.nextChar))); size += 2 - case JVM.ldc => code.emit(CONSTANT(pool.getConstant(toUnsignedByte(in.nextByte)))); size += 1 - case JVM.ldc_w => code.emit(CONSTANT(pool.getConstant(in.nextChar))); size += 2 - case JVM.ldc2_w => code.emit(CONSTANT(pool.getConstant(in.nextChar))); size += 2 - case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, INT))); size += 1 - case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, LONG))); size += 1 - case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, FLOAT))); size += 1 - case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(in.nextByte, DOUBLE))); size += 1 + case JVM.bipush => code.emit(CONSTANT(Constant(u1))); size += 1 + case JVM.sipush => code.emit(CONSTANT(Constant(u2))); size += 2 + case JVM.ldc => code.emit(CONSTANT(pool.getConstant(u1))); size += 1 + case JVM.ldc_w => code.emit(CONSTANT(pool.getConstant(u2))); size += 2 + case JVM.ldc2_w => code.emit(CONSTANT(pool.getConstant(u2))); size += 2 + case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(u1, INT))); size += 1 + case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(u1, LONG))); size += 1 + case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(u1, FLOAT))); size += 1 + case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(u1, DOUBLE))); size += 1 case JVM.aload => - val local = in.nextByte.toInt; size += 1 + val local = u1.toInt; size += 1 if (local == 0 && !method.isStatic) code.emit(THIS(method.symbol.owner)) else @@ -285,11 +284,11 @@ abstract class ICodeReader extends ClassfileParser { case JVM.caload => code.emit(LOAD_ARRAY_ITEM(CHAR)) case JVM.saload => code.emit(LOAD_ARRAY_ITEM(SHORT)) - case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, INT))); size += 1 - case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, LONG))); size += 1 - case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, FLOAT))); size += 1 - case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, DOUBLE))); size += 1 - case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextByte, ObjectReference))); size += 1 + case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(u1, INT))); size += 1 + case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(u1, LONG))); size += 1 + case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(u1, FLOAT))); size += 1 + case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(u1, DOUBLE))); size += 1 + case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(u1, ObjectReference))); size += 1 case JVM.istore_0 => code.emit(STORE_LOCAL(code.getLocal(0, INT))) case JVM.istore_1 => code.emit(STORE_LOCAL(code.getLocal(1, INT))) case JVM.istore_2 => code.emit(STORE_LOCAL(code.getLocal(2, INT))) @@ -373,9 +372,9 @@ abstract class ICodeReader extends ClassfileParser { case JVM.lxor => code.emit(CALL_PRIMITIVE(Logical(XOR, LONG))) case JVM.iinc => size += 2 - val local = code.getLocal(in.nextByte, INT) + val local = code.getLocal(u1, INT) code.emit(LOAD_LOCAL(local)) - code.emit(CONSTANT(Constant(in.nextByte))) + code.emit(CONSTANT(Constant(u1))) code.emit(CALL_PRIMITIVE(Arithmetic(ADD, INT))) code.emit(STORE_LOCAL(local)) @@ -425,14 +424,14 @@ abstract class ICodeReader extends ClassfileParser { size += padding in.bp += padding assert((pc + size % 4) != 0, pc) -/* var byte1 = in.nextByte; size += 1; - while (byte1 == 0) { byte1 = in.nextByte; size += 1; } - val default = byte1 << 24 | in.nextByte << 16 | in.nextByte << 8 | in.nextByte; +/* var byte1 = u1; size += 1; + while (byte1 == 0) { byte1 = u1; size += 1; } + val default = byte1 << 24 | u1 << 16 | u1 << 8 | u1; size = size + 3 */ - val default = pc + in.nextInt; size += 4 - val low = in.nextInt - val high = in.nextInt + val default = pc + u4; size += 4 + val low = u4 + val high = u4 size += 8 assert(low <= high, "Value low not <= high for tableswitch.") @@ -445,13 +444,13 @@ abstract class ICodeReader extends ClassfileParser { size += padding in.bp += padding assert((pc + size % 4) != 0, pc) - val default = pc + in.nextInt; size += 4 - val npairs = in.nextInt; size += 4 + val default = pc + u4; size += 4 + val npairs = u4; size += 4 var tags: List[List[Int]] = Nil var targets: List[Int] = Nil var i = 0 while (i < npairs) { - tags = List(in.nextInt) :: tags; size += 4 + tags = List(u4) :: tags; size += 4 targets = parseJumpTargetW :: targets; // parseJumpTargetW updates 'size' itself i += 1 } @@ -466,47 +465,54 @@ abstract class ICodeReader extends ClassfileParser { case JVM.return_ => code.emit(RETURN(UNIT)) case JVM.getstatic => - val field = pool.getMemberSymbol(in.nextChar, static = true); size += 2 + val field = pool.getMemberSymbol(u2, static = true); size += 2 if (field.hasModuleFlag) code emit LOAD_MODULE(field) else code emit LOAD_FIELD(field, isStatic = true) case JVM.putstatic => - val field = pool.getMemberSymbol(in.nextChar, static = true); size += 2 + val field = pool.getMemberSymbol(u2, static = true); size += 2 code.emit(STORE_FIELD(field, isStatic = true)) case JVM.getfield => - val field = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val field = pool.getMemberSymbol(u2, static = false); size += 2 code.emit(LOAD_FIELD(field, isStatic = false)) case JVM.putfield => - val field = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val field = pool.getMemberSymbol(u2, static = false); size += 2 code.emit(STORE_FIELD(field, isStatic = false)) case JVM.invokevirtual => - val m = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val m = pool.getMemberSymbol(u2, static = false); size += 2 code.emit(CALL_METHOD(m, Dynamic)) case JVM.invokeinterface => - val m = pool.getMemberSymbol(in.nextChar, static = false); size += 4 + val m = pool.getMemberSymbol(u2, static = false); size += 4 in.skip(2) code.emit(CALL_METHOD(m, Dynamic)) case JVM.invokespecial => - val m = pool.getMemberSymbol(in.nextChar, static = false); size += 2 + val m = pool.getMemberSymbol(u2, static = false); size += 2 val style = if (m.name == nme.CONSTRUCTOR || m.isPrivate) Static(onInstance = true) else SuperCall(m.owner.name) code.emit(CALL_METHOD(m, style)) case JVM.invokestatic => - val m = pool.getMemberSymbol(in.nextChar, static = true); size += 2 + val m = pool.getMemberSymbol(u2, static = true); size += 2 if (isBox(m)) code.emit(BOX(toTypeKind(m.info.paramTypes.head))) else if (isUnbox(m)) code.emit(UNBOX(toTypeKind(m.info.resultType))) else code.emit(CALL_METHOD(m, Static(onInstance = false))) + case JVM.invokedynamic => + // TODO, this is just a place holder. A real implementation must parse the class constant entry + debuglog("Found JVM invokedynamic instructionm, inserting place holder ICode INVOKE_DYNAMIC.") + containsInvokeDynamic = true + val poolEntry = in.nextChar + in.skip(2) + code.emit(INVOKE_DYNAMIC(poolEntry)) case JVM.new_ => - code.emit(NEW(REFERENCE(pool.getClassSymbol(in.nextChar)))) + code.emit(NEW(REFERENCE(pool.getClassSymbol(u2)))) size += 2 case JVM.newarray => - val kind = in.nextByte match { + val kind = u1 match { case T_BOOLEAN => BOOL case T_CHAR => CHAR case T_FLOAT => FLOAT @@ -520,35 +526,35 @@ abstract class ICodeReader extends ClassfileParser { code.emit(CREATE_ARRAY(kind, 1)) case JVM.anewarray => - val tpe = pool.getClassOrArrayType(in.nextChar); size += 2 + val tpe = pool.getClassOrArrayType(u2); size += 2 code.emit(CREATE_ARRAY(toTypeKind(tpe), 1)) case JVM.arraylength => code.emit(CALL_PRIMITIVE(ArrayLength(ObjectReference))); // the kind does not matter case JVM.athrow => code.emit(THROW(definitions.ThrowableClass)) case JVM.checkcast => - code.emit(CHECK_CAST(toTypeKind(pool.getClassOrArrayType(in.nextChar)))); size += 2 + code.emit(CHECK_CAST(toTypeKind(pool.getClassOrArrayType(u2)))); size += 2 case JVM.instanceof => - code.emit(IS_INSTANCE(toTypeKind(pool.getClassOrArrayType(in.nextChar)))); size += 2 + code.emit(IS_INSTANCE(toTypeKind(pool.getClassOrArrayType(u2)))); size += 2 case JVM.monitorenter => code.emit(MONITOR_ENTER()) case JVM.monitorexit => code.emit(MONITOR_EXIT()) case JVM.wide => size += 1 - toUnsignedByte(in.nextByte) match { - case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, INT))); size += 2 - case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2 - case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2 - case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2 - case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2 - case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, INT))); size += 2 - case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, LONG))); size += 2 - case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, FLOAT))); size += 2 - case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, DOUBLE))); size += 2 - case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(in.nextChar, ObjectReference))); size += 2 + u1 match { + case JVM.iload => code.emit(LOAD_LOCAL(code.getLocal(u2, INT))); size += 2 + case JVM.lload => code.emit(LOAD_LOCAL(code.getLocal(u2, LONG))); size += 2 + case JVM.fload => code.emit(LOAD_LOCAL(code.getLocal(u2, FLOAT))); size += 2 + case JVM.dload => code.emit(LOAD_LOCAL(code.getLocal(u2, DOUBLE))); size += 2 + case JVM.aload => code.emit(LOAD_LOCAL(code.getLocal(u2, ObjectReference))); size += 2 + case JVM.istore => code.emit(STORE_LOCAL(code.getLocal(u2, INT))); size += 2 + case JVM.lstore => code.emit(STORE_LOCAL(code.getLocal(u2, LONG))); size += 2 + case JVM.fstore => code.emit(STORE_LOCAL(code.getLocal(u2, FLOAT))); size += 2 + case JVM.dstore => code.emit(STORE_LOCAL(code.getLocal(u2, DOUBLE))); size += 2 + case JVM.astore => code.emit(STORE_LOCAL(code.getLocal(u2, ObjectReference))); size += 2 case JVM.ret => sys.error("Cannot handle jsr/ret") case JVM.iinc => size += 4 - val local = code.getLocal(in.nextChar, INT) - code.emit(CONSTANT(Constant(in.nextChar))) + val local = code.getLocal(u2, INT) + code.emit(CONSTANT(Constant(u2))) code.emit(CALL_PRIMITIVE(Arithmetic(ADD, INT))) code.emit(STORE_LOCAL(local)) case _ => sys.error("Invalid 'wide' operand") @@ -556,8 +562,8 @@ abstract class ICodeReader extends ClassfileParser { case JVM.multianewarray => size += 3 - val tpe = toTypeKind(pool getClassOrArrayType in.nextChar) - val dim = in.nextByte + val tpe = toTypeKind(pool getClassOrArrayType u2) + val dim = u1 // assert(dim == 1, "Cannot handle multidimensional arrays yet.") code emit CREATE_ARRAY(tpe, dim) @@ -583,14 +589,14 @@ abstract class ICodeReader extends ClassfileParser { pc = 0 while (pc < codeLength) parseInstruction() - val exceptionEntries = in.nextChar.toInt + val exceptionEntries = u2.toInt code.containsEHs = (exceptionEntries != 0) var i = 0 while (i < exceptionEntries) { // skip start end PC in.skip(4) // read the handler PC - code.jmpTargets += in.nextChar + code.jmpTargets += u2 // skip the exception type in.skip(2) i += 1 @@ -626,10 +632,8 @@ abstract class ICodeReader extends ClassfileParser { /** Return the icode class that should include members with the given flags. * There are two possible classes, the static part and the instance part. */ - def getCode(flags: Int): IClass = - if (isScalaModule) staticCode - else if ((flags & JAVA_ACC_STATIC) != 0) staticCode - else instanceCode + def getCode(flags: JavaAccFlags): IClass = + if (isScalaModule || flags.isStatic) staticCode else instanceCode class LinearCode { val instrs: ListBuffer[(Int, Instruction)] = new ListBuffer @@ -639,6 +643,7 @@ abstract class ICodeReader extends ClassfileParser { var containsDUPX = false var containsNEW = false var containsEHs = false + var containsInvokeDynamic = false def emit(i: Instruction) { instrs += ((pc, i)) @@ -657,6 +662,7 @@ abstract class ICodeReader extends ClassfileParser { val code = new Code(method) method.setCode(code) method.bytecodeHasEHs = containsEHs + method.bytecodeHasInvokeDynamic = containsInvokeDynamic var bb = code.startBlock def makeBasicBlocks: mutable.Map[Int, BasicBlock] = diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 9217bbeeb8..94880c4b2e 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -239,7 +239,7 @@ abstract class Pickler extends SubComponent { putSymbols(tparams) case AnnotatedType(annotations, underlying, selfsym) => putType(underlying) - if (settings.selfInAnnots.value) putSymbol(selfsym) + if (settings.selfInAnnots) putSymbol(selfsym) putAnnotations(annotations filter (_.isStatic)) case _ => throw new FatalError("bad type: " + tp + "(" + tp.getClass + ")") @@ -643,7 +643,7 @@ abstract class Pickler extends SubComponent { annotations filter (_.isStatic) match { case Nil => writeBody(tp) // write the underlying type if there are no annotations case staticAnnots => - if (settings.selfInAnnots.value && selfsym != NoSymbol) + if (settings.selfInAnnots && selfsym != NoSymbol) writeRef(selfsym) writeRef(tp) writeRefs(staticAnnots) diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 3b9cee2d88..ac18e5ba4f 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -85,7 +85,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { def transformApplyDynamic(ad: ApplyDynamic) = { val qual0 = ad.qual val params = ad.args - if (settings.logReflectiveCalls.value) + if (settings.logReflectiveCalls) unit.echo(ad.pos, "method invocation uses reflection") val typedPos = typedWithPos(ad.pos) _ @@ -473,7 +473,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL { /* For testing purposes, the dynamic application's condition * can be printed-out in great detail. Remove? */ - if (settings.debug.value) { + if (settings.debug) { def paramsToString(xs: Any*) = xs map (_.toString) mkString ", " val mstr = ad.symbol.tpe match { case MethodType(mparams, resType) => diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala index 768c9b6989..06367009b6 100644 --- a/src/compiler/scala/tools/nsc/transform/Constructors.scala +++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala @@ -94,8 +94,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { val paramAccessors = clazz.constrParamAccessors // The constructor parameter corresponding to an accessor - def parameter(acc: Symbol): Symbol = - parameterNamed(nme.getterName(acc.originalName.toTermName)) + def parameter(acc: Symbol): Symbol = parameterNamed(acc.unexpandedName.getterName) // The constructor parameter with given name. This means the parameter // has given name, or starts with given name, and continues with a `$` afterwards. @@ -191,8 +190,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { stat match { case ValDef(mods, name, _, _) if (mods hasFlag PRESUPER) => // stat is the constructor-local definition of the field value - val fields = presupers filter ( - vdef => nme.localToGetter(vdef.name) == name) + val fields = presupers filter (_.getterName == name) assert(fields.length == 1) val to = fields.head.symbol if (!to.tpe.isInstanceOf[ConstantType]) @@ -314,10 +312,8 @@ abstract class Constructors extends Transform with ast.TreeDSL { def specializedAssignFor(sym: Symbol): Option[Tree] = specializedStats find { - case Assign(sel @ Select(This(_), _), rhs) => - ( (sel.symbol hasFlag SPECIALIZED) - && (nme.unspecializedName(nme.localToGetter(sel.symbol.name.toTermName)) == nme.localToGetter(sym.name.toTermName)) - ) + case Assign(sel @ Select(This(_), _), _) => + sel.symbol.isSpecialized && (nme.unspecializedName(sel.symbol.getterName) == sym.getterName) case _ => false } @@ -432,8 +428,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { } def addGetter(sym: Symbol): Symbol = { - val getr = addAccessor( - sym, nme.getterName(sym.name.toTermName), getterFlags(sym.flags)) + val getr = addAccessor(sym, sym.getterName, getterFlags(sym.flags)) getr setInfo MethodType(List(), sym.tpe) defBuf += localTyper.typedPos(sym.pos)(DefDef(getr, Select(This(clazz), sym))) getr @@ -441,8 +436,7 @@ abstract class Constructors extends Transform with ast.TreeDSL { def addSetter(sym: Symbol): Symbol = { sym setFlag MUTABLE - val setr = addAccessor( - sym, nme.getterToSetter(nme.getterName(sym.name.toTermName)), setterFlags(sym.flags)) + val setr = addAccessor(sym, sym.setterName, setterFlags(sym.flags)) setr setInfo MethodType(setr.newSyntheticValueParams(List(sym.tpe)), UnitClass.tpe) defBuf += localTyper.typed { //util.trace("adding setter def for "+setr) { diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 857c01c49a..ac79c60254 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -66,8 +66,8 @@ abstract class Erasure extends AddInterfaces } } - override protected def verifyJavaErasure = settings.Xverify.value || settings.debug.value - def needsJavaSig(tp: Type) = !settings.Ynogenericsig.value && NeedsSigCollector.collect(tp) + override protected def verifyJavaErasure = settings.Xverify || settings.debug + def needsJavaSig(tp: Type) = !settings.Ynogenericsig && NeedsSigCollector.collect(tp) // only refer to type params that will actually make it into the sig, this excludes: // * higher-order type parameters @@ -418,7 +418,7 @@ abstract class Erasure extends AddInterfaces |both have erased type ${exitingPostErasure(bridge.tpe)}""") } for (bc <- root.baseClasses) { - if (settings.debug.value) + if (settings.debug) exitingPostErasure(println( sm"""check bridge overrides in $bc |${bc.info.nonPrivateDecl(bridge.name)} @@ -648,7 +648,7 @@ abstract class Erasure extends AddInterfaces * @return the adapted tree */ private def adaptToType(tree: Tree, pt: Type): Tree = { - if (settings.debug.value && pt != WildcardType) + if (settings.debug && pt != WildcardType) log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug if (tree.tpe <:< pt) tree @@ -730,7 +730,7 @@ abstract class Erasure extends AddInterfaces else if (tree.symbol == Any_isInstanceOf) adaptMember(atPos(tree.pos)(Select(qual, Object_isInstanceOf))) else if (tree.symbol.owner == AnyClass) - adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name)))) + adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, tree.symbol.name)))) else { var qual1 = typedQualifier(qual) if ((isPrimitiveValueType(qual1.tpe) && !isPrimitiveValueMember(tree.symbol)) || diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 124dd6c995..367825c251 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -41,26 +41,24 @@ abstract class ExplicitOuter extends InfoTransform private def isInner(clazz: Symbol) = !clazz.isPackageClass && !clazz.outerClass.isStaticOwner - private def haveSameOuter(parent: Type, clazz: Symbol) = parent match { - case TypeRef(pre, sym, _) => - val owner = clazz.owner + private def haveSameOuter(parent: Type, clazz: Symbol) = { + val owner = clazz.owner + val parentSym = parent.typeSymbol - //println(s"have same outer $parent $clazz $sym ${sym.owner} $owner $pre") - - sym.isClass && owner.isClass && - (owner isSubClass sym.owner) && - owner.thisType =:= pre - - case _ => false + parentSym.isClass && owner.isClass && + (owner isSubClass parentSym.owner) && + owner.thisType =:= parent.prefix } /** Does given clazz define an outer field? */ def hasOuterField(clazz: Symbol) = { - val parents = clazz.info.parents + val parent = clazz.info.firstParent - isInner(clazz) && !clazz.isTrait && { - parents.isEmpty || !haveSameOuter(parents.head, clazz) - } + // space optimization: inherit the $outer pointer from the parent class if + // we know that it will point to the correct instance. + def canReuseParentOuterField = !parent.typeSymbol.isJavaDefined && haveSameOuter(parent, clazz) + + isInner(clazz) && !clazz.isTrait && !canReuseParentOuterField } private def outerField(clazz: Symbol): Symbol = { @@ -100,6 +98,29 @@ abstract class ExplicitOuter extends InfoTransform sym setInfo clazz.outerClass.thisType } + /** + * Will the outer accessor of the `clazz` subsume the outer accessor of + * `mixin`? + * + * This arises when an inner object mixes in its companion trait. + * + * {{{ + * class C { + * trait T { C.this } // C$T$$$outer$ : C + * object T extends T { C.this } // C$T$$$outer$ : C.this.type + * } + * }}} + * + * See SI-7242. + }} + */ + private def skipMixinOuterAccessor(clazz: Symbol, mixin: Symbol) = { + // Reliant on the current scheme for name expansion, the expanded name + // of the outer accessors in a trait and its companion object are the same. + // If the assumption is one day falsified, run/t7424.scala will let us know. + clazz.fullName == mixin.fullName + } + /** <p> * The type transformation method: * </p> @@ -162,10 +183,14 @@ abstract class ExplicitOuter extends InfoTransform for (mc <- clazz.mixinClasses) { val mixinOuterAcc: Symbol = exitingExplicitOuter(outerAccessor(mc)) if (mixinOuterAcc != NoSymbol) { - if (decls1 eq decls) decls1 = decls.cloneScope - val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED) - newAcc setInfo (clazz.thisType memberType mixinOuterAcc) - decls1 enter newAcc + if (skipMixinOuterAccessor(clazz, mc)) + debuglog(s"Reusing outer accessor symbol of $clazz for the mixin outer accessor of $mc") + else { + if (decls1 eq decls) decls1 = decls.cloneScope + val newAcc = mixinOuterAcc.cloneSymbol(clazz, mixinOuterAcc.flags & ~DEFERRED) + newAcc setInfo (clazz.thisType memberType mixinOuterAcc) + decls1 enter newAcc + } } } } @@ -370,6 +395,7 @@ abstract class ExplicitOuter extends InfoTransform val outerAcc = outerAccessor(mixinClass) overridingSymbol currentClass def mixinPrefix = (currentClass.thisType baseType mixinClass).prefix assert(outerAcc != NoSymbol, "No outer accessor for inner mixin " + mixinClass + " in " + currentClass) + assert(outerAcc.alternatives.size == 1, s"Multiple outer accessors match inner mixin $mixinClass in $currentClass : ${outerAcc.alternatives.map(_.defString)}") // I added the mixinPrefix.typeArgs.nonEmpty condition to address the // crash in SI-4970. I feel quite sure this can be improved. val path = ( @@ -404,7 +430,7 @@ abstract class ExplicitOuter extends InfoTransform } if (!currentClass.isTrait) for (mc <- currentClass.mixinClasses) - if (outerAccessor(mc) != NoSymbol) + if (outerAccessor(mc) != NoSymbol && !skipMixinOuterAccessor(currentClass, mc)) newDefs += mixinOuterAccessorDef(mc) } } diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index e6c9afb042..69a93b482c 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -183,7 +183,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD if (bmps.isEmpty) rhs else rhs match { case Block(assign, l @ LabelDef(name, params, _)) - if name.toString == ("_" + methSym.name) && isMatch(params) => + if (name string_== "_" + methSym.name) && isMatch(params) => Block(assign, deriveLabelDef(l)(rhs => typed(prependStats(bmps, rhs)))) case _ => prependStats(bmps, rhs) diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index e0b30ab9f9..35df63b246 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -86,7 +86,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * Note: The `checkinit` option does not check if transient fields are initialized. */ private def needsInitFlag(sym: Symbol) = ( - settings.checkInit.value + settings.checkInit && sym.isGetter && !sym.isInitializedToDefault && !sym.hasFlag(PARAMACCESSOR | SPECIALIZED | LAZY) @@ -125,7 +125,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { debuglog("starting rebindsuper " + base + " " + member + ":" + member.tpe + " " + mixinClass + " " + base.info.baseClasses + "/" + bcs) while (!bcs.isEmpty && sym == NoSymbol) { - if (settings.debug.value) { + if (settings.debug) { val other = bcs.head.info.nonPrivateDecl(member.name) debuglog("rebindsuper " + bcs.head + " " + other + " " + other.tpe + " " + other.isDeferred) @@ -185,11 +185,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { newSym updateInfo (mixinMember.info cloneInfo newSym) } - def needsExpandedSetterName(field: Symbol) = !field.isLazy && ( - if (field.isMethod) field.hasStableFlag - else !field.isMutable - ) - /** Add getters and setters for all non-module fields of an implementation * class to its interface unless they are already present. This is done * only once per class. The mixedin flag is used to remember whether late @@ -207,19 +202,19 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // println("creating new getter for "+ field +" : "+ field.info +" at "+ field.locationString+(field hasFlag MUTABLE)) val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED | ( if (field.isMutable) 0 else STABLE ) // TODO preserve pre-erasure info? - clazz.newMethod(nme.getterName(field.name.toTermName), field.pos, newFlags) setInfo MethodType(Nil, field.info) + clazz.newMethod(field.getterName, field.pos, newFlags) setInfo MethodType(Nil, field.info) } /* Create a new setter. Setters are never private or local. They are * always accessors and deferred. */ def newSetter(field: Symbol): Symbol = { //println("creating new setter for "+field+field.locationString+(field hasFlag MUTABLE)) - val setterName = nme.getterToSetter(nme.getterName(field.name.toTermName)) + val setterName = field.setterName val newFlags = field.flags & ~PrivateLocal | ACCESSOR | lateDEFERRED val setter = clazz.newMethod(setterName, field.pos, newFlags) // TODO preserve pre-erasure info? setter setInfo MethodType(setter.newSyntheticValueParams(List(field.info)), UnitClass.tpe) - if (needsExpandedSetterName(field)) + if (field.needsExpandedSetterName) setter.name = nme.expandedSetterName(setter.name, clazz) setter @@ -237,7 +232,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { val getter = member.getter(clazz) if (getter == NoSymbol) addMember(clazz, newGetter(member)) if (!member.tpe.isInstanceOf[ConstantType] && !member.isLazy) { - val setter = member.setter(clazz, needsExpandedSetterName(member)) + val setter = member.setter(clazz) if (setter == NoSymbol) addMember(clazz, newSetter(member)) } } @@ -315,7 +310,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { // carries over the current entry in the type history) val sym = enteringErasure { // so we have a type history entry before erasure - clazz.newValue(nme.getterToLocal(mixinMember.name.toTermName), mixinMember.pos).setInfo(mixinMember.tpe.resultType) + clazz.newValue(mixinMember.localName, mixinMember.pos).setInfo(mixinMember.tpe.resultType) } sym updateInfo mixinMember.tpe.resultType // info at current phase @@ -924,7 +919,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { else if (sym.isConstructor) { deriveDefDef(stat)(addInitBits(clazz, _)) } - else if (settings.checkInit.value && !clazz.isTrait && sym.isSetter) { + else if (settings.checkInit && !clazz.isTrait && sym.isSetter) { val getter = sym.getter(clazz) if (needsInitFlag(getter) && fieldOffset.isDefinedAt(getter)) deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))), UNIT)) @@ -1236,10 +1231,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { case Assign(Apply(lhs @ Select(qual, _), List()), rhs) => // assign to fields in some implementation class via an abstract // setter in the interface. - def setter = lhs.symbol.setter( - toInterface(lhs.symbol.owner.tpe).typeSymbol, - needsExpandedSetterName(lhs.symbol) - ) setPos lhs.pos + def setter = lhs.symbol.setter(toInterface(lhs.symbol.owner.tpe).typeSymbol) setPos lhs.pos typedPos(tree.pos)((qual DOT setter)(rhs)) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 15fe9f0a24..8e008edde2 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -79,7 +79,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ /** For a given class and concrete type arguments, give its specialized class */ - val specializedClass: mutable.Map[(Symbol, TypeEnv), Symbol] = new mutable.LinkedHashMap + val specializedClass = perRunCaches.newMap[(Symbol, TypeEnv), Symbol] /** Map a method symbol to a list of its specialized overloads in the same class. */ private val overloads = perRunCaches.newMap[Symbol, List[Overload]]() withDefaultValue Nil @@ -322,20 +322,20 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Specialize name for the two list of types. The first one denotes * specialization on method type parameters, the second on outer environment. */ - private def specializedName(name: Name, types1: List[Type], types2: List[Type]): TermName = { - if (nme.INITIALIZER == name || (types1.isEmpty && types2.isEmpty)) + private def specializedName(name: Name, types1: List[Type], types2: List[Type]): TermName = ( + if (name == nme.CONSTRUCTOR || (types1.isEmpty && types2.isEmpty)) name.toTermName else if (nme.isSetterName(name)) - nme.getterToSetter(specializedName(nme.setterToGetter(name.toTermName), types1, types2)) + specializedName(name.getterName, types1, types2).setterName else if (nme.isLocalName(name)) - nme.getterToLocal(specializedName(nme.localToGetter(name.toTermName), types1, types2)) + specializedName(name.getterName, types1, types2).localName else { val (base, cs, ms) = nme.splitSpecializedName(name) newTermName(base.toString + "$" + "m" + ms + types1.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "") + "c" + cs + types2.map(t => definitions.abbrvTag(t.typeSymbol)).mkString("", "", "$sp")) } - } + ) lazy val specializableTypes = ScalaValueClasses map (_.tpe) sorted @@ -714,7 +714,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { // debuglog("m: " + m + " isLocal: " + nme.isLocalName(m.name) + " specVal: " + specVal.name + " isLocal: " + nme.isLocalName(specVal.name)) if (nme.isLocalName(m.name)) { - val specGetter = mkAccessor(specVal, nme.localToGetter(specVal.name.toTermName)) setInfo MethodType(Nil, specVal.info) + val specGetter = mkAccessor(specVal, specVal.getterName) setInfo MethodType(Nil, specVal.info) val origGetter = overrideIn(sClass, m.getter(clazz)) info(origGetter) = Forward(specGetter) enterMember(specGetter) @@ -729,7 +729,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { } if (specVal.isVariable && m.setter(clazz) != NoSymbol) { - val specSetter = mkAccessor(specVal, nme.getterToSetter(specGetter.name)) + val specSetter = mkAccessor(specVal, specGetter.setterName) .resetFlag(STABLE) specSetter.setInfo(MethodType(specSetter.newSyntheticValueParams(List(specVal.info)), UnitClass.tpe)) @@ -1156,7 +1156,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { * If it is a 'no-specialization' run, it is applied only to loaded symbols. */ override def transformInfo(sym: Symbol, tpe: Type): Type = { - if (settings.nospecialization.value && currentRun.compiles(sym)) tpe + if (settings.nospecialization && currentRun.compiles(sym)) tpe else tpe.resultType match { case cinfo @ ClassInfoType(parents, decls, clazz) if !unspecializableClass(cinfo) => val tparams = tpe.typeParams @@ -1752,21 +1752,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { /** Create specialized class definitions */ def implSpecClasses(trees: List[Tree]): List[Tree] = { - val buf = new mutable.ListBuffer[Tree] - for (tree <- trees) - tree match { - case ClassDef(_, _, _, impl) => - tree.symbol.info // force specialization - for (((sym1, env), specCls) <- specializedClass if sym1 == tree.symbol) { - val parents = specCls.info.parents.map(TypeTree) - buf += - ClassDef(specCls, atPos(impl.pos)(Template(parents, emptyValDef, List())) - .setSymbol(specCls.newLocalDummy(sym1.pos))) setPos tree.pos - debuglog("created synthetic class: " + specCls + " of " + sym1 + " in " + pp(env)) - } - case _ => - } - buf.toList + trees flatMap { + case tree @ ClassDef(_, _, _, impl) => + tree.symbol.info // force specialization + for (((sym1, env), specCls) <- specializedClass if sym1 == tree.symbol) yield { + debuglog("created synthetic class: " + specCls + " of " + sym1 + " in " + pp(env)) + val parents = specCls.info.parents.map(TypeTree) + ClassDef(specCls, atPos(impl.pos)(Template(parents, emptyValDef, List())) + .setSymbol(specCls.newLocalDummy(sym1.pos))) setPos tree.pos + } + case _ => Nil + } sortBy (_.name.decoded) } } @@ -1805,7 +1801,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ def initializesSpecializedField(f: Symbol) = ( (f.name endsWith nme.SPECIALIZED_SUFFIX) - && clazz.info.member(nme.originalName(f.name)).isPublic + && clazz.info.member(f.unexpandedName).isPublic && clazz.info.decl(f.name).suchThat(_.isGetter) != NoSymbol ) @@ -1836,7 +1832,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { class SpecializationTransformer(unit: CompilationUnit) extends Transformer { informProgress("specializing " + unit) override def transform(tree: Tree) = { - val resultTree = if (settings.nospecialization.value) tree + val resultTree = if (settings.nospecialization) tree else exitingSpecialize(specializeCalls(unit).transform(tree)) // Remove the final modifier and @inline annotation from anything in the diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala index 92ed7fc555..313f968e93 100644 --- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala +++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala @@ -217,7 +217,10 @@ abstract class TailCalls extends Transform { debuglog("Rewriting tail recursive call: " + fun.pos.lineContent.trim) accessed += ctx.label - typedPos(fun.pos)(Apply(Ident(ctx.label), noTailTransform(recv) :: transformArgs)) + typedPos(fun.pos) { + val args = mapWithIndex(transformArgs)((arg, i) => mkAttributedCastHack(arg, ctx.label.info.params(i + 1).tpe)) + Apply(Ident(ctx.label), noTailTransform(recv) :: args) + } } if (!ctx.isEligible) fail("it is neither private nor final so can be overridden") @@ -276,7 +279,7 @@ abstract class TailCalls extends Transform { typedPos(tree.pos)(Block( List(ValDef(newThis, This(currentClass))), - LabelDef(newCtx.label, newThis :: vpSyms, newRHS) + LabelDef(newCtx.label, newThis :: vpSyms, mkAttributedCastHack(newRHS, newCtx.label.tpe.resultType)) )) } else { @@ -373,6 +376,13 @@ abstract class TailCalls extends Transform { super.transform(tree) } } + + // Workaround for SI-6900. Uncurry installs an InfoTransformer and a tree Transformer. + // These leave us with conflicting view on method signatures; the parameter symbols in + // the MethodType can be clones of the ones originally found on the parameter ValDef, and + // consequently appearing in the typechecked RHS of the method. + private def mkAttributedCastHack(tree: Tree, tpe: Type) = + gen.mkAttributedCast(tree, tpe) } // collect the LabelDefs (generated by the pattern matcher) in a DefDef that are in tail position diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index 8fd1df7cea..2f5cb23abb 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -347,10 +347,14 @@ abstract class UnCurry extends InfoTransform } else { log(s"Argument '$arg' at line ${arg.pos.safeLine} is $formal from ${fun.fullName}") + def canUseDirectly(recv: Tree) = ( + recv.tpe.typeSymbol.isSubClass(FunctionClass(0)) + && treeInfo.isExprSafeToInline(recv) + ) arg match { // don't add a thunk for by-name argument if argument already is an application of // a Function0. We can then remove the application and use the existing Function0. - case Apply(Select(recv, nme.apply), Nil) if recv.tpe.typeSymbol isSubClass FunctionClass(0) => + case Apply(Select(recv, nme.apply), Nil) if canUseDirectly(recv) => recv case _ => newFunction0(arg) @@ -424,7 +428,7 @@ abstract class UnCurry extends InfoTransform val result = ( // TODO - settings.noassertions.value temporarily retained to avoid // breakage until a reasonable interface is settled upon. - if ((sym ne null) && (sym.elisionLevel.exists (_ < settings.elidebelow.value || settings.noassertions.value))) + if ((sym ne null) && (sym.elisionLevel.exists (_ < settings.elidebelow.value || settings.noassertions))) replaceElidableTree(tree) else translateSynchronized(tree) match { case dd @ DefDef(mods, name, tparams, _, tpt, rhs) => @@ -628,7 +632,8 @@ abstract class UnCurry extends InfoTransform * * This transformation erases the dependent method types by: * - Widening the formal parameter type to existentially abstract - * over the prior parameters (using `packSymbols`) + * over the prior parameters (using `packSymbols`). This transformation + * is performed in the the `InfoTransform`er [[scala.reflect.internal.transform.UnCurry]]. * - Inserting casts in the method body to cast to the original, * precise type. * @@ -656,15 +661,14 @@ abstract class UnCurry extends InfoTransform */ def erase(dd: DefDef): (List[List[ValDef]], Tree) = { import dd.{ vparamss, rhs } - val vparamSyms = vparamss flatMap (_ map (_.symbol)) - val paramTransforms: List[ParamTransform] = - vparamss.flatten.map { p => - val declaredType = p.symbol.info - // existentially abstract over value parameters - val packedType = typer.packSymbols(vparamSyms, declaredType) - if (packedType =:= declaredType) Identity(p) + map2(vparamss.flatten, dd.symbol.info.paramss.flatten) { (p, infoParam) => + val packedType = infoParam.info + if (packedType =:= p.symbol.info) Identity(p) else { + // The Uncurry info transformer existentially abstracted over value parameters + // from the previous parameter lists. + // Change the type of the param symbol p.symbol updateInfo packedType @@ -676,8 +680,8 @@ abstract class UnCurry extends InfoTransform // the method body to refer to this, rather than the parameter. val tempVal: ValDef = { val tempValName = unit freshTermName (p.name + "$") - val newSym = dd.symbol.newTermSymbol(tempValName, p.pos, SYNTHETIC).setInfo(declaredType) - atPos(p.pos)(ValDef(newSym, gen.mkAttributedCast(Ident(p.symbol), declaredType))) + val newSym = dd.symbol.newTermSymbol(tempValName, p.pos, SYNTHETIC).setInfo(p.symbol.info) + atPos(p.pos)(ValDef(newSym, gen.mkAttributedCast(Ident(p.symbol), p.symbol.info))) } Packed(newParam, tempVal) } @@ -695,13 +699,6 @@ abstract class UnCurry extends InfoTransform Block(tempVals, rhsSubstituted) } - // update the type of the method after uncurry. - dd.symbol updateInfo { - val GenPolyType(tparams, tp) = dd.symbol.info - logResult(s"erased dependent param types for ${dd.symbol.info}") { - GenPolyType(tparams, MethodType(allParams map (_.symbol), tp.finalResultType)) - } - } (allParams :: Nil, rhs1) } } diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 0371df3b10..92b7700c04 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -573,6 +573,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis { assert(tp.isInstanceOf[SingletonType]) val toString = tp match { case ConstantType(c) => c.escapedStringValue + case _ if tp.typeSymbol.isModuleClass => tp.typeSymbol.name.toString case _ => tp.toString } Const.unique(tp, new ValueConst(tp, tp.widen, toString)) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala index 125e9a3b65..31b04d0bd6 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala @@ -15,9 +15,9 @@ import scala.reflect.internal.util.Position /** Optimize and analyze matches based on their TreeMaker-representation. * * The patmat translation doesn't rely on this, so it could be disabled in principle. - * - * TODO: split out match analysis + * - well, not quite: the backend crashes if we emit duplicates in switches (e.g. SI-7290) */ +// TODO: split out match analysis trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { import global._ import global.definitions._ @@ -435,7 +435,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { case SwitchableTreeMaker(pattern) :: GuardAndBodyTreeMakers(guard, body) => Some(CaseDef(pattern, guard, body)) // alternatives - case AlternativesTreeMaker(_, altss, _) :: GuardAndBodyTreeMakers(guard, body) if alternativesSupported => + case AlternativesTreeMaker(_, altss, pos) :: GuardAndBodyTreeMakers(guard, body) if alternativesSupported => val switchableAlts = altss map { case SwitchableTreeMaker(pattern) :: Nil => Some(pattern) @@ -445,7 +445,17 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis { // succeed if they were all switchable sequence(switchableAlts) map { switchableAlts => - CaseDef(Alternative(switchableAlts), guard, body) + def extractConst(t: Tree) = t match { + case Literal(const) => const + case _ => t + } + // SI-7290 Discard duplicate alternatives that would crash the backend + 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(", ")}") + } + CaseDef(Alternative(distinctAlts), guard, body) } case _ => // debug.patmat("can't emit switch for "+ makers) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala index 76268f3ecd..4dff445fe8 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala @@ -522,7 +522,7 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging { debug.patmat("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}"))) val (suppression, requireSwitch): (Suppression, Boolean) = - if (settings.XnoPatmatAnalysis.value) (Suppression.NoSuppression, false) + if (settings.XnoPatmatAnalysis) (Suppression.NoSuppression, false) else scrut match { case Typed(tree, tpt) => val suppressExhaustive = tpt.tpe hasAnnotation UncheckedClass diff --git a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala index 62c584e97b..567d5d0ecd 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Adaptations.scala @@ -66,9 +66,9 @@ trait Adaptations { ) } - if (settings.noAdaptedArgs.value) + if (settings.noAdaptedArgs) adaptWarning("No automatic adaptation here: use explicit parentheses.") - else if (settings.warnAdaptedArgs.value) + else if (settings.warnAdaptedArgs) adaptWarning( if (args.isEmpty) "Adapting argument list by inserting (): " + ( if (isLeakyTarget) "leaky (Object-receiving) target makes this especially dangerous." @@ -77,7 +77,7 @@ trait Adaptations { else "Adapting argument list by creating a " + args.size + "-tuple: this may not be what you want." ) - !settings.noAdaptedArgs.value + !settings.noAdaptedArgs } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 36121f2653..02e1eb6f00 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -97,9 +97,9 @@ trait Analyzer extends AnyRef try { val typer = newTyper(rootContext(unit)) unit.body = typer.typed(unit.body) - if (global.settings.Yrangepos.value && !global.reporter.hasErrors) global.validatePositions(unit.body) + if (global.settings.Yrangepos && !global.reporter.hasErrors) global.validatePositions(unit.body) for (workItem <- unit.toCheck) workItem() - if (settings.lint.value) + if (settings.lint) typer checkUnused unit } finally { diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala index 026f5f7bc8..0686b28079 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala @@ -203,7 +203,7 @@ trait Checkable { private def isEffectivelyFinal(sym: Symbol): Boolean = ( // initialization important sym.initialize.isEffectivelyFinal || ( - settings.future.value && isTupleSymbol(sym) // SI-7294 step into the future and treat TupleN as final. + settings.future && isTupleSymbol(sym) // SI-7294 step into the future and treat TupleN as final. ) ) diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 8723046728..89fc55bc2c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -919,7 +919,7 @@ trait ContextErrors { def NotWithinBounds(tree: Tree, prefix: String, targs: List[Type], tparams: List[Symbol], kindErrors: List[String]) = issueNormalTypeError(tree, - NotWithinBoundsErrorMessage(prefix, targs, tparams, settings.explaintypes.value)) + NotWithinBoundsErrorMessage(prefix, targs, tparams, settings.explaintypes)) //substExpr def PolymorphicExpressionInstantiationError(tree: Tree, undetparams: List[Symbol], pt: Type) = @@ -1058,9 +1058,6 @@ trait ContextErrors { issueSymbolTypeError(currentSym, prevSym.name + " is already defined as " + s2 + s3 + where) } - def MaxParametersCaseClassError(tree: Tree) = - issueNormalTypeError(tree, "Implementation restriction: case classes cannot have more than " + definitions.MaxFunctionArity + " parameters.") - def MissingParameterOrValTypeError(vparam: Tree) = issueNormalTypeError(vparam, "missing parameter type") @@ -1364,7 +1361,7 @@ trait ContextErrors { } def MacroImplTargMismatchError(atargs: List[Type], atparams: List[Symbol]) = - compatibilityError(typer.infer.InferErrorGen.NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes.value)) + compatibilityError(typer.infer.InferErrorGen.NotWithinBoundsErrorMessage("", atargs, atparams, macroDebugVerbose || settings.explaintypes)) def MacroImplTparamInstantiationError(atparams: List[Symbol], ex: NoInstance) = compatibilityError( diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 9d5a9c819c..e89a860e0f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -79,9 +79,9 @@ trait Contexts { self: Analyzer => protected def rootImports(unit: CompilationUnit): List[Symbol] = { assert(definitions.isDefinitionsInitialized, "definitions uninitialized") - if (settings.noimports.value) Nil + if (settings.noimports) Nil else if (unit.isJava) RootImports.javaList - else if (settings.nopredef.value || treeInfo.noPredefImportForUnit(unit.body)) RootImports.javaAndScalaList + else if (settings.nopredef || treeInfo.noPredefImportForUnit(unit.body)) RootImports.javaAndScalaList else RootImports.completeList } @@ -322,7 +322,7 @@ trait Contexts { self: Analyzer => def makeNewImport(imp: Import): Context = { val impInfo = new ImportInfo(imp, depth) - if (settings.lint.value && imp.pos.isDefined) // pos.isDefined excludes java.lang/scala/Predef imports + if (settings.lint && imp.pos.isDefined) // pos.isDefined excludes java.lang/scala/Predef imports allImportInfos(unit) ::= impInfo make(unit, imp, owner, scope, impInfo :: imports) @@ -406,7 +406,7 @@ trait Contexts { self: Analyzer => unit.error(pos, if (checking) "\n**** ERROR DURING INTERNAL CHECKING ****\n" + msg else msg) @inline private def issueCommon(err: AbsTypeError)(pf: PartialFunction[AbsTypeError, Unit]) { - if (settings.Yissuedebug.value) { + if (settings.Yissuedebug) { log("issue error: " + err.errMsg) (new Exception).printStackTrace() } @@ -912,7 +912,25 @@ trait Contexts { self: Analyzer => def lookupImport(imp: ImportInfo, requireExplicit: Boolean) = importedAccessibleSymbol(imp, name, requireExplicit) filter qualifies - while (!impSym.exists && imports.nonEmpty && imp1.depth > symbolDepth) { + // Java: A single-type-import declaration d in a compilation unit c of package p + // that imports a type named n shadows, throughout c, the declarations of: + // + // 1) any top level type named n declared in another compilation unit of p + // + // A type-import-on-demand declaration never causes any other declaration to be shadowed. + // + // Scala: Bindings of different kinds have a precedence defined on them: + // + // 1) Definitions and declarations that are local, inherited, or made available by a + // package clause in the same compilation unit where the definition occurs have + // highest precedence. + // 2) Explicit imports have next highest precedence. + def depthOk(imp: ImportInfo) = ( + imp.depth > symbolDepth + || (unit.isJava && imp.isExplicitImport(name) && imp.depth == symbolDepth) + ) + + while (!impSym.exists && imports.nonEmpty && depthOk(imports.head)) { impSym = lookupImport(imp1, requireExplicit = false) if (!impSym.exists) imports = imports.tail @@ -1047,7 +1065,7 @@ trait Contexts { self: Analyzer => if (result == NoSymbol) selectors = selectors.tail } - if (settings.lint.value && selectors.nonEmpty && result != NoSymbol && pos != NoPosition) + if (settings.lint && selectors.nonEmpty && result != NoSymbol && pos != NoPosition) recordUsage(current, result) // Harden against the fallout from bugs like SI-6745 diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala index 282dd8a99d..7092f00bff 100644 --- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala +++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala @@ -98,6 +98,7 @@ trait EtaExpansion { self: Analyzer => case TypeApply(fn, args) => treeCopy.TypeApply(tree, liftoutPrefix(fn), args).clearType() case Select(qual, name) => + val name = tree.symbol.name // account for renamed imports, SI-7233 treeCopy.Select(tree, liftout(qual, byName = false), name).clearType() setSymbol NoSymbol case Ident(name) => tree @@ -114,7 +115,7 @@ trait EtaExpansion { self: Analyzer => val origTpe = sym.tpe val isRepeated = definitions.isRepeatedParamType(origTpe) // SI-4176 Don't leak A* in eta-expanded function types. See t4176b.scala - val droppedStarTpe = if (settings.etaExpandKeepsStar.value) origTpe else dropIllegalStarTypes(origTpe) + val droppedStarTpe = if (settings.etaExpandKeepsStar) origTpe else dropIllegalStarTypes(origTpe) val valDef = ValDef(Modifiers(SYNTHETIC | PARAM), sym.name.toTermName, TypeTree(droppedStarTpe), EmptyTree) (valDef, isRepeated) } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 8f52088b19..85e31347be 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -298,7 +298,7 @@ trait Implicits { def pos = if (pos0 != NoPosition) pos0 else tree.pos def failure(what: Any, reason: String, pos: Position = this.pos): SearchResult = { - if (settings.XlogImplicits.value) + if (settings.XlogImplicits) reporter.echo(pos, what+" is not a valid implicit value for "+pt+" because:\n"+reason) SearchFailure } @@ -1118,7 +1118,7 @@ trait Implicits { ) // todo. migrate hardcoded materialization in Implicits to corresponding implicit macros val materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), if (prefix != EmptyTree) List(prefix) else List())) - if (settings.XlogImplicits.value) reporter.echo(pos, "materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) + if (settings.XlogImplicits) reporter.echo(pos, "materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) if (context.macrosEnabled) success(materializer) // don't call `failure` here. if macros are disabled, we just fail silently // otherwise -Xlog-implicits will spam the long with zillions of "macros are disabled" @@ -1141,7 +1141,7 @@ trait Implicits { if (args contains EmptyTree) EmptyTree else typedPos(tree.pos.focus) { val mani = gen.mkManifestFactoryCall(full, constructor, tparg, args.toList) - if (settings.debug.value) println("generated manifest: "+mani) // DEBUG + if (settings.debug) println("generated manifest: "+mani) // DEBUG mani } @@ -1316,7 +1316,7 @@ trait Implicits { } } - if (result.isFailure && settings.debug.value) + if (result.isFailure && settings.debug) log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType) result diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 495ac1c086..8d7830897d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -131,7 +131,7 @@ trait Infer extends Checkable { else if (optionArgs.nonEmpty) if (nbSubPats == 1) { val productArity = productArgs.size - if (productArity > 1 && settings.lint.value) + if (productArity > 1 && settings.lint) global.currentUnit.warning(pos, s"extractor pattern binds a single value to a Product${productArity} of type ${optionArgs.head}") optionArgs } @@ -338,7 +338,7 @@ trait Infer extends Checkable { sym1 = sym if (sym1 == NoSymbol) { - if (settings.debug.value) { + if (settings.debug) { Console.println(context) Console.println(tree) Console.println("" + pre + " " + sym.owner + " " + context.owner + " " + context.outer.enclClass.owner + " " + sym.owner.thisType + (pre =:= sym.owner.thisType)) @@ -378,7 +378,7 @@ trait Infer extends Checkable { try pre.memberType(sym1) catch { case ex: MalformedType => - if (settings.debug.value) ex.printStackTrace + if (settings.debug) ex.printStackTrace val sym2 = underlyingSymbol(sym1) val itype = pre.memberType(sym2) ErrorUtils.issueTypeError( diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 5999a64b36..50383a1e78 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -471,7 +471,7 @@ trait MethodSynthesis { } } case class Setter(tree: ValDef) extends DerivedSetter { - def name = nme.getterToSetter(tree.name) + def name = tree.setterName def category = SetterTargetClass def flagsMask = SetterFlags def flagsExtra = ACCESSOR @@ -479,7 +479,7 @@ trait MethodSynthesis { override def derivedSym = basisSym.setter(enclClass) } case class Field(tree: ValDef) extends DerivedFromValDef { - def name = nme.getterToLocal(tree.name) + def name = tree.localName def category = FieldTargetClass def flagsMask = FieldFlags def flagsExtra = PrivateLocal diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index e966cc9060..de3010c371 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -184,7 +184,7 @@ trait Namers extends MethodSynthesis { (newS.owner.isTypeParameter || newS.owner.isAbstractType) // FIXME: name comparisons not successful, are these underscores // sometimes nme.WILDCARD and sometimes tpnme.WILDCARD? - && (newS.name.toString == nme.WILDCARD.toString) + && (newS.name string_== nme.WILDCARD) ) ) @@ -323,7 +323,7 @@ trait Namers extends MethodSynthesis { } } private def createFieldSymbol(tree: ValDef): TermSymbol = - owner.newValue(nme.getterToLocal(tree.name), tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) + owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) private def createImportSymbol(tree: Tree) = NoSymbol.newImport(tree.pos) setInfo completerOf(tree) @@ -523,14 +523,20 @@ trait Namers extends MethodSynthesis { if (from != nme.WILDCARD && base != ErrorType) { if (isValid(from)) { // for Java code importing Scala objects - if (!nme.isModuleName(from) || isValid(nme.stripModuleSuffix(from))) { + if (!nme.isModuleName(from) || isValid(from.dropModule)) { typer.TyperErrorGen.NotAMemberError(tree, expr, from) } } // Setting the position at the import means that if there is // more than one hidden name, the second will not be warned. // So it is the position of the actual hidden name. - checkNotRedundant(tree.pos withPoint fromPos, from, to) + // + // Note: java imports have precence over definitions in the same package + // so don't warn for them. There is a corresponding special treatment + // in the shadowing rules in typedIdent to (SI-7232). In any case, + // we shouldn't be emitting warnings for .java source files. + if (!context.unit.isJava) + checkNotRedundant(tree.pos withPoint fromPos, from, to) } } @@ -654,9 +660,6 @@ trait Namers extends MethodSynthesis { tree.symbol setInfo completerOf(tree) if (mods.isCase) { - if (primaryConstructorArity > MaxFunctionArity) - MaxParametersCaseClassError(tree) - val m = ensureCompanionObject(tree, caseModuleDef) m.moduleClass.updateAttachment(new ClassForCaseCompanionAttachment(tree)) } @@ -669,7 +672,7 @@ trait Namers extends MethodSynthesis { m.updateAttachment(new ConstructorDefaultsAttachment(tree, null)) } val owner = tree.symbol.owner - if (settings.lint.value && owner.isPackageObjectClass && !mods.isImplicit) { + if (settings.lint && owner.isPackageObjectClass && !mods.isImplicit) { context.unit.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." @@ -705,7 +708,7 @@ trait Namers extends MethodSynthesis { // check that lower bound is not an F-bound // but carefully: class Foo[T <: Bar[_ >: T]] should be allowed for (tp1 @ TypeRef(_, sym, _) <- lo) { - if (settings.breakCycles.value) { + if (settings.breakCycles) { if (!sym.maybeInitialize) { log(s"Cycle inspecting $lo for possible f-bounds: ${sym.fullLocationString}") return sym @@ -1373,7 +1376,9 @@ trait Namers extends MethodSynthesis { if (!cdef.symbol.hasAbstractFlag) namer.enterSyntheticSym(caseModuleApplyMeth(cdef)) - namer.enterSyntheticSym(caseModuleUnapplyMeth(cdef)) + val primaryConstructorArity = treeInfo.firstConstructorArgs(cdef.impl.body).size + if (primaryConstructorArity <= MaxTupleArity) + namer.enterSyntheticSym(caseModuleUnapplyMeth(cdef)) } def addCopyMethod(cdef: ClassDef, namer: Namer) { diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala index d5ecb687b0..6921f8ce27 100644 --- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala +++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala @@ -499,7 +499,7 @@ trait NamesDefaults { self: Analyzer => // disable conforms as a view... val errsBefore = reporter.ERROR.count try typer.silent { tpr => - val res = tpr.typed(arg, subst(paramtpe)) + val res = tpr.typed(arg.duplicate, subst(paramtpe)) // better warning for SI-5044: if `silent` was not actually silent give a hint to the user // [H]: the reason why `silent` is not silent is because the cyclic reference exception is // thrown in a context completely different from `context` here. The exception happens while diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 40428272cf..efd4fd804f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -142,7 +142,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } // This has become noisy with implicit classes. - if (settings.lint.value && settings.developer.value) { + if (settings.lint && settings.developer) { clazz.info.decls filter (x => x.isImplicit && x.typeParams.nonEmpty) foreach { sym => val alts = clazz.info.decl(sym.name).alternatives if (alts.size > 1) @@ -307,7 +307,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans infoStringWithLocation(other), infoStringWithLocation(member) ) - else if (settings.debug.value) + else if (settings.debug) analyzer.foundReqMsg(member.tpe, other.tpe) else "" @@ -407,7 +407,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans else if (member.isAnyOverride && (other hasFlag ACCESSOR) && other.accessed.isVariable && !other.accessed.isLazy) { // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here. // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches. - if (!settings.overrideVars.value) + if (!settings.overrideVars) overrideError("cannot override a mutable variable") } else if (member.isAnyOverride && @@ -431,7 +431,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans } else { checkOverrideTypes() checkOverrideDeprecated() - if (settings.warnNullaryOverride.value) { + if (settings.warnNullaryOverride) { if (other.paramss.isEmpty && !member.paramss.isEmpty) { unit.warning(member.pos, "non-nullary method overrides nullary method") } @@ -920,7 +920,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans def apply(tp: Type) = mapOver(tp).normalize } - def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint.value) (fn, args) match { + 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 == Option_apply => unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567 case _ => @@ -1199,7 +1199,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans catch { case ex: TypeError => unit.error(tree0.pos, ex.getMessage()) - if (settings.explaintypes.value) { + if (settings.explaintypes) { val bounds = tparams map (tp => tp.info.instantiateTypeParams(tparams, argtps).bounds) (argtps, bounds).zipped map ((targ, bound) => explainTypes(bound.lo, targ)) (argtps, bounds).zipped map ((targ, bound) => explainTypes(targ, bound.hi)) @@ -1537,9 +1537,9 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) => checkDeprecatedOvers(tree) checkInfiniteLoop(tree.asInstanceOf[ValOrDefDef]) - if (settings.warnNullaryUnit.value) + if (settings.warnNullaryUnit) checkNullaryMethodReturnType(sym) - if (settings.warnInaccessible.value) { + if (settings.warnInaccessible) { if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic) checkAccessibilityOfReferencedTypes(tree) } @@ -1642,7 +1642,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans result } catch { case ex: TypeError => - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() unit.error(tree.pos, ex.getMessage()) tree } finally { diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index c967fed0b9..e22dc73b53 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -91,7 +91,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT if (!found.isErroneous && !req.isErroneous) { val msg = analyzer.ErrorUtils.typeErrorMsg(found, req, typer.infer.isPossiblyMissingArgs(found, req)) typer.context.error(pos, analyzer.withAddendum(pos)(msg)) - if (settings.explaintypes.value) + if (settings.explaintypes) explainTypes(found, req) } } @@ -241,7 +241,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT // also exists in a superclass, because they may be surprised // to find out that a constructor parameter will shadow a // field. See SI-4762. - if (settings.lint.value) { + if (settings.lint) { if (sym.isPrivateLocal && sym.paramss.isEmpty) { qual.symbol.ancestors foreach { parent => parent.info.decls filterNot (x => x.isPrivate || x.hasLocalFlag) foreach { m2 => @@ -256,9 +256,16 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } } - // direct calls to aliases of param accessors to the superclass in order to avoid + + def isAccessibleFromSuper(sym: Symbol) = { + val pre = SuperType(sym.owner.tpe, qual.tpe) + localTyper.context.isAccessible(sym, pre, superAccess = true) + } + + // Direct calls to aliases of param accessors to the superclass in order to avoid // duplicating fields. - if (sym.isParamAccessor && sym.alias != NoSymbol) { + // ... but, only if accessible (SI-6793) + if (sym.isParamAccessor && sym.alias != NoSymbol && isAccessibleFromSuper(sym.alias)) { val result = (localTyper.typedPos(tree.pos) { Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias) }).asInstanceOf[Select] @@ -296,7 +303,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT case Super(_, mix) => if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) { - if (!settings.overrideVars.value) + if (!settings.overrideVars) unit.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") @@ -388,7 +395,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT assert(clazz != NoSymbol, sym) debuglog("Decided for host class: " + clazz) - val accName = nme.protName(sym.originalName) + val accName = nme.protName(sym.unexpandedName) val hasArgs = sym.tpe.paramSectionCount > 0 val memberType = refChecks.toScalaRepeatedParam(sym.tpe) // fix for #2413 @@ -406,7 +413,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT } val protAcc = clazz.info.decl(accName).suchThat(s => s == NoSymbol || s.tpe =:= accType(s)) orElse { - val newAcc = clazz.newMethod(nme.protName(sym.originalName), tree.pos, newFlags = ARTIFACT) + val newAcc = clazz.newMethod(nme.protName(sym.unexpandedName), tree.pos, newFlags = ARTIFACT) newAcc setInfoAndEnter accType(newAcc) val code = DefDef(newAcc, { @@ -466,7 +473,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT assert(clazz != NoSymbol, field) debuglog("Decided for host class: " + clazz) - val accName = nme.protSetterName(field.originalName) + val accName = nme.protSetterName(field.unexpandedName) val protectedAccessor = clazz.info decl accName orElse { val protAcc = clazz.newMethod(accName, field.pos, newFlags = ARTIFACT) val paramTypes = List(clazz.typeOfThis, field.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 5dc422bc1a..c531caa2e8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -93,7 +93,7 @@ trait SyntheticMethods extends ast.TreeDSL { // like Tags and Arrays which are not robust and infer things // which they shouldn't. val accessorLub = ( - if (settings.Xexperimental.value) { + if (settings.Xexperimental) { global.weakLub(accessors map (_.tpe.finalResultType))._1 match { case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents) case tp => tp @@ -336,7 +336,7 @@ trait SyntheticMethods extends ast.TreeDSL { def shouldGenerate(m: Symbol) = { !hasOverridingImplementation(m) || { clazz.isDerivedValueClass && (m == Any_hashCode || m == Any_equals) && { - if (settings.lint.value) { + if (settings.lint) { (clazz.info nonPrivateMember m.name) filter (m => (m.owner != AnyClass) && (m.owner != clazz) && !m.isDeferred) andAlso { m => currentUnit.warning(clazz.pos, s"Implementation of ${m.name} inherited from ${m.owner} overridden in $clazz to enforce value class semantics") } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index dc11c5fe96..1c8d37ef39 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -69,7 +69,7 @@ abstract class TreeCheckers extends Analyzer { // new symbols if (newSyms.nonEmpty) { informFn(newSyms.size + " new symbols.") - val toPrint = if (settings.debug.value) sortedNewSyms mkString " " else "" + val toPrint = if (settings.debug) sortedNewSyms mkString " " else "" newSyms.clear() if (toPrint != "") @@ -120,7 +120,7 @@ abstract class TreeCheckers extends Analyzer { def errorFn(msg: Any): Unit = {hasError = true; println("[check: %s] %s".format(phase.prev, msg))} def errorFn(pos: Position, msg: Any): Unit = errorFn(posstr(pos) + ": " + msg) def informFn(msg: Any) { - if (settings.verbose.value || settings.debug.value) + if (settings.verbose || settings.debug) println("[check: %s] %s".format(phase.prev, msg)) } @@ -137,7 +137,7 @@ abstract class TreeCheckers extends Analyzer { } def checkTrees() { - if (settings.verbose.value) + if (settings.verbose) Console.println("[consistency check at the beginning of phase " + phase + "]") currentRun.units foreach (x => wrap(x)(check(x))) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 46740cd03c..5f45fead91 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -545,7 +545,7 @@ trait TypeDiagnostics { // Error suppression will squash some of these warnings unless we circumvent it. // It is presumed if you are using a -Y option you would really like to hear // the warnings you've requested. - if (settings.warnDeadCode.value && context.unit.exists && treeOK(tree) && exprOK) + if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && exprOK) context.warning(tree.pos, "dead code following this construct", force = true) tree } @@ -585,7 +585,7 @@ trait TypeDiagnostics { // but it seems that throwErrors excludes some of the errors that should actually be // buffered, causing TypeErrors to fly around again. This needs some more investigation. if (!context0.reportErrors) throw ex - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() ex match { case CyclicReference(sym, info: TypeCompleter) => diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala index 65a3fedbd2..eb05486dca 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeStrings.scala @@ -50,7 +50,7 @@ trait StructuredTypeStrings extends DestructureTypes { else block(level, grouping)(name, nodes) } private def shortClass(x: Any) = { - if (settings.debug.value) { + if (settings.debug) { val name = (x.getClass.getName split '.').last val str = if (TypeStrings.isAnonClass(x.getClass)) name else (name split '$').last diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 0e57145343..692c24fd20 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -944,6 +944,57 @@ trait Typers extends Adaptations with Tags { // due to wrapClassTagUnapply, but when we support parameterized extractors, it will become // more common place) val extractor = overloadedExtractorOfObject orElse unapplyMember(tree.tpe) + def convertToCaseConstructor(clazz: Symbol): TypeTree = { + // convert synthetic unapply of case class to case class constructor + val prefix = tree.tpe.prefix + val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) + .setOriginal(tree) + + val skolems = new mutable.ListBuffer[TypeSymbol] + object variantToSkolem extends TypeMap(trackVariance = true) { + def apply(tp: Type) = mapOver(tp) match { + // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 + case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + // must initialize or tpSym.tpe might see random type params!! + // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala + // TODO: why is that?? + tpSym.initialize + val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) + // origin must be the type param so we can deskolemize + val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) + skolems += skolem + skolem.tpe + case tp1 => tp1 + } + } + + // have to open up the existential and put the skolems in scope + // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) + val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? + val freeVars = skolems.toList + + // use "tree" for the context, not context.tree: don't make another CaseDef context, + // as instantiateTypeVar's bounds would end up there + val ctorContext = context.makeNewScope(tree, context.owner) + freeVars foreach ctorContext.scope.enter + newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) + + // simplify types without losing safety, + // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems + val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) + val extrapolated = tree1.tpe match { + case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node + ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type + copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) + case tp => tp + } + + // once the containing CaseDef has been type checked (see typedCase), + // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) + tree1 setType extrapolated + } + if (extractor != NoSymbol) { // if we did some ad-hoc overloading resolution, update the tree's symbol // do not update the symbol if the tree's symbol's type does not define an unapply member @@ -959,59 +1010,16 @@ trait Typers extends Adaptations with Tags { val clazz = unapplyParameterType(unapply) if (unapply.isCase && clazz.isCase) { - // convert synthetic unapply of case class to case class constructor - val prefix = tree.tpe.prefix - val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) - .setOriginal(tree) - - val skolems = new mutable.ListBuffer[TypeSymbol] - object variantToSkolem extends TypeMap(trackVariance = true) { - def apply(tp: Type) = mapOver(tp) match { - // !!! FIXME - skipping this when variance.isInvariant allows unsoundness, see SI-5189 - case TypeRef(NoPrefix, tpSym, Nil) if !variance.isInvariant && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => - // must initialize or tpSym.tpe might see random type params!! - // without this, we'll get very weird types inferred in test/scaladoc/run/SI-5933.scala - // TODO: why is that?? - tpSym.initialize - val bounds = if (variance.isPositive) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) - // origin must be the type param so we can deskolemize - val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?"+tpSym.name), tpSym, bounds) - // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt +" in "+ context.owner +" at "+ context.tree ) - skolems += skolem - skolem.tpe - case tp1 => tp1 - } - } - - // have to open up the existential and put the skolems in scope - // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) - val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? - val freeVars = skolems.toList - - // use "tree" for the context, not context.tree: don't make another CaseDef context, - // as instantiateTypeVar's bounds would end up there - val ctorContext = context.makeNewScope(tree, context.owner) - freeVars foreach ctorContext.scope.enter - newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) - - // simplify types without losing safety, - // so that we get rid of unnecessary type slack, and so that error messages don't unnecessarily refer to skolems - val extrapolate = new ExistentialExtrapolation(freeVars) extrapolate (_: Type) - val extrapolated = tree1.tpe match { - case MethodType(ctorArgs, res) => // ctorArgs are actually in a covariant position, since this is the type of the subpatterns of the pattern represented by this Apply node - ctorArgs foreach (p => p.info = extrapolate(p.info)) // no need to clone, this is OUR method type - copyMethodType(tree1.tpe, ctorArgs, extrapolate(res)) - case tp => tp - } - - // once the containing CaseDef has been type checked (see typedCase), - // tree1's remaining type-slack skolems will be deskolemized (to the method type parameter skolems) - tree1 setType extrapolated + convertToCaseConstructor(clazz) } else { tree } } else { - CaseClassConstructorError(tree) + val clazz = tree.tpe.typeSymbol.linkedClassOfClass + if (clazz.isCase) + convertToCaseConstructor(clazz) + else + CaseClassConstructorError(tree) } } @@ -1133,14 +1141,14 @@ trait Typers extends Adaptations with Tags { // note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially // infinite expansion if pt is constant type () if (sym == UnitClass) { // (12) - if (settings.warnValueDiscard.value) + if (settings.warnValueDiscard) context.unit.warning(tree.pos, "discarded non-Unit value") return typedPos(tree.pos, mode, pt) { Block(List(tree), Literal(Constant(()))) } } else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) { - if (settings.warnNumericWiden.value) + if (settings.warnNumericWiden) context.unit.warning(tree.pos, "implicit numeric widening") return typedPos(tree.pos, mode, pt) { Select(tree, "to" + sym.name) @@ -1159,7 +1167,7 @@ trait Typers extends Adaptations with Tags { val coercion = inferView(tree, tree.tpe, pt, reportAmbiguous = true) if (coercion != EmptyTree) { def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe - if (settings.logImplicitConv.value) + if (settings.logImplicitConv) unit.echo(tree.pos, msg) debuglog(msg) @@ -1170,9 +1178,9 @@ trait Typers extends Adaptations with Tags { } } } - if (settings.debug.value) { + if (settings.debug) { log("error tree = " + tree) - if (settings.explaintypes.value) explainTypes(tree.tpe, pt) + if (settings.explaintypes) explainTypes(tree.tpe, pt) } val found = tree.tpe @@ -1286,7 +1294,7 @@ trait Typers extends Adaptations with Tags { inferView(qual, qual.tpe, searchTemplate, reportAmbiguous, saveErrors) match { case EmptyTree => qual case coercion => - if (settings.logImplicitConv.value) + if (settings.logImplicitConv) unit.echo(qual.pos, "applied implicit conversion from %s to %s = %s".format( qual.tpe, searchTemplate, coercion.symbol.defString)) @@ -1324,8 +1332,7 @@ trait Typers extends Adaptations with Tags { def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = { def onError(reportError: => Tree): Tree = context.tree match { case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty => - ( silent (_.typedArgs(args, mode)) - map (_.asInstanceOf[List[Tree]]) + ( silent (_.typedArgs(args.map(_.duplicate), mode)) filter (xs => !(xs exists (_.isErrorTyped))) map (xs => adaptToArguments(qual, name, xs, WildcardType, reportAmbiguous, saveErrors)) orElse ( _ => reportError) @@ -1745,12 +1752,12 @@ trait Typers extends Adaptations with Tags { if (!(selfType <:< parent.tpe.typeOfThis) && !phase.erasedTypes && !context.owner.isSynthetic && // don't check synthetic concrete classes for virtuals (part of DEVIRTUALIZE) - !settings.noSelfCheck.value && // setting to suppress this very check + !settings.noSelfCheck && // setting to suppress this very check !selfType.isErroneous && !parent.tpe.isErroneous) { pending += ParentSelfTypeConformanceError(parent, selfType) - if (settings.explaintypes.value) explainTypes(selfType, parent.tpe.typeOfThis) + if (settings.explaintypes) explainTypes(selfType, parent.tpe.typeOfThis) } if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError)) @@ -2300,7 +2307,7 @@ trait Typers extends Adaptations with Tags { tdef.symbol.annotations.map(_.completeInfo()) // @specialized should not be pickled when compiling with -no-specialize - if (settings.nospecialization.value && currentRun.compiles(tdef.symbol)) { + if (settings.nospecialization && currentRun.compiles(tdef.symbol)) { tdef.symbol.removeAnnotation(definitions.SpecializedClass) tdef.symbol.deSkolemize.removeAnnotation(definitions.SpecializedClass) } @@ -2492,7 +2499,7 @@ trait Typers extends Adaptations with Tags { // TODO: add fallback __match sentinel to predef val matchStrategy: Tree = - if (!(newPatternMatching && settings.Xexperimental.value && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen + if (!(newPatternMatching && settings.Xexperimental && context.isNameInScope(vpmName._match))) null // fast path, avoiding the next line if there's no __match to be seen else newTyper(context.makeImplicit(reportAmbiguousErrors = false)).silent(_.typed(Ident(vpmName._match), EXPRmode, WildcardType), reportAmbiguousErrors = false) orElse (_ => null) if (matchStrategy ne null) // virtualize @@ -3558,7 +3565,7 @@ trait Typers extends Adaptations with Tags { // If there are dummy type arguments in typeFun part, it suggests we // must type the actual constructor call, not only the select. The value // arguments are how the type arguments will be inferred. - if (targs.isEmpty && typedFun0.exists(t => isDummyAppliedType(t.tpe))) + if (targs.isEmpty && typedFun0.exists(t => t.tpe != null && isDummyAppliedType(t.tpe))) logResult(s"Retyped $typedFun0 to find type args")(typed(argss.foldLeft(fun0)(Apply(_, _)))) else typedFun0 @@ -3671,77 +3678,9 @@ trait Typers extends Adaptations with Tags { }) } - def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? - sym.isTypeParameter && sym.owner.isJavaDefined - - /** If we map a set of hidden symbols to their existential bounds, we - * have a problem: the bounds may themselves contain references to the - * hidden symbols. So this recursively calls existentialBound until - * the typeSymbol is not amongst the symbols being hidden. - */ - def existentialBoundsExcludingHidden(hidden: List[Symbol]): Map[Symbol, Type] = { - def safeBound(t: Type): Type = - if (hidden contains t.typeSymbol) safeBound(t.typeSymbol.existentialBound.bounds.hi) else t - - def hiBound(s: Symbol): Type = safeBound(s.existentialBound.bounds.hi) match { - case tp @ RefinedType(parents, decls) => - val parents1 = parents mapConserve safeBound - if (parents eq parents1) tp - else copyRefinedType(tp, parents1, decls) - case tp => tp - } - - // Hanging onto lower bound in case anything interesting - // happens with it. - mapFrom(hidden)(s => s.existentialBound match { - case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s)) - case _ => hiBound(s) - }) - } - - /** Given a set `rawSyms` of term- and type-symbols, and a type - * `tp`, produce a set of fresh type parameters and a type so that - * it can be abstracted to an existential type. Every type symbol - * `T` in `rawSyms` is mapped to a clone. Every term symbol `x` of - * type `T` in `rawSyms` is given an associated type symbol of the - * following form: - * - * type x.type <: T with Singleton - * - * The name of the type parameter is `x.type`, to produce nice - * diagnostics. The Singleton parent ensures that the type - * parameter is still seen as a stable type. Type symbols in - * rawSyms are fully replaced by the new symbols. Term symbols are - * also replaced, except for term symbols of an Ident tree, where - * only the type of the Ident is changed. - */ - protected def existentialTransform[T](rawSyms: List[Symbol], tp: Type)(creator: (List[Symbol], Type) => T): T = { - val allBounds = existentialBoundsExcludingHidden(rawSyms) - val typeParams: List[Symbol] = rawSyms map { sym => - val name = sym.name match { - case x: TypeName => x - case x => tpnme.singletonName(x) - } - val bound = allBounds(sym) - val sowner = if (isRawParameter(sym)) context.owner else sym.owner - val quantified = sowner.newExistential(name, sym.pos) - - quantified setInfo bound.cloneInfo(quantified) - } - // Higher-kinded existentials are not yet supported, but this is - // tpeHK for when they are: "if a type constructor is expected/allowed, - // tpeHK must be called instead of tpe." - val typeParamTypes = typeParams map (_.tpeHK) - def doSubst(info: Type) = info.subst(rawSyms, typeParamTypes) - - creator(typeParams map (_ modifyInfo doSubst), doSubst(tp)) - } - /** Compute an existential type from raw hidden symbols `syms` and type `tp` */ - def packSymbols(hidden: List[Symbol], tp: Type): Type = - if (hidden.isEmpty) tp - else existentialTransform(hidden, tp)(existentialAbstraction) + def packSymbols(hidden: List[Symbol], tp: Type): Type = global.packSymbols(hidden, tp, Some(context0.owner)) def isReferencedFrom(ctx: Context, sym: Symbol): Boolean = ctx.owner.isTerm && @@ -4085,7 +4024,7 @@ trait Typers extends Adaptations with Tags { if (ann.tpe == null) { // an annotated type val selfsym = - if (!settings.selfInAnnots.value) + if (!settings.selfInAnnots) NoSymbol else arg1.tpe.selfsym orElse { @@ -4211,7 +4150,7 @@ trait Typers extends Adaptations with Tags { if (treeInfo.mayBeVarGetter(varsym)) { lhs1 match { case treeInfo.Applied(Select(qual, name), _, _) => - val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos + val sel = Select(qual, name.setterName) setPos lhs.pos val app = Apply(sel, List(rhs)) setPos tree.pos return typed(app, mode, pt) @@ -4372,6 +4311,12 @@ trait Typers extends Adaptations with Tags { treeCopy.New(tree, tpt1).setType(tp) } + def functionTypeWildcard(tree: Tree, arity: Int): Type = { + val tp = functionType(List.fill(arity)(WildcardType), WildcardType) + if (tp == NoType) MaxFunctionArityError(tree) + tp + } + def typedEta(expr1: Tree): Tree = expr1.tpe match { case TypeRef(_, ByNameParamClass, _) => val expr2 = Function(List(), expr1) setPos expr1.pos @@ -4383,10 +4328,10 @@ trait Typers extends Adaptations with Tags { typed1(expr2, mode, pt) case PolyType(_, MethodType(formals, _)) => if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) + else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) case MethodType(formals, _) => if (isFunctionType(pt)) expr1 - else adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) + else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length)) case ErrorType => expr1 case _ => @@ -4658,7 +4603,7 @@ trait Typers extends Adaptations with Tags { if (isPastTyper) t.tpe match { case OverloadedType(pre, alts) => if (alts forall (s => (s.owner == ObjectClass) || (s.owner == AnyClass) || isPrimitiveValueClass(s.owner))) () - else if (settings.debug.value) printCaller( + else if (settings.debug) printCaller( s"""|Select received overloaded type during $phase, but typer is over. |If this type reaches the backend, we are likely doomed to crash. |$t has these overloads: @@ -4692,11 +4637,13 @@ trait Typers extends Adaptations with Tags { def handleMissing: Tree = { def errorTree = missingSelectErrorTree(tree, qual, name) def asTypeSelection = ( - if (context.owner.enclosingTopLevelClass.isJavaDefined && name.isTypeName) { + if (context.unit.isJava && name.isTypeName) { + // SI-3120 Java uses the same syntax, A.B, to express selection from the + // value A and from the type A. We have to try both. atPos(tree.pos)(gen.convertToSelectFromType(qual, name)) match { case EmptyTree => None case tree1 => Some(typed1(tree1, mode, pt)) - } + } } else None ) @@ -4826,7 +4773,7 @@ trait Typers extends Adaptations with Tags { */ def typedIdent(tree: Tree, name: Name): Tree = { // setting to enable unqualified idents in empty package (used by the repl) - def inEmptyPackage = if (settings.exposeEmptyPackage.value) lookupInEmpty(name) else NoSymbol + def inEmptyPackage = if (settings.exposeEmptyPackage) lookupInEmpty(name) else NoSymbol def issue(err: AbsTypeError) = { // Avoiding some spurious error messages: see SI-2388. @@ -4947,7 +4894,7 @@ trait Typers extends Adaptations with Tags { AppliedTypeNoParametersError(tree, tpt1.tpe) } else { //Console.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}") - if (settings.debug.value) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info)//debug + if (settings.debug) Console.println(tpt1+":"+tpt1.symbol+":"+tpt1.symbol.info)//debug AppliedTypeWrongNumberOfArgsError(tree, tpt1, tparams) } } @@ -4993,6 +4940,13 @@ trait Typers extends Adaptations with Tags { } def typedTry(tree: Try) = { + tree match { + case Try(_, Nil, EmptyTree) => + if (!isPastTyper) context.warning(tree.pos, + "A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.") + case _ => + } + var block1 = typed(tree.block, pt) var catches1 = typedCases(tree.catches, ThrowableClass.tpe, pt) @@ -5143,7 +5097,7 @@ trait Typers extends Adaptations with Tags { def typedLiteral(tree: Literal) = { val value = tree.value // Warn about likely interpolated strings which are missing their interpolators - if (settings.lint.value) value match { + if (settings.lint) value match { case Constant(s: String) => def names = InterpolatorIdentRegex findAllIn s map (n => newTermName(n stripPrefix "$")) val shouldWarn = ( @@ -5325,7 +5279,7 @@ trait Typers extends Adaptations with Tags { reportTypeError(context, tree.pos, ex) setError(tree) case ex: Exception => - if (settings.debug.value) // @M causes cyclic reference error + if (settings.debug) // @M causes cyclic reference error Console.println("exception when typing "+tree+", pt = "+ptPlugins) if (context != null && context.unit.exists && tree != null) logError("AT: " + (tree.pos).dbgString, ex) diff --git a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala index 589e5ce6fd..d55dce70c7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Unapplies.scala @@ -115,11 +115,16 @@ trait Unapplies extends ast.TreeDSL /** The module corresponding to a case class; overrides toString to show the module's name */ def caseModuleDef(cdef: ClassDef): ModuleDef = { - // > MaxFunctionArity is caught in Namers, but for nice error reporting instead of - // an abrupt crash we trim the list here. - def primaries = constrParamss(cdef).head take MaxFunctionArity map (_.tpt) - def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && constrParamss(cdef).length == 1 - def createFun = gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) + val params = constrParamss(cdef) + def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && (params match { + case List(ps) if ps.length <= MaxFunctionArity => true + case _ => false + }) + def createFun = { + def primaries = params.head map (_.tpt) + gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true) + } + def parents = if (inheritFromFun) List(createFun) else Nil def toString = DefDef( Modifiers(OVERRIDE | FINAL | SYNTHETIC), diff --git a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala index 6997dbd402..4e1cf02a6e 100644 --- a/src/compiler/scala/tools/nsc/util/SimpleTracer.scala +++ b/src/compiler/scala/tools/nsc/util/SimpleTracer.scala @@ -10,7 +10,7 @@ import java.io.PrintStream * @param enabled: A condition that must be true for trace info to be produced. */ class SimpleTracer(out: PrintStream, enabled: Boolean = true) { - def apply[T](msg: String)(value: T): T = { + def apply[T](msg: => String)(value: T): T = { if (enabled) out.println(msg+value) value } diff --git a/src/compiler/scala/tools/nsc/util/TreeSet.scala b/src/compiler/scala/tools/nsc/util/TreeSet.scala index 3cdbcc5110..d2e9238e8f 100644 --- a/src/compiler/scala/tools/nsc/util/TreeSet.scala +++ b/src/compiler/scala/tools/nsc/util/TreeSet.scala @@ -40,12 +40,22 @@ class TreeSet[T >: Null <: AnyRef](less: (T, T) => Boolean) extends Set[T] { tree = add(tree) } - def iterator = { - def elems(t: Tree): Iterator[T] = { - if (t eq null) Iterator.empty - else elems(t.l) ++ (Iterator single t.elem) ++ elems(t.r) + def iterator = toList.iterator + + override def foreach[U](f: T => U) { + def loop(t: Tree) { + if (t ne null) { + loop(t.l) + f(t.elem) + loop(t.r) + } } - elems(tree) + loop(tree) + } + override def toList = { + val xs = scala.collection.mutable.ListBuffer[T]() + foreach(xs += _) + xs.toList } override def toString(): String = { diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala index 002a3fce82..4e8f02084d 100644 --- a/src/compiler/scala/tools/reflect/MacroImplementations.scala +++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala @@ -117,7 +117,8 @@ abstract class MacroImplementations { if (!strIsEmpty) { val len = str.length while (idx < len) { - if (str(idx) == '%') { + def notPercentN = str(idx) != '%' || (idx + 1 < len && str(idx + 1) != 'n') + if (str(idx) == '%' && notPercentN) { bldr append (str substring (start, idx)) append "%%" start = idx + 1 } diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 3e227ce8aa..f6ed5f8f1c 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -250,7 +250,7 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => throwIfErrors() val className = mdef.symbol.fullName - if (settings.debug.value) println("generated: "+className) + if (settings.debug) println("generated: "+className) def moduleFileName(className: String) = className + "$" val jclazz = jClass.forName(moduleFileName(className), true, classLoader) val jmeth = jclazz.getDeclaredMethods.find(_.getName == wrapperMethodName).get @@ -349,11 +349,11 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => lazy val exporter = importer.reverse def typeCheck(tree: u.Tree, expectedType: u.Type, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): u.Tree = compiler.withCleanupCaches { - if (compiler.settings.verbose.value) println("importing "+tree+", expectedType = "+expectedType) + if (compiler.settings.verbose) println("importing "+tree+", expectedType = "+expectedType) val ctree: compiler.Tree = importer.importTree(tree) val cexpectedType: compiler.Type = importer.importType(expectedType) - if (compiler.settings.verbose.value) println("typing "+ctree+", expectedType = "+expectedType) + if (compiler.settings.verbose) println("typing "+ctree+", expectedType = "+expectedType) val ttree: compiler.Tree = compiler.typeCheck(ctree, cexpectedType, silent = silent, withImplicitViewsDisabled = withImplicitViewsDisabled, withMacrosDisabled = withMacrosDisabled) val uttree = exporter.importTree(ttree) uttree @@ -369,12 +369,12 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } private def inferImplicit(tree: u.Tree, pt: u.Type, isView: Boolean, silent: Boolean, withMacrosDisabled: Boolean, pos: u.Position): u.Tree = compiler.withCleanupCaches { - if (compiler.settings.verbose.value) println(s"importing pt=$pt, tree=$tree, pos=$pos") + if (compiler.settings.verbose) println(s"importing pt=$pt, tree=$tree, pos=$pos") val ctree: compiler.Tree = importer.importTree(tree) val cpt: compiler.Type = importer.importType(pt) val cpos: compiler.Position = importer.importPosition(pos) - if (compiler.settings.verbose.value) println("inferring implicit %s of type %s, macros = %s".format(if (isView) "view" else "value", pt, !withMacrosDisabled)) + if (compiler.settings.verbose) println("inferring implicit %s of type %s, macros = %s".format(if (isView) "view" else "value", pt, !withMacrosDisabled)) val itree: compiler.Tree = compiler.inferImplicit(ctree, cpt, isView = isView, silent = silent, withMacrosDisabled = withMacrosDisabled, pos = cpos) val uitree = exporter.importTree(itree) uitree @@ -395,17 +395,17 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => } def parse(code: String): u.Tree = { - if (compiler.settings.verbose.value) println("parsing "+code) + if (compiler.settings.verbose) println("parsing "+code) val ctree: compiler.Tree = compiler.parse(code) val utree = exporter.importTree(ctree) utree } def compile(tree: u.Tree): () => Any = { - if (compiler.settings.verbose.value) println("importing "+tree) + if (compiler.settings.verbose) println("importing "+tree) val ctree: compiler.Tree = importer.importTree(tree) - if (compiler.settings.verbose.value) println("compiling "+ctree) + if (compiler.settings.verbose) println("compiling "+ctree) compiler.compile(ctree) } diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index d8e545e6b1..8d65c40a01 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -146,7 +146,7 @@ object PathResolver { import PathResolver.{ Defaults, Environment, ppcp } class PathResolver(settings: Settings, context: JavaContext) { - def this(settings: Settings) = this(settings, if (settings.inline.value) new JavaContext else DefaultJavaContext) + def this(settings: Settings) = this(settings, if (settings.inline) new JavaContext else DefaultJavaContext) private def cmdLineOrElse(name: String, alt: String) = { (commandLineFor(name) match { @@ -240,7 +240,7 @@ class PathResolver(settings: Settings, context: JavaContext) { lazy val result = { val cp = new JavaClassPath(containers.toIndexedSeq, context) - if (settings.Ylogcp.value) { + if (settings.Ylogcp) { Console.println("Classpath built from " + settings.toConciseString) Console.println("Defaults: " + PathResolver.Defaults) Console.println("Calculated: " + Calculated) diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index 099a882f10..dbdb2d02b6 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -547,7 +547,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") for (s <- allSources; if !ignoredFiles(s.file); unit <- getUnit(s)) { try { if (!unit.isUpToDate) - if (unit.problems.isEmpty || !settings.YpresentationStrict.value) + if (unit.problems.isEmpty || !settings.YpresentationStrict) typeCheck(unit) else debugLog("%s has syntax errors. Skipped typechecking".format(unit)) else debugLog("already up to date: "+unit) diff --git a/src/interactive/scala/tools/nsc/interactive/Main.scala b/src/interactive/scala/tools/nsc/interactive/Main.scala index 3b4a36f62d..c838606f02 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.value) { + if (this.settings.Yidedebug) { this.settings.Xprintpos.value = true this.settings.Yrangepos.value = true val compiler = new interactive.Global(this.settings, this.reporter) diff --git a/src/interactive/scala/tools/nsc/interactive/REPL.scala b/src/interactive/scala/tools/nsc/interactive/REPL.scala index 04c06b9357..432400ecd2 100644 --- a/src/interactive/scala/tools/nsc/interactive/REPL.scala +++ b/src/interactive/scala/tools/nsc/interactive/REPL.scala @@ -32,7 +32,7 @@ object REPL { val settings = new Settings(replError) reporter = new ConsoleReporter(settings) val command = new CompilerCommand(args.toList, settings) - if (command.settings.version.value) + if (command.settings.version) reporter.echo(versionMsg) else { try { @@ -50,7 +50,7 @@ object REPL { } } catch { case ex @ FatalError(msg) => - if (true || command.settings.debug.value) // !!! + if (true || command.settings.debug) // !!! ex.printStackTrace() reporter.error(null, "fatal error: " + msg) } diff --git a/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala b/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala index a4a2de9b51..f30d896fb7 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/InteractiveTest.scala @@ -74,7 +74,7 @@ abstract class InteractiveTest /** Test's entry point */ def main(args: Array[String]) { try execute() - finally shutdown() + finally askShutdown() } protected def execute(): Unit = { @@ -110,14 +110,4 @@ abstract class InteractiveTest tester.run() } ****/ - - /** shutdown the presentation compiler. */ - protected def shutdown() { - askShutdown() - - // this is actually needed to force exit on test completion. - // Note: May be a bug on either the testing framework or (less likely) - // the presentation compiler - sys.exit(0) - } } diff --git a/src/interactive/scala/tools/nsc/interactive/tests/Tester.scala b/src/interactive/scala/tools/nsc/interactive/tests/Tester.scala index 9382d5890f..8a47c1df37 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/Tester.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/Tester.scala @@ -17,7 +17,7 @@ class Tester(ntests: Int, inputs: Array[SourceFile], settings: Settings) { val compiler = new Global(settings, reporter) def askAndListen[T, U](msg: String, arg: T, op: (T, Response[U]) => Unit) { - if (settings.verbose.value) print(msg+" "+arg+": ") + if (settings.verbose) print(msg+" "+arg+": ") val TIMEOUT = 10 // ms val limit = System.currentTimeMillis() + randomDelayMillis val res = new Response[U] @@ -28,7 +28,7 @@ class Tester(ntests: Int, inputs: Array[SourceFile], settings: Settings) { } else res.get(TIMEOUT) match { case Some(Left(t)) => /**/ - if (settings.verbose.value) println(t) + if (settings.verbose) println(t) case Some(Right(ex)) => ex.printStackTrace() println(ex) diff --git a/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala b/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala index 9085eb56e6..c0ad245091 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/core/CoreTestDefs.scala @@ -17,7 +17,7 @@ private[tests] trait CoreTestDefs with AskCompletionAt { def memberPrinter(member: compiler.Member): String = - "[accessible: %5s] ".format(member.accessible) + "`" + (member.sym.toString() + member.tpe.toString()).trim() + "`" + "[accessible: %5s] ".format(member.accessible) + "`" + (member.sym.toString.trim + member.tpe.toString()).trim + "`" override def runTest() { askAllSources(CompletionMarker) { pos => @@ -29,7 +29,7 @@ private[tests] trait CoreTestDefs // universal check file that we can provide for this to work reporter.println("retrieved %d members".format(members.size)) compiler ask { () => - val filtered = members.filterNot(member => member.sym.name.toString == "getClass" || member.sym.isConstructor) + val filtered = members.filterNot(member => (member.sym.name string_== "getClass") || member.sym.isConstructor) reporter.println(filtered.map(memberPrinter).sortBy(_.toString()).mkString("\n")) } } diff --git a/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala b/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala index 676feeba8a..40cfc111a1 100644 --- a/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala +++ b/src/interactive/scala/tools/nsc/interactive/tests/core/SourcesCollector.scala @@ -11,7 +11,7 @@ private[tests] object SourcesCollector { * With the default `filter` only .scala and .java files are collected. * */ def apply(base: Path, filter: SourceFilter): Array[SourceFile] = { - assert(base.isDirectory) + assert(base.isDirectory, base + " is not a directory") base.walk.filter(filter).map(source).toList.toArray.sortBy(_.file.name) } diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index c85a4fb6e7..72a23a0dd0 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -161,6 +161,41 @@ object Iterator { def hasNext = true def next = elem } + + /** Avoid stack overflows when applying ++ to lots of iterators by + * flattening the unevaluated iterators out into a vector of closures. + */ + private[scala] final class ConcatIterator[+A](initial: Vector[() => Iterator[A]]) extends Iterator[A] { + // current set to null when all iterators are exhausted + private[this] var current: Iterator[A] = Iterator.empty + private[this] var queue: Vector[() => Iterator[A]] = initial + // Advance current to the next non-empty iterator + private[this] def advance(): Boolean = { + if (queue.isEmpty) { + current = null + false + } + else { + current = queue.head() + queue = queue.tail + current.hasNext || advance() + } + } + def hasNext = (current ne null) && (current.hasNext || advance()) + def next() = if (hasNext) current.next else Iterator.empty.next + + override def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = + new ConcatIterator(queue :+ (() => that.toIterator)) + } + + private[scala] final class JoinIterator[+A](lhs: Iterator[A], that: => GenTraversableOnce[A]) extends Iterator[A] { + private[this] lazy val rhs: Iterator[A] = that.toIterator + def hasNext = lhs.hasNext || rhs.hasNext + def next = if (lhs.hasNext) lhs.next else rhs.next + + override def ++[B >: A](that: => GenTraversableOnce[B]) = + new ConcatIterator(Vector(() => this, () => that.toIterator)) + } } import Iterator.empty @@ -338,24 +373,7 @@ trait Iterator[+A] extends TraversableOnce[A] { * @usecase def ++(that: => Iterator[A]): Iterator[A] * @inheritdoc */ - def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new AbstractIterator[B] { - // optimize a little bit to prevent n log n behavior. - private var cur : Iterator[B] = self - private var selfExhausted : Boolean = false - // since that is by-name, make sure it's only referenced once - - // if "val it = that" is inside the block, then hasNext on an empty - // iterator will continually reevaluate it. (ticket #3269) - lazy val it = that.toIterator - // the eq check is to avoid an infinite loop on "x ++ x" - def hasNext = cur.hasNext || (!selfExhausted && { - it.hasNext && { - cur = it - selfExhausted = true - true - } - }) - def next() = { hasNext; cur.next() } - } + def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] = new Iterator.JoinIterator(self, that) /** Creates a new iterator by applying a function to all values produced by this iterator * and concatenating the results. diff --git a/src/library/scala/collection/parallel/package.scala b/src/library/scala/collection/parallel/package.scala index d91f70da75..66fbad7643 100644 --- a/src/library/scala/collection/parallel/package.scala +++ b/src/library/scala/collection/parallel/package.scala @@ -41,12 +41,7 @@ package object parallel { private[parallel] def outofbounds(idx: Int) = throw new IndexOutOfBoundsException(idx.toString) - private[parallel] def getTaskSupport: TaskSupport = - if (scala.util.Properties.isJavaAtLeast("1.6")) { - val vendor = scala.util.Properties.javaVmVendor - if ((vendor contains "Oracle") || (vendor contains "Sun") || (vendor contains "Apple")) new ForkJoinTaskSupport - else new ThreadPoolTaskSupport - } else new ThreadPoolTaskSupport + private[parallel] def getTaskSupport: TaskSupport = new ForkJoinTaskSupport val defaultTaskSupport: TaskSupport = getTaskSupport diff --git a/src/library/scala/reflect/NameTransformer.scala b/src/library/scala/reflect/NameTransformer.scala index 8a1cce6b02..6192971c74 100755 --- a/src/library/scala/reflect/NameTransformer.scala +++ b/src/library/scala/reflect/NameTransformer.scala @@ -15,9 +15,12 @@ package reflect object NameTransformer { // XXX Short term: providing a way to alter these without having to recompile // the compiler before recompiling the compiler. - val MODULE_SUFFIX_STRING = sys.props.getOrElse("SCALA_MODULE_SUFFIX_STRING", "$") - val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$") - val MODULE_INSTANCE_NAME = "MODULE$" + val MODULE_SUFFIX_STRING = sys.props.getOrElse("SCALA_MODULE_SUFFIX_STRING", "$") + val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$") + val MODULE_INSTANCE_NAME = "MODULE$" + val LOCAL_SUFFIX_STRING = " " + val SETTER_SUFFIX_STRING = "_$eq" + val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" private val nops = 128 private val ncodes = 26 * 26 diff --git a/src/library/scala/reflect/package.scala b/src/library/scala/reflect/package.scala index 10e6d7d9a4..97d7da3f2d 100644 --- a/src/library/scala/reflect/package.scala +++ b/src/library/scala/reflect/package.scala @@ -1,5 +1,7 @@ package scala +import java.lang.reflect.{ AccessibleObject => jAccessibleObject } + package object reflect { // in the new scheme of things ClassManifests are aliased to ClassTags @@ -42,6 +44,18 @@ package object reflect { def classTag[T](implicit ctag: ClassTag[T]) = ctag + /** Make a java reflection object accessible, if it is not already + * and it is possible to do so. If a SecurityException is thrown in the + * attempt, it is caught and discarded. + */ + def ensureAccessible[T <: jAccessibleObject](m: T): T = { + if (!m.isAccessible) { + try m setAccessible true + catch { case _: SecurityException => } // does nothing + } + m + } + // anchor for the class tag materialization macro emitted during tag materialization in Implicits.scala // implementation is hardwired into `scala.reflect.reify.Taggers` // using the mechanism implemented in `scala.tools.reflect.FastTrack` diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 753dd0205e..ea1f392e2b 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -158,13 +158,7 @@ object ScalaRunTime { // Java bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957 // More background at ticket #2318. - def ensureAccessible(m: JMethod): JMethod = { - if (!m.isAccessible) { - try m setAccessible true - catch { case _: SecurityException => () } - } - m - } + def ensureAccessible(m: JMethod): JMethod = scala.reflect.ensureAccessible(m) def checkInitialized[T <: AnyRef](x: T): T = if (x == null) throw new UninitializedError else x diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala index 3f61062073..46e9621b31 100644 --- a/src/partest/scala/tools/partest/DirectTest.scala +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -6,7 +6,6 @@ package scala.tools.partest import scala.tools.nsc._ -import io.Directory import util.{BatchSourceFile, CommandLineParser} import reporters.{Reporter, ConsoleReporter} @@ -21,8 +20,8 @@ abstract class DirectTest extends App { def show(): Unit // the test file or dir, and output directory - def testPath = io.File(sys.props("partest.test-path")) - def testOutput = io.Directory(sys.props("partest.output")) + def testPath = SFile(sys.props("partest.test-path")) + def testOutput = Directory(sys.props("partest.output")) // override to add additional settings with strings def extraSettings: String = "" diff --git a/src/partest/scala/tools/partest/IcodeTest.scala b/src/partest/scala/tools/partest/IcodeTest.scala index f5333cc5f9..b12ec0de61 100644 --- a/src/partest/scala/tools/partest/IcodeTest.scala +++ b/src/partest/scala/tools/partest/IcodeTest.scala @@ -5,9 +5,7 @@ package scala.tools.partest -import scala.tools.nsc._ -import nest.FileUtil._ -import io.Directory +import scala.tools.partest.nest.FileUtil.compareContents /** A trait for testing icode. All you need is this in a * partest source file: diff --git a/src/partest/scala/tools/partest/PartestDefaults.scala b/src/partest/scala/tools/partest/PartestDefaults.scala index 5d98a8be81..16f1a6933f 100644 --- a/src/partest/scala/tools/partest/PartestDefaults.scala +++ b/src/partest/scala/tools/partest/PartestDefaults.scala @@ -1,13 +1,10 @@ package scala.tools package partest -import nsc.io.{ File, Path, Directory } -import scala.tools.util.PathResolver -import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } -import java.lang.Runtime.getRuntime +import scala.tools.nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } +import java.lang.Runtime.{ getRuntime => runtime } object PartestDefaults { - import nsc.Properties._ def testRootName = propOrNone("partest.root") def srcDirName = propOrElse("partest.srcdir", "files") @@ -23,7 +20,7 @@ object PartestDefaults { def testBuild = propOrNone("partest.build") def errorCount = propOrElse("partest.errors", "0").toInt - def numThreads = propOrNone("partest.threads") map (_.toInt) getOrElse getRuntime.availableProcessors + def numThreads = propOrNone("partest.threads") map (_.toInt) getOrElse runtime.availableProcessors def timeout = "1200000" } diff --git a/src/partest/scala/tools/partest/PartestTask.scala b/src/partest/scala/tools/partest/PartestTask.scala index 13207b16fd..b5b09a753a 100644 --- a/src/partest/scala/tools/partest/PartestTask.scala +++ b/src/partest/scala/tools/partest/PartestTask.scala @@ -10,14 +10,10 @@ package scala.tools package partest import scala.util.Properties.setProp -import scala.tools.nsc.io.{ Directory, Path => SPath } -import nsc.util.ClassPath -import util.PathResolver import scala.tools.ant.sabbus.CompilationPathProperty -import java.io.File import java.lang.reflect.Method import org.apache.tools.ant.Task -import org.apache.tools.ant.types.{Path, Reference, FileSet} +import org.apache.tools.ant.types.{ Reference, FileSet} import org.apache.tools.ant.types.Commandline.Argument /** An Ant task to execute the Scala test suite (NSC). @@ -26,92 +22,42 @@ import org.apache.tools.ant.types.Commandline.Argument * - `srcdir`, * - `classpath`, * - `classpathref`, - * - `showlog`, - * - `showdiff`, * - `erroronfailed`, * - `javacmd`, * - `javaccmd`, * - `scalacopts`, - * - `timeout`, * - `debug`, * - `junitreportdir`. * * It also takes the following parameters as nested elements: * - `compilationpath`. - * - `postests`, - * - `negtests`, - * - `runtests`, - * - `jvmtests`, - * - `residenttests`, - * - `shootouttests`, - * - `scalaptests`, - * - `scalachecktests`, - * - `specializedtests`, - * - `instrumentedtests`, - * - `presentationtests`, - * - `scripttests`. * * @author Philippe Haller */ class PartestTask extends Task with CompilationPathProperty { - - def addConfiguredPosTests(input: FileSet) { - posFiles = Some(input) - } - - def addConfiguredNegTests(input: FileSet) { - negFiles = Some(input) - } - - def addConfiguredRunTests(input: FileSet) { - runFiles = Some(input) - } - - def addConfiguredJvmTests(input: FileSet) { - jvmFiles = Some(input) - } - - def addConfiguredResidentTests(input: FileSet) { - residentFiles = Some(input) - } - - def addConfiguredScalacheckTests(input: FileSet) { - scalacheckFiles = Some(input) - } - - def addConfiguredScriptTests(input: FileSet) { - scriptFiles = Some(input) - } - - def addConfiguredShootoutTests(input: FileSet) { - shootoutFiles = Some(input) - } - - def addConfiguredScalapTests(input: FileSet) { - scalapFiles = Some(input) - } - - def addConfiguredSpecializedTests(input: FileSet) { - specializedFiles = Some(input) - } - - def addConfiguredInstrumentedTests(input: FileSet) { - instrumentedFiles = Some(input) - } - - def addConfiguredPresentationTests(input: FileSet) { - presentationFiles = Some(input) - } - - def addConfiguredAntTests(input: FileSet) { - antFiles = Some(input) - } - + type Path = org.apache.tools.ant.types.Path + + private var kinds: List[String] = Nil + private var classpath: Option[Path] = None + private var debug = false + private var errorOnFailed: Boolean = true + private var jUnitReportDir: Option[File] = None + private var javaccmd: Option[File] = None + private var javacmd: Option[File] = Option(sys.props("java.home")) map (x => new File(x, "bin/java")) + private var scalacArgs: Option[Seq[Argument]] = None + private var srcDir: Option[String] = None + private var colors: Int = 0 def setSrcDir(input: String) { srcDir = Some(input) } + def setColors(input: String) { + try colors = input.toInt catch { case _: NumberFormatException => () } + if (colors > 0) + sys.props("partest.colors") = colors.toString + } + def setClasspath(input: Path) { if (classpath.isEmpty) classpath = Some(input) @@ -127,15 +73,6 @@ class PartestTask extends Task with CompilationPathProperty { def setClasspathref(input: Reference) { createClasspath().setRefid(input) } - - def setShowLog(input: Boolean) { - showLog = input - } - - def setShowDiff(input: Boolean) { - showDiff = input - } - def setErrorOnFailed(input: Boolean) { errorOnFailed = input } @@ -144,6 +81,10 @@ class PartestTask extends Task with CompilationPathProperty { javacmd = Some(input) } + def setKinds(input: String) { + kinds = words(input) + } + def setJavacCmd(input: File) { javaccmd = Some(input) } @@ -159,10 +100,6 @@ class PartestTask extends Task with CompilationPathProperty { a } - def setTimeout(delay: String) { - timeout = Some(delay) - } - def setDebug(input: Boolean) { debug = input } @@ -171,172 +108,35 @@ class PartestTask extends Task with CompilationPathProperty { jUnitReportDir = Some(input) } - private var classpath: Option[Path] = None - private var srcDir: Option[String] = None - private var javacmd: Option[File] = None - private var javaccmd: Option[File] = None - private var showDiff: Boolean = false - private var showLog: Boolean = false - private var posFiles: Option[FileSet] = None - private var negFiles: Option[FileSet] = None - private var runFiles: Option[FileSet] = None - private var jvmFiles: Option[FileSet] = None - private var residentFiles: Option[FileSet] = None - private var scalacheckFiles: Option[FileSet] = None - private var scriptFiles: Option[FileSet] = None - private var shootoutFiles: Option[FileSet] = None - private var scalapFiles: Option[FileSet] = None - private var specializedFiles: Option[FileSet] = None - private var instrumentedFiles: Option[FileSet] = None - private var presentationFiles: Option[FileSet] = None - private var antFiles: Option[FileSet] = None - private var errorOnFailed: Boolean = false - private var scalacArgs: Option[Seq[Argument]] = None - private var timeout: Option[String] = None - private var jUnitReportDir: Option[File] = None - private var debug = false - - def fileSetToDir(fs: FileSet) = Directory(fs getDir getProject) - def fileSetToArray(fs: FileSet): Array[SPath] = { - val root = fileSetToDir(fs) - (fs getDirectoryScanner getProject).getIncludedFiles map (root / _) - } - - private def getFiles(fileSet: Option[FileSet]): Array[File] = fileSet match { - case None => Array() - case Some(fs) => fileSetToArray(fs) filterNot (_ hasExtension "log") map (_.jfile) - } - - private def getFilesAndDirs(fileSet: Option[FileSet]): Array[File] = fileSet match { - case None => Array() - case Some(fs) => - def shouldExclude(name: String) = (name endsWith ".obj") || (name startsWith ".") - // println("----> " + fileSet) - - val fileTests = getFiles(Some(fs)) filterNot (x => shouldExclude(x.getName)) - val dirResult = getDirs(Some(fs)) filterNot (x => shouldExclude(x.getName)) - // println("dirs: " + dirResult.toList) - // println("files: " + fileTests.toList) - - dirResult ++ fileTests - } - - private def getDirs(fileSet: Option[FileSet]): Array[File] = fileSet match { - case None => Array() - case Some(fs) => - def shouldExclude(name: String) = (name endsWith ".obj") || (name startsWith ".") - - val dirTests: Iterator[SPath] = fileSetToDir(fs).dirs filterNot (x => shouldExclude(x.name)) - val dirResult = dirTests.toList.toArray map (_.jfile) - - dirResult - } - - - private def getPosFiles = getFilesAndDirs(posFiles) - private def getNegFiles = getFilesAndDirs(negFiles) - private def getRunFiles = getFilesAndDirs(runFiles) - private def getJvmFiles = getFilesAndDirs(jvmFiles) - private def getResidentFiles = getFiles(residentFiles) - private def getScalacheckFiles = getFilesAndDirs(scalacheckFiles) - private def getScriptFiles = getFiles(scriptFiles) - private def getShootoutFiles = getFiles(shootoutFiles) - private def getScalapFiles = getFiles(scalapFiles) - private def getSpecializedFiles = getFiles(specializedFiles) - private def getInstrumentedFiles = getFilesAndDirs(instrumentedFiles) - private def getPresentationFiles = getDirs(presentationFiles) - private def getAntFiles = getFiles(antFiles) - override def execute() { - val opts = getProject().getProperties() get "env.PARTEST_OPTS" - if (opts != null && opts.toString != "") - opts.toString.split(" ") foreach { propDef => - log("setting system property " + propDef) - val kv = propDef split "=" - val key = kv(0) substring 2 - val value = kv(1) - setProp(key, value) - } - - if (isPartestDebug || debug) { - setProp("partest.debug", "true") - nest.NestUI._verbose = true + if (debug || sys.props.contains("partest.debug")) { + nest.NestUI.setDebug() } srcDir foreach (x => setProp("partest.srcdir", x)) val classpath = this.compilationPath getOrElse sys.error("Mandatory attribute 'compilationPath' is not set.") - - val scalaLibrary = { - (classpath.list map { fs => new File(fs) }) find { f => - f.getName match { - case "scala-library.jar" => true - case "library" if (f.getParentFile.getName == "classes") => true - case _ => false - } - } - } getOrElse sys.error("Provided classpath does not contain a Scala library.") - - val scalaReflect = { - (classpath.list map { fs => new File(fs) }) find { f => - f.getName match { - case "scala-reflect.jar" => true - case "reflect" if (f.getParentFile.getName == "classes") => true - case _ => false - } - } - } getOrElse sys.error("Provided classpath does not contain a Scala reflection library.") - - val scalaCompiler = { - (classpath.list map { fs => new File(fs) }) find { f => - f.getName match { - case "scala-compiler.jar" => true - case "compiler" if (f.getParentFile.getName == "classes") => true - case _ => false - } - } - } getOrElse sys.error("Provided classpath does not contain a Scala compiler.") - - val scalaPartest = { - (classpath.list map { fs => new File(fs) }) find { f => - f.getName match { - case "scala-partest.jar" => true - case "partest" if (f.getParentFile.getName == "classes") => true - case _ => false - } - } - } getOrElse sys.error("Provided classpath does not contain a Scala partest.") - - val scalaActors = { - (classpath.list map { fs => new File(fs) }) find { f => - f.getName match { - case "scala-actors.jar" => true - case "actors" if (f.getParentFile.getName == "classes") => true - case _ => false - } - } - } getOrElse sys.error("Provided classpath does not contain a Scala actors.") + val cpfiles = classpath.list map { fs => new File(fs) } toList + def findCp(name: String) = cpfiles find (f => + (f.getName == s"scala-$name.jar") + || (f.absolutePathSegments endsWith Seq("classes", name)) + ) getOrElse sys.error(s"Provided classpath does not contain a Scala $name element.") + + val scalaLibrary = findCp("library") + val scalaReflect = findCp("reflect") + val scalaCompiler = findCp("compiler") + val scalaPartest = findCp("partest") + val scalaActors = findCp("actors") def scalacArgsFlat: Option[Seq[String]] = scalacArgs map (_ flatMap { a => val parts = a.getParts - if(parts eq null) Seq[String]() else parts.toSeq + if (parts eq null) Nil else parts.toSeq }) val antRunner = new scala.tools.partest.nest.AntRunner val antFileManager = antRunner.fileManager - // this is a workaround for https://issues.scala-lang.org/browse/SI-5433 - // when that bug is fixed, this paragraph of code can be safely removed - // we hack into the classloader that will become parent classloader for scalac - // this way we ensure that reflective macro lookup will pick correct Code.lift - val loader = getClass.getClassLoader.asInstanceOf[org.apache.tools.ant.AntClassLoader] - val path = new org.apache.tools.ant.types.Path(getProject()) - val newClassPath = ClassPath.join(nest.PathSettings.srcCodeLib.toString, loader.getClasspath) - path.setPath(newClassPath) - loader.setClassPath(path) - - antFileManager.showDiff = showDiff - antFileManager.showLog = showLog + // antFileManager.failed = runFailed antFileManager.CLASSPATH = ClassPath.join(classpath.list: _*) antFileManager.LATEST_LIB = scalaLibrary.getAbsolutePath antFileManager.LATEST_REFLECT = scalaReflect.getAbsolutePath @@ -347,53 +147,35 @@ class PartestTask extends Task with CompilationPathProperty { javacmd foreach (x => antFileManager.JAVACMD = x.getAbsolutePath) javaccmd foreach (x => antFileManager.JAVAC_CMD = x.getAbsolutePath) scalacArgsFlat foreach (antFileManager.SCALAC_OPTS ++= _) - timeout foreach (antFileManager.timeout = _) - - type TFSet = (Array[File], String, String) - val testFileSets = List( - (getPosFiles, "pos", "Compiling files that are expected to build"), - (getNegFiles, "neg", "Compiling files that are expected to fail"), - (getRunFiles, "run", "Compiling and running files"), - (getJvmFiles, "jvm", "Compiling and running files"), - (getResidentFiles, "res", "Running resident compiler scenarii"), - (getScalacheckFiles, "scalacheck", "Running scalacheck tests"), - (getScriptFiles, "script", "Running script files"), - (getShootoutFiles, "shootout", "Running shootout tests"), - (getScalapFiles, "scalap", "Running scalap tests"), - (getSpecializedFiles, "specialized", "Running specialized files"), - (getInstrumentedFiles, "instrumented", "Running instrumented files"), - (getPresentationFiles, "presentation", "Running presentation compiler test files"), - (getAntFiles, "ant", "Running ant task tests") - ) - - def runSet(set: TFSet): (Int, Int, Iterable[String]) = { - val (files, name, msg) = set + + def runSet(kind: String, files: Array[File]): (Int, Int, List[String]) = { if (files.isEmpty) (0, 0, List()) else { - log(msg) - val results: Iterable[(String, TestState)] = antRunner.reflectiveRunTestsForFiles(files, name) - val (succs, fails) = resultsToStatistics(results) + log(s"Running ${files.length} tests in '$kind' at $now") + // log(s"Tests: ${files.toList}") + val results = antRunner.reflectiveRunTestsForFiles(files, kind) + val (passed, failed) = results partition (_.isOk) + val numPassed = passed.size + val numFailed = failed.size + def failedMessages = failed map (_.longStatus) - val failed: Iterable[String] = results collect { - case (path, TestState.Fail) => path + " [FAILED]" - case (path, TestState.Timeout) => path + " [TIMOUT]" - } + log(s"Completed '$kind' at $now") // create JUnit Report xml files if directory was specified jUnitReportDir foreach { d => d.mkdir - val report = testReport(name, results, succs, fails) - scala.xml.XML.save(d.getAbsolutePath+"/"+name+".xml", report) + val report = testReport(kind, results, numPassed, numFailed) + scala.xml.XML.save(d.getAbsolutePath+"/"+kind+".xml", report) } - (succs, fails, failed) + (numPassed, numFailed, failedMessages) } } - val _results = testFileSets map runSet - val allSuccesses = _results map (_._1) sum - val allFailures = _results map (_._2) sum + val _results = kinds map (k => runSet(k, TestKinds testsFor k map (_.jfile) toArray)) + val allSuccesses = _results map (_._1) sum + val allFailures = _results map (_._2) sum val allFailedPaths = _results flatMap (_._3) def f = if (errorOnFailed && allFailures > 0) (sys error _) else log(_: String) @@ -408,20 +190,17 @@ class PartestTask extends Task with CompilationPathProperty { f(msg) } - private def oneResult(res: (String, TestState)) = - <testcase name={res._1}>{ - res._2 match { - case TestState.Ok => scala.xml.NodeSeq.Empty - case TestState.Fail => <failure message="Test failed"/> - case TestState.Timeout => <failure message="Test timed out"/> - } + private def oneResult(res: TestState) = + <testcase name={res.testIdent}>{ + if (res.isOk) scala.xml.NodeSeq.Empty + else <failure message="Test failed"/> }</testcase> - private def testReport(kind: String, results: Iterable[(String, TestState)], succs: Int, fails: Int) = + private def testReport(kind: String, results: Iterable[TestState], succs: Int, fails: Int) = <testsuite name={kind} tests={(succs + fails).toString} failures={fails.toString}> <properties/> { - results.map(oneResult(_)) + results map oneResult } </testsuite> } diff --git a/src/partest/scala/tools/partest/TestKinds.scala b/src/partest/scala/tools/partest/TestKinds.scala new file mode 100644 index 0000000000..ec682690ca --- /dev/null +++ b/src/partest/scala/tools/partest/TestKinds.scala @@ -0,0 +1,67 @@ +package scala.tools +package partest + +import nest.PathSettings.srcDir + +object TestKinds { + val standardKinds = "pos neg run jvm res buildmanager scalacheck scalap specialized instrumented presentation ant" split "\\s+" toList + val standardArgs = standardKinds map ("--" + _) + + def denotesTestFile(p: Path) = p.isFile && p.hasExtension("scala", "res", "xml") + def denotesTestDir(p: Path) = kindOf(p) match { + case "res" => false + case _ => p.isDirectory && p.extension == "" + } + def denotesTestPath(p: Path) = denotesTestDir(p) || denotesTestFile(p) + + // TODO + def isTestForPartest(p: Path) = ( + (p.name == "intentional-failure.scala") + || (p.path contains "test-for-partest") + ) + + def kindOf(p: Path) = { + p.toAbsolute.segments takeRight 2 head + + // (srcDir relativize p.toCanonical).segments match { + // case (".." :: "scaladoc" :: xs) => xs.head + // case xs => xs.head + // } + } + def logOf(p: Path) = { + p.parent / s"${p.stripExtension}-${kindOf(p)}.log" + // p.parent / s"${p.stripExtension}.log" + } + + // true if a test path matches the --grep expression. + private def pathMatchesExpr(path: Path, expr: String) = { + // Matches the expression if any source file contains the expr, + // or if the checkfile contains it, or if the filename contains + // it (the last is case-insensitive.) + def matches(p: Path) = ( + (p.path.toLowerCase contains expr.toLowerCase) + || (p.fileContents contains expr) + ) + def candidates = { + (path changeExtension "check") +: { + if (path.isFile) List(path) + else path.toDirectory.deepList() filter (_.isJavaOrScala) toList + } + } + + (candidates exists matches) + } + + def groupedTests(paths: List[Path]): List[(String, List[Path])] = + (paths.distinct groupBy kindOf).toList sortBy (standardKinds indexOf _._1) + + /** Includes tests for testing partest. */ + private def allTestsForKind(kind: String): List[Path] = + (srcDir / kind toDirectory).list.toList filter denotesTestPath + + def testsForPartest: List[Path] = standardKinds flatMap allTestsForKind filter isTestForPartest + def testsFor(kind: String): List[Path] = allTestsForKind(kind) filterNot isTestForPartest + def grepFor(expr: String): List[Path] = standardTests filter (t => pathMatchesExpr(t, expr)) + def standardTests: List[Path] = standardKinds flatMap testsFor + def failedTests: List[Path] = standardTests filter (p => logOf(p).isFile) +} diff --git a/src/partest/scala/tools/partest/TestState.scala b/src/partest/scala/tools/partest/TestState.scala new file mode 100644 index 0000000000..ce8e72f616 --- /dev/null +++ b/src/partest/scala/tools/partest/TestState.scala @@ -0,0 +1,54 @@ +package scala.tools.partest + +import scala.tools.nsc.FatalError +import scala.tools.nsc.util.stackTraceString + +sealed abstract class TestState { + def testFile: File + def what: String + def reason: String + def transcript: List[String] + + def isOk = false + def isSkipped = false + def testIdent = testFile.testIdent + def transcriptString = transcript.mkString("\n") + + def identAndReason = testIdent + reasonString + def status = s"$what - $identAndReason" + def longStatus = status + transcriptString + def reasonString = if (reason == "") "" else s" [$reason]" + + override def toString = status +} + +object TestState { + case class Uninitialized(testFile: File) extends TestState { + def what = "uninitialized" + def reason = what + def transcript = Nil + } + case class Pass(testFile: File) extends TestState { + final override def isOk = true + def what = "pass" + def transcript: List[String] = Nil + def reason = "" + } + case class Skip(testFile: File, reason: String) extends TestState { + override def isOk = true + final override def isSkipped = true + def transcript: List[String] = Nil + def what = "skip" + } + case class Fail(testFile: File, reason: String, transcript: List[String]) extends TestState { + def what = "fail" + } + case class Crash(testFile: File, caught: Throwable, transcript: List[String]) extends TestState { + def what = "crash" + def reason = s"caught $caught_s - ${caught.getMessage}" + + private def caught_s = (caught.getClass.getName split '.').last + private def stack_s = stackTraceString(caught) + override def transcriptString = nljoin(super.transcriptString, caught_s) + } +} diff --git a/src/partest/scala/tools/partest/nest/AntRunner.scala b/src/partest/scala/tools/partest/nest/AntRunner.scala index 93045b8c1d..1d3b79171b 100644 --- a/src/partest/scala/tools/partest/nest/AntRunner.scala +++ b/src/partest/scala/tools/partest/nest/AntRunner.scala @@ -10,24 +10,21 @@ package scala.tools.partest package nest -import java.io.File -import scala.tools.nsc.io.{ Directory } - class AntRunner extends DirectRunner { val fileManager = new FileManager { - var JAVACMD: String = "java" - var JAVAC_CMD: String = "javac" - var CLASSPATH: String = _ - var LATEST_LIB: String = _ - var LATEST_REFLECT: String = _ - var LATEST_COMP: String = _ - var LATEST_PARTEST: String = _ - var LATEST_ACTORS: String = _ - val testRootPath: String = "test" - val testRootDir: Directory = Directory(testRootPath) + var JAVACMD: String = "java" + var JAVAC_CMD: String = "javac" + var CLASSPATH: String = _ + var LATEST_LIB: String = _ + var LATEST_REFLECT: String = _ + var LATEST_COMP: String = _ + var LATEST_PARTEST: String = _ + var LATEST_ACTORS: String = _ + val testRootPath: String = "test" + val testRootDir: Directory = Directory(testRootPath) } - def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String) = + def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String): List[TestState] = runTestsForFiles(kindFiles.toList, kind) } diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala deleted file mode 100644 index a8694cc0d6..0000000000 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ /dev/null @@ -1,182 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2013 LAMP/EPFL - * @author Philipp Haller - */ - -// $Id$ - -package scala.tools.partest -package nest - -import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError, io } -import scala.reflect.io.{ Directory, File => SFile, FileOperationException } -import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } -import scala.tools.nsc.util.{ ClassPath, FakePos } -import scala.tools.nsc.Properties.{ setProp, propOrEmpty } -import scala.tools.util.PathResolver -import io.Path -import java.io.{ File, BufferedReader, PrintWriter, FileReader, Writer, FileWriter, StringWriter } -import File.pathSeparator - -sealed abstract class CompilationOutcome { - def merge(other: CompilationOutcome): CompilationOutcome - def isPositive = this eq CompileSuccess - def isNegative = this eq CompileFailed -} -case object CompileSuccess extends CompilationOutcome { - def merge(other: CompilationOutcome) = other -} -case object CompileFailed extends CompilationOutcome { - def merge(other: CompilationOutcome) = if (other eq CompileSuccess) this else other -} -case object CompilerCrashed extends CompilationOutcome { - def merge(other: CompilationOutcome) = this -} - -class ExtConsoleReporter(settings: Settings, val writer: PrintWriter) extends ConsoleReporter(settings, Console.in, writer) { - shortname = true -} - -class TestSettings(cp: String, error: String => Unit) extends Settings(error) { - def this(cp: String) = this(cp, _ => ()) - - nowarnings.value = false - encoding.value = "UTF-8" - classpath.value = cp -} - -abstract class SimpleCompiler { - def compile(out: Option[File], files: List[File], kind: String, log: File): CompilationOutcome -} - -class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { - def newGlobal(settings: Settings, reporter: Reporter): Global = - Global(settings, reporter) - - def newGlobal(settings: Settings, logWriter: FileWriter): Global = - newGlobal(settings, new ExtConsoleReporter(settings, new PrintWriter(logWriter))) - - def newSettings(): TestSettings = new TestSettings(fileManager.LATEST_LIB) - def newSettings(outdir: String): TestSettings = { - val cp = ClassPath.join(fileManager.LATEST_LIB, outdir) - val s = new TestSettings(cp) - s.outdir.value = outdir - - s - } - - implicit class Copier(f: SFile) { - // But what if f is bigger than CHUNK?! - def copyTo(dest: Path) { - dest.toFile writeAll f.slurp - } - } - - // plugin path can be relative to test root, or cwd is out - private def updatePluginPath(options: String, out: Option[File], srcdir: Directory): String = { - val dir = fileManager.testRootDir - def pathOrCwd(p: String) = - if (p == "." && out.isDefined) { - val plugxml = "scalac-plugin.xml" - val pout = Path(out.get) - val pd = (srcdir / plugxml).toFile - if (pd.exists) pd copyTo (pout / plugxml) - pout - } else Path(p) - def absolutize(path: String) = pathOrCwd(path) match { - case x if x.isAbsolute => x.path - case x => (dir / x).toAbsolute.path - } - - val (opt1, opt2) = (options split "\\s").toList partition (_ startsWith "-Xplugin:") - val plugins = opt1 map (_ stripPrefix "-Xplugin:") flatMap (_ split pathSeparator) map absolutize - val pluginOption = if (opt1.isEmpty) Nil else List("-Xplugin:" + (plugins mkString pathSeparator)) - - (opt2 ::: pluginOption) mkString " " - } - - def compile(out: Option[File], files: List[File], kind: String, log: File): CompilationOutcome = { - val testSettings = out match { - case Some(f) => newSettings(f.getAbsolutePath) - case _ => newSettings() - } - val logWriter = new FileWriter(log) - - // this api has no notion of srcdir, so fake it - val fstFile = SFile(files(0)) - val srcdir = fstFile.parent - - // check whether there is a ".flags" file - def convertFlags(f: SFile) = updatePluginPath(f.slurp(), out, srcdir) - val logFile = basename(log.getName) - val flagsFileName = "%s.flags" format (logFile.substring(0, logFile.lastIndexOf("-"))) - val argString = (SFile(log).parent / flagsFileName) ifFile (convertFlags) getOrElse "" - - // slurp local flags (e.g., "A_1.flags") - def isInGroup(num: Int) = fstFile.stripExtension endsWith ("_" + num) - val inGroup = (1 to 9) flatMap (group => if (isInGroup(group)) List(group) else List()) - val localFlagsList = if (inGroup.nonEmpty) { - val localArgString = (srcdir / (fstFile.stripExtension + ".flags")) ifFile (convertFlags) getOrElse "" - localArgString.split(' ').toList.filter(_.length > 0) - } else List() - - val allOpts = fileManager.SCALAC_OPTS.toList ::: argString.split(' ').toList.filter(_.length > 0) ::: localFlagsList - val args = allOpts.toList - - NestUI.verbose("scalac options: "+allOpts) - - val command = new CompilerCommand(args, testSettings) - val global = newGlobal(command.settings, logWriter) - val testRep: ExtConsoleReporter = global.reporter.asInstanceOf[ExtConsoleReporter] - - val testFileFn: (File, FileManager) => TestFile = kind match { - case "pos" => PosTestFile.apply - case "neg" => NegTestFile.apply - case "run" => RunTestFile.apply - case "jvm" => JvmTestFile.apply - case "shootout" => ShootoutTestFile.apply - case "scalap" => ScalapTestFile.apply - case "scalacheck" => ScalaCheckTestFile.apply - case "specialized" => SpecializedTestFile.apply - case "instrumented" => InstrumentedTestFile.apply - case "presentation" => PresentationTestFile.apply - case "ant" => AntTestFile.apply - } - val test: TestFile = testFileFn(files.head, fileManager) - if (!test.defineSettings(command.settings, out.isEmpty)) { - testRep.error(FakePos("partest"), test.flags match { - case Some(flags) => "bad flags: " + flags - case _ => "bad settings: " + command.settings - }) - } - - val toCompile = files map (_.getPath) - - try { - NestUI.verbose("compiling "+toCompile) - NestUI.verbose("with classpath: "+global.classPath.toString) - NestUI.verbose("and java classpath: "+ propOrEmpty("java.class.path")) - try { - if (command.shouldStopWithInfo) logWriter append (command getInfoMessage global) - else new global.Run compile toCompile - } catch { - case FatalError(msg) => - testRep.error(null, "fatal error: " + msg) - return CompilerCrashed - } - - testRep.printSummary() - testRep.writer.close() - } - finally logWriter.close() - - if (testRep.hasErrors || command.shouldStopWithInfo) CompileFailed - else CompileSuccess - } -} - -class CompileManager(val fileManager: FileManager) { - private def newCompiler = new DirectCompiler(fileManager) - def attemptCompile(outdir: Option[File], sources: List[File], kind: String, log: File): CompilationOutcome = - newCompiler.compile(outdir, sources, kind, log) -} diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala index 0ec3f60bf5..b436675d3a 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala @@ -3,20 +3,15 @@ * @author Philipp Haller */ -// $Id$ + package scala.tools.partest package nest -import java.io.{ File, FilenameFilter, IOException, StringWriter } +import java.io.{ FilenameFilter, IOException } import java.net.URI import scala.util.Properties.{ propOrElse, scalaCmd, scalacCmd } -import scala.tools.util.PathResolver import scala.tools.nsc.{ io, util } -import util.{ ClassPath } -import io.{ Path, Directory } -import File.pathSeparator -import ClassPath.{ join } import PathResolver.{ Environment, Defaults } class ConsoleFileManager extends FileManager { @@ -55,7 +50,7 @@ class ConsoleFileManager extends FileManager { var JAVAC_CMD = PartestDefaults.javacCmd - NestUI.verbose("CLASSPATH: "+CLASSPATH) + vlog("CLASSPATH: "+CLASSPATH) if (!srcDir.isDirectory) { NestUI.failure("Source directory \"" + srcDir.path + "\" not found") @@ -70,14 +65,14 @@ class ConsoleFileManager extends FileManager { } def findLatest() { - NestUI.verbose("test parent: "+testParent) + vlog("test parent: "+testParent) - def prefixFileWith(parent: File, relPath: String) = (io.File(parent) / relPath).toCanonical + def prefixFileWith(parent: File, relPath: String) = (SFile(parent) / relPath).toCanonical def prefixFile(relPath: String) = (testParent / relPath).toCanonical if (!testClasses.isEmpty) { testClassesDir = Path(testClasses.get).toCanonical.toDirectory - NestUI.verbose("Running with classes in "+testClassesDir) + vlog("Running with classes in "+testClassesDir) latestLibFile = testClassesDir / "library" latestActorsFile = testClassesDir / "library" / "actors" @@ -87,7 +82,7 @@ class ConsoleFileManager extends FileManager { } else if (testBuild.isDefined) { val dir = Path(testBuild.get) - NestUI.verbose("Running on "+dir) + vlog("Running on "+dir) latestLibFile = dir / "lib/scala-library.jar" latestActorsFile = dir / "lib/scala-actors.jar" latestReflectFile = dir / "lib/scala-reflect.jar" @@ -96,7 +91,7 @@ class ConsoleFileManager extends FileManager { } else { def setupQuick() { - NestUI.verbose("Running build/quick") + vlog("Running build/quick") latestLibFile = prefixFile("build/quick/classes/library") latestActorsFile = prefixFile("build/quick/classes/library/actors") latestReflectFile = prefixFile("build/quick/classes/reflect") @@ -105,7 +100,7 @@ class ConsoleFileManager extends FileManager { } def setupInst() { - NestUI.verbose("Running dist (installed)") + vlog("Running dist (installed)") val p = testParent.getParentFile latestLibFile = prefixFileWith(p, "lib/scala-library.jar") latestActorsFile = prefixFileWith(p, "lib/scala-actors.jar") @@ -115,7 +110,7 @@ class ConsoleFileManager extends FileManager { } def setupDist() { - NestUI.verbose("Running dists/latest") + vlog("Running dists/latest") latestLibFile = prefixFile("dists/latest/lib/scala-library.jar") latestActorsFile = prefixFile("dists/latest/lib/scala-actors.jar") latestReflectFile = prefixFile("dists/latest/lib/scala-reflect.jar") @@ -124,7 +119,7 @@ class ConsoleFileManager extends FileManager { } def setupPack() { - NestUI.verbose("Running build/pack") + vlog("Running build/pack") latestLibFile = prefixFile("build/pack/lib/scala-library.jar") latestActorsFile = prefixFile("build/pack/lib/scala-actors.jar") latestReflectFile = prefixFile("build/pack/lib/scala-reflect.jar") @@ -170,11 +165,13 @@ class ConsoleFileManager extends FileManager { var latestReflectFile: File = _ var latestCompFile: File = _ var latestPartestFile: File = _ - def latestScalapFile: File = (latestLibFile.parent / "scalap.jar").jfile + //def latestScalapFile: File = (latestLibFile.parent / "scalap.jar").jfile + //def latestScalapFile: File = new File(latestLibFile.getParentFile, "scalap.jar") var testClassesDir: Directory = _ // initialize above fields findLatest() + /* def getFiles(kind: String, cond: Path => Boolean): List[File] = { def ignoreDir(p: Path) = List("svn", "obj") exists (p hasExtension _) @@ -187,4 +184,6 @@ class ConsoleFileManager extends FileManager { ( if (failed) files filter (x => logFileExists(x, kind)) else files ) map (_.jfile) } + */ + var latestFjbgFile: File = _ } diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index fd4d52f603..ddd42f5601 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -3,83 +3,110 @@ * @author Philipp Haller */ -// $Id$ - -package scala.tools.partest +package scala.tools +package partest package nest -import java.io.{File, PrintStream, FileOutputStream, BufferedReader, - InputStreamReader, StringWriter, PrintWriter} import utils.Properties._ import scala.tools.nsc.Properties.{ versionMsg, setProp } import scala.tools.nsc.util.CommandLineParser -import scala.tools.nsc.io -import io.{ Path } import scala.collection.{ mutable, immutable } +import PathSettings.srcDir +import TestKinds._ class ConsoleRunner extends DirectRunner { - import PathSettings.{ srcDir, testRoot } - - case class TestSet(kind: String, filter: Path => Boolean, msg: String) - private def stdFilter(p: Path) = p.isDirectory || (p hasExtension "scala") - private def antFilter(p: Path) = p.isFile && (p endsWith "build.xml") - - val testSets = { - List( - TestSet("pos", stdFilter, "Testing compiler (on files whose compilation should succeed)"), - TestSet("neg", stdFilter, "Testing compiler (on files whose compilation should fail)"), - TestSet("run", stdFilter, "Testing interpreter and backend"), - TestSet("jvm", stdFilter, "Testing JVM backend"), - TestSet("res", x => x.isFile && (x hasExtension "res"), "Testing resident compiler"), - TestSet("shootout", stdFilter, "Testing shootout tests"), - TestSet("script", stdFilter, "Testing script tests"), - TestSet("scalacheck", stdFilter, "Testing ScalaCheck tests"), - TestSet("scalap", _.isDirectory, "Run scalap decompiler tests"), - TestSet("specialized", stdFilter, "Testing specialized tests"), - TestSet("instrumented", stdFilter, "Testing instrumented tests"), - TestSet("presentation", _.isDirectory, "Testing presentation compiler tests."), - TestSet("ant", antFilter, "Run Ant task tests.") - ) - } + import NestUI._ + import NestUI.color._ + + // So we can ctrl-C a test run and still hear all + // the buffered failure info. + scala.sys addShutdownHook issueSummaryReport() var fileManager: ConsoleFileManager = _ - private var testFiles: List[File] = List() - private val errors = PartestDefaults.errorCount - private val testSetKinds = testSets map (_.kind) - private val testSetArgs = testSets map ("--" + _.kind) - private val testSetArgMap = testSetArgs zip testSets toMap + private var totalTests = 0 + private val passedTests = mutable.ListBuffer[TestState]() + private val failedTests = mutable.ListBuffer[TestState]() + + def comment(s: String) = echo(magenta("# " + s)) + def levyJudgment() = { + if (totalTests == 0) echoMixed("No tests to run.") + else if (elapsedMillis == 0) echoMixed("Test Run ABORTED") + else if (isSuccess) echoPassed("Test Run PASSED") + else echoFailed("Test Run FAILED") + } + + def passFailString(passed: Int, failed: Int, skipped: Int): String = { + val total = passed + failed + skipped + val isSuccess = failed == 0 + def p0 = s"$passed/$total" + def p = ( if (isSuccess) bold(green(p0)) else p0 ) + " passed" + def f = if (failed == 0) "" else bold(red("" + failed)) + " failed" + def s = if (skipped == 0) "" else bold(yellow("" + skipped)) + " skipped" + + oempty(p, f, s) mkString ", " + } + + private var summarizing = false + private var elapsedMillis = 0L + private var expectedFailures = 0 + private def isSuccess = failedTests.size == expectedFailures + + def issueSummaryReport() { + // Don't run twice + if (!summarizing) { + summarizing = true + + val passed0 = passedTests.toList + val failed0 = failedTests.toList + val passed = passed0.size + val failed = failed0.size + val skipped = totalTests - (passed + failed) + val passFail = passFailString(passed, failed, skipped) + val elapsed = if (elapsedMillis > 0) " (elapsed time: " + elapsedString(elapsedMillis) + ")" else "" + val message = passFail + elapsed + + if (failed0.nonEmpty) { + echo(bold(cyan("##### Transcripts from failed tests #####\n"))) + failed0 foreach { state => + comment("partest " + state.testFile) + echo(state.transcriptString + "\n") + } + } - private def printVersion() { NestUI outline (versionMsg + "\n") } + echo(message) + levyJudgment() + } + } private val unaryArgs = List( - "--pack", "--all", "--verbose", "--show-diff", "--show-log", + "--pack", "--all", + "--terse", "--verbose", "--show-diff", "--show-log", "--self-test", "--failed", "--update-check", "--version", "--ansi", "--debug", "--help" - ) ::: testSetArgs + ) ::: standardArgs private val binaryArgs = List( "--grep", "--srcpath", "--buildpath", "--classpath" ) - // true if a test path matches the --grep expression. - private def pathMatchesExpr(path: Path, expr: String) = { - def pred(p: Path) = file2String(p.toFile) contains expr - def greppable(f: Path) = f.isFile && (f hasExtension ("scala", "java")) - def any(d: Path) = d.toDirectory.deepList() exists (f => greppable(f) && pred(f)) - - (path.isFile && pred(path)) || - (path.isDirectory && any(path)) || - (pred(path changeExtension "check")) - } - def main(argstr: String) { val parsed = CommandLineParser(argstr) withUnaryArgs unaryArgs withBinaryArgs binaryArgs - val args = onlyValidTestPaths(parsed.residualArgs) - /* Early return on no args, version, or invalid args */ - if (argstr == "") return NestUI.usage() - if (parsed isSet "--version") return printVersion - if (parsed isSet "--help") return NestUI.usage() + if (parsed isSet "--debug") NestUI.setDebug() + if (parsed isSet "--verbose") NestUI.setVerbose() + if (parsed isSet "--terse") NestUI.setTerse() + + // Early return on no args, version, or invalid args + if (parsed isSet "--version") return echo(versionMsg) + if ((argstr == "") || (parsed isSet "--help")) return NestUI.usage() + + val (individualTests, invalid) = parsed.residualArgs map (p => Path(p)) partition denotesTestPath + if (invalid.nonEmpty) { + if (isPartestVerbose) + invalid foreach (p => echoWarning(s"Discarding invalid test path " + p)) + else if (!isPartestTerse) + echoWarning(s"Discarding ${invalid.size} invalid test paths") + } parsed get "--srcpath" foreach (x => setProp("partest.srcdir", x)) @@ -89,144 +116,102 @@ class ConsoleRunner extends DirectRunner { else if (parsed isSet "--pack") new ConsoleFileManager("build/pack") else new ConsoleFileManager // auto detection, see ConsoleFileManager.findLatest - NestUI._verbose = parsed isSet "--verbose" - fileManager.showDiff = true - // parsed isSet "--show-diff" fileManager.updateCheck = parsed isSet "--update-check" - fileManager.showLog = parsed isSet "--show-log" fileManager.failed = parsed isSet "--failed" - if (parsed isSet "--ansi") NestUI initialize NestUI.MANY - if (parsed isSet "--timeout") fileManager.timeout = parsed("--timeout") - if (parsed isSet "--debug") setProp("partest.debug", "true") + val partestTests = ( + if (parsed isSet "--self-test") TestKinds.testsForPartest + else Nil + ) - def addTestFile(file: File) = { - if (!file.exists) - NestUI.failure("Test file '%s' not found, skipping.\n" format file) - else { - NestUI.verbose("adding test file " + file) - testFiles +:= file - } - } + val grepExpr = parsed get "--grep" getOrElse "" // If --grep is given we suck in every file it matches. - - val grepOption = parsed get "--grep" - val grepPaths = grepOption.toList flatMap { expr => - val subjectDirs = testSetKinds map (srcDir / _ toDirectory) - val testPaths = subjectDirs flatMap (_.list filter stdFilter) - val paths = testPaths filter (p => pathMatchesExpr(p, expr)) - + var grepMessage = "" + val greppedTests = if (grepExpr == "") Nil else { + val paths = grepFor(grepExpr) if (paths.isEmpty) - NestUI.failure("--grep string '%s' matched no tests." format expr) + echoWarning(s"grep string '$grepExpr' matched no tests.\n") - paths map (_.jfile) + paths.sortBy(_.toString) } - val grepMessage = grepOption map (x => "Argument '%s' matched %d test(s)".format(x, grepPaths.size)) getOrElse "" - - grepPaths foreach addTestFile - args foreach (x => addTestFile(new File(x))) - // If no file arguments were given, we assume --all - val enabledTestSets: List[TestSet] = { - val enabledArgs = testSetArgs filter parsed.isSet - - if (args.isEmpty && !(parsed isSet "--grep") && (enabledArgs.isEmpty || (parsed isSet "--all"))) testSets - else enabledArgs map testSetArgMap - } + val isRerun = parsed isSet "--failed" + val rerunTests = if (isRerun) TestKinds.failedTests else Nil + def miscTests = partestTests ++ individualTests ++ greppedTests ++ rerunTests + val givenKinds = standardArgs filter parsed.isSet + val kinds = ( + if (parsed isSet "--all") standardKinds + else if (givenKinds.nonEmpty) givenKinds map (_ stripPrefix "--") + else if (invalid.isEmpty && miscTests.isEmpty && !isRerun) standardKinds // If no kinds, --grep, or individual tests were given, assume --all + else Nil + ) + val kindsTests = kinds flatMap testsFor val dir = if (fileManager.testClasses.isDefined) fileManager.testClassesDir else fileManager.testBuildFile getOrElse { fileManager.latestCompFile.getParentFile.getParentFile.getAbsoluteFile } - val vmBin = javaHome + File.separator + "bin" - val vmName = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo) - val vmOpts = fileManager.JAVA_OPTS - - NestUI.verbose("enabled test sets: " + (enabledTestSets map (_.kind) mkString " ")) - - List( - "Scala compiler classes in: " + dir, - "Scala version is: " + versionMsg, - "Scalac options are: " + fileManager.SCALAC_OPTS, - "Java binaries in: " + vmBin, - "Java runtime is: " + vmName, - "Java options are: " + vmOpts, - "Source directory is: " + srcDir, - "" - ) foreach (x => NestUI verbose (x + "\n")) - - NestUI.verbose("available processors: " + Runtime.getRuntime().availableProcessors()) - - // Dragged down here so it isn't buried under the banner. - if (grepMessage != "") - NestUI.normal(grepMessage + "\n") - - val ((successes, failures), elapsedMillis) = timed(testCheckAll(enabledTestSets)) - val total = successes + failures - - val elapsedSecs = elapsedMillis/1000 - val elapsedMins = elapsedSecs/60 - val elapsedHrs = elapsedMins/60 - val dispMins = elapsedMins - elapsedHrs * 60 - val dispSecs = elapsedSecs - elapsedMins * 60 - - val dispElapsed = { - def form(num: Long) = if (num < 10) "0"+num else ""+num - form(elapsedHrs)+":"+form(dispMins)+":"+form(dispSecs) + def testContributors = { + List( + if (partestTests.isEmpty) "" else "partest self-tests", + if (rerunTests.isEmpty) "" else "previously failed tests", + if (kindsTests.isEmpty) "" else s"${kinds.size} named test categories", + if (greppedTests.isEmpty) "" else s"${greppedTests.size} tests matching '$grepExpr'", + if (individualTests.isEmpty) "" else "specified tests" + ) filterNot (_ == "") mkString ", " } - if (failures == 0) - NestUI.success("All of "+total+" tests were successful (elapsed time: "+dispElapsed+")\n") - else - NestUI.failure(failures+" of "+total+" tests failed (elapsed time: "+dispElapsed+")\n") + def banner = { + val vmBin = javaHome + fileSeparator + "bin" + val vmName = "%s (build %s, %s)".format(javaVmName, javaVmVersion, javaVmInfo) + val vmOpts = fileManager.JAVA_OPTS + + s"""|Scala compiler classes in: $dir + |Scala version is: $versionMsg + |Scalac options are: ${fileManager.SCALAC_OPTS mkString " "} + |Java binaries in: $vmBin + |Java runtime is: $vmName + |Java options are: $vmOpts + |Source directory is: $srcDir + |Available processors: ${Runtime.getRuntime().availableProcessors()} + |Java Classpath: ${sys.props("java.class.path")} + """.stripMargin + } - System exit ( if (failures == errors) 0 else 1 ) - } + chatty(banner) - def runTests(testSet: TestSet): (Int, Int) = { - val TestSet(kind, filter, msg) = testSet + val allTests = (miscTests ++ (kinds flatMap testsFor)).distinct + val grouped = (allTests groupBy kindOf).toList sortBy (x => standardKinds indexOf x._1) - fileManager.getFiles(kind, filter) match { - case Nil => NestUI.verbose("test dir empty\n") ; (0, 0) - case files => - NestUI.verbose("test files: "+files) - NestUI.outline("\n"+msg+"\n") - resultsToStatistics(runTestsForFiles(files, kind)) + totalTests = allTests.size + expectedFailures = propOrNone("partest.errors") match { + case Some(num) => num.toInt + case _ => 0 } - } - - /** - * @return (success count, failure count) - */ - def testCheckAll(enabledSets: List[TestSet]): (Int, Int) = { - def kindOf(f: File) = { - (srcDir relativize Path(f).toCanonical).segments match { - case (".." :: "scaladoc" :: xs) => xs.head - case xs => xs.head + val expectedFailureMessage = if (expectedFailures == 0) "" else s" (expecting $expectedFailures to fail)" + echo(s"Selected $totalTests tests drawn from $testContributors$expectedFailureMessage\n") + + val (_, millis) = timed { + for ((kind, paths) <- grouped) { + val num = paths.size + val ss = if (num == 1) "" else "s" + comment(s"starting $num test$ss in $kind") + val results = runTestsForFiles(paths map (_.jfile), kind) + val (passed, failed) = results partition (_.isOk) + + passedTests ++= passed + failedTests ++= failed + if (failed.nonEmpty) { + comment(passFailString(passed.size, failed.size, 0) + " in " + kind) + } + echo("") } } - - val (valid, invalid) = testFiles partition (x => testSetKinds contains kindOf(x)) - invalid foreach (x => NestUI.failure( - "Invalid test file '%s', skipping.\n".format(x) + - "(Test kind '%s' not in known set '%s')".format(kindOf(x), testSetKinds)) - ) - - val grouped = (valid groupBy kindOf).toList sortBy (x => testSetKinds indexOf x._1) - val runTestsFileLists = - for ((kind, files) <- grouped) yield { - NestUI.outline("\nTesting individual files\n") - resultsToStatistics(runTestsForFiles(files, kind)) - } - - if (enabledSets.nonEmpty) - NestUI.verbose("Run sets: "+enabledSets) - - val results = runTestsFileLists ::: (enabledSets map runTests) - - (results map (_._1) sum, results map (_._2) sum) + this.elapsedMillis = millis + issueSummaryReport() + System exit ( if (isSuccess) 0 else 1 ) } } diff --git a/src/partest/scala/tools/partest/nest/Diff.java b/src/partest/scala/tools/partest/nest/Diff.java deleted file mode 100644 index f69fc6858b..0000000000 --- a/src/partest/scala/tools/partest/nest/Diff.java +++ /dev/null @@ -1,873 +0,0 @@ - -package scala.tools.partest.nest; - -import java.util.Hashtable; - -/** A class to compare IndexedSeqs of objects. The result of comparison - is a list of <code>change</code> objects which form an - edit script. The objects compared are traditionally lines - of text from two files. Comparison options such as "ignore - whitespace" are implemented by modifying the <code>equals</code> - and <code>hashcode</code> methods for the objects compared. -<p> - The basic algorithm is described in: </br> - "An O(ND) Difference Algorithm and its Variations", Eugene Myers, - Algorithmica Vol. 1 No. 2, 1986, p 251. -<p> - This class outputs different results from GNU diff 1.15 on some - inputs. Our results are actually better (smaller change list, smaller - total size of changes), but it would be nice to know why. Perhaps - there is a memory overwrite bug in GNU diff 1.15. - - @author Stuart D. Gathman, translated from GNU diff 1.15 - Copyright (C) 2000 Business Management Systems, Inc. -<p> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. -<p> - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -<p> - You should have received a copy of the <a href=COPYING.txt> - GNU General Public License</a> - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - */ - -public class Diff { - - /** Prepare to find differences between two arrays. Each element of - the arrays is translated to an "equivalence number" based on - the result of <code>equals</code>. The original Object arrays - are no longer needed for computing the differences. They will - be needed again later to print the results of the comparison as - an edit script, if desired. - */ - public Diff(Object[] a,Object[] b) { - Hashtable<Object, Integer> h = new Hashtable<Object, Integer>(a.length + b.length); - filevec[0] = new file_data(a,h); - filevec[1] = new file_data(b,h); - } - - /** 1 more than the maximum equivalence value used for this or its - sibling file. */ - private int equiv_max = 1; - - /** When set to true, the comparison uses a heuristic to speed it up. - With this heuristic, for files with a constant small density - of changes, the algorithm is linear in the file size. */ - public boolean heuristic = false; - - /** When set to true, the algorithm returns a guarranteed minimal - set of changes. This makes things slower, sometimes much slower. */ - public boolean no_discards = false; - - private int[] xvec, yvec; /* IndexedSeqs being compared. */ - private int[] fdiag; /* IndexedSeq, indexed by diagonal, containing - the X coordinate of the point furthest - along the given diagonal in the forward - search of the edit matrix. */ - private int[] bdiag; /* IndexedSeq, indexed by diagonal, containing - the X coordinate of the point furthest - along the given diagonal in the backward - search of the edit matrix. */ - private int fdiagoff, bdiagoff; - private final file_data[] filevec = new file_data[2]; - private int cost; - - /** Find the midpoint of the shortest edit script for a specified - portion of the two files. - - We scan from the beginnings of the files, and simultaneously from the ends, - doing a breadth-first search through the space of edit-sequence. - When the two searches meet, we have found the midpoint of the shortest - edit sequence. - - The value returned is the number of the diagonal on which the midpoint lies. - The diagonal number equals the number of inserted lines minus the number - of deleted lines (counting only lines before the midpoint). - The edit cost is stored into COST; this is the total number of - lines inserted or deleted (counting only lines before the midpoint). - - This function assumes that the first lines of the specified portions - of the two files do not match, and likewise that the last lines do not - match. The caller must trim matching lines from the beginning and end - of the portions it is going to specify. - - Note that if we return the "wrong" diagonal value, or if - the value of bdiag at that diagonal is "wrong", - the worst this can do is cause suboptimal diff output. - It cannot cause incorrect diff output. */ - - private int diag (int xoff, int xlim, int yoff, int ylim) { - final int[] fd = fdiag; // Give the compiler a chance. - final int[] bd = bdiag; // Additional help for the compiler. - final int[] xv = xvec; // Still more help for the compiler. - final int[] yv = yvec; // And more and more . . . - final int dmin = xoff - ylim; // Minimum valid diagonal. - final int dmax = xlim - yoff; // Maximum valid diagonal. - final int fmid = xoff - yoff; // Center diagonal of top-down search. - final int bmid = xlim - ylim; // Center diagonal of bottom-up search. - int fmin = fmid, fmax = fmid; // Limits of top-down search. - int bmin = bmid, bmax = bmid; // Limits of bottom-up search. - /* True if southeast corner is on an odd - diagonal with respect to the northwest. */ - final boolean odd = (fmid - bmid & 1) != 0; - - fd[fdiagoff + fmid] = xoff; - bd[bdiagoff + bmid] = xlim; - - for (int c = 1;; ++c) - { - int d; /* Active diagonal. */ - boolean big_snake = false; - - /* Extend the top-down search by an edit step in each diagonal. */ - if (fmin > dmin) - fd[fdiagoff + --fmin - 1] = -1; - else - ++fmin; - if (fmax < dmax) - fd[fdiagoff + ++fmax + 1] = -1; - else - --fmax; - for (d = fmax; d >= fmin; d -= 2) - { - int x, y, oldx, tlo = fd[fdiagoff + d - 1], thi = fd[fdiagoff + d + 1]; - - if (tlo >= thi) - x = tlo + 1; - else - x = thi; - oldx = x; - y = x - d; - while (x < xlim && y < ylim && xv[x] == yv[y]) { - ++x; ++y; - } - if (x - oldx > 20) - big_snake = true; - fd[fdiagoff + d] = x; - if (odd && bmin <= d && d <= bmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) - { - cost = 2 * c - 1; - return d; - } - } - - /* Similar extend the bottom-up search. */ - if (bmin > dmin) - bd[bdiagoff + --bmin - 1] = Integer.MAX_VALUE; - else - ++bmin; - if (bmax < dmax) - bd[bdiagoff + ++bmax + 1] = Integer.MAX_VALUE; - else - --bmax; - for (d = bmax; d >= bmin; d -= 2) - { - int x, y, oldx, tlo = bd[bdiagoff + d - 1], thi = bd[bdiagoff + d + 1]; - - if (tlo < thi) - x = tlo; - else - x = thi - 1; - oldx = x; - y = x - d; - while (x > xoff && y > yoff && xv[x - 1] == yv[y - 1]) { - --x; --y; - } - if (oldx - x > 20) - big_snake = true; - bd[bdiagoff + d] = x; - if (!odd && fmin <= d && d <= fmax && bd[bdiagoff + d] <= fd[fdiagoff + d]) - { - cost = 2 * c; - return d; - } - } - - /* Heuristic: check occasionally for a diagonal that has made - lots of progress compared with the edit distance. - If we have any such, find the one that has made the most - progress and return it as if it had succeeded. - - With this heuristic, for files with a constant small density - of changes, the algorithm is linear in the file size. */ - - if (c > 200 && big_snake && heuristic) - { - int best = 0; - int bestpos = -1; - - for (d = fmax; d >= fmin; d -= 2) - { - int dd = d - fmid; - if ((fd[fdiagoff + d] - xoff)*2 - dd > 12 * (c + (dd > 0 ? dd : -dd))) - { - if (fd[fdiagoff + d] * 2 - dd > best - && fd[fdiagoff + d] - xoff > 20 - && fd[fdiagoff + d] - d - yoff > 20) - { - int k; - int x = fd[fdiagoff + d]; - - /* We have a good enough best diagonal; - now insist that it end with a significant snake. */ - for (k = 1; k <= 20; k++) - if (xvec[x - k] != yvec[x - d - k]) - break; - - if (k == 21) - { - best = fd[fdiagoff + d] * 2 - dd; - bestpos = d; - } - } - } - } - if (best > 0) - { - cost = 2 * c - 1; - return bestpos; - } - - best = 0; - for (d = bmax; d >= bmin; d -= 2) - { - int dd = d - bmid; - if ((xlim - bd[bdiagoff + d])*2 + dd > 12 * (c + (dd > 0 ? dd : -dd))) - { - if ((xlim - bd[bdiagoff + d]) * 2 + dd > best - && xlim - bd[bdiagoff + d] > 20 - && ylim - (bd[bdiagoff + d] - d) > 20) - { - /* We have a good enough best diagonal; - now insist that it end with a significant snake. */ - int k; - int x = bd[bdiagoff + d]; - - for (k = 0; k < 20; k++) - if (xvec[x + k] != yvec[x - d + k]) - break; - if (k == 20) - { - best = (xlim - bd[bdiagoff + d]) * 2 + dd; - bestpos = d; - } - } - } - } - if (best > 0) - { - cost = 2 * c - 1; - return bestpos; - } - } - } - } - - /** Compare in detail contiguous subsequences of the two files - which are known, as a whole, to match each other. - - The results are recorded in the IndexedSeqs filevec[N].changed_flag, by - storing a 1 in the element for each line that is an insertion or deletion. - - The subsequence of file 0 is [XOFF, XLIM) and likewise for file 1. - - Note that XLIM, YLIM are exclusive bounds. - All line numbers are origin-0 and discarded lines are not counted. */ - - private void compareseq (int xoff, int xlim, int yoff, int ylim) { - /* Slide down the bottom initial diagonal. */ - while (xoff < xlim && yoff < ylim && xvec[xoff] == yvec[yoff]) { - ++xoff; ++yoff; - } - /* Slide up the top initial diagonal. */ - while (xlim > xoff && ylim > yoff && xvec[xlim - 1] == yvec[ylim - 1]) { - --xlim; --ylim; - } - - /* Handle simple cases. */ - if (xoff == xlim) - while (yoff < ylim) - filevec[1].changed_flag[1+filevec[1].realindexes[yoff++]] = true; - else if (yoff == ylim) - while (xoff < xlim) - filevec[0].changed_flag[1+filevec[0].realindexes[xoff++]] = true; - else - { - /* Find a point of correspondence in the middle of the files. */ - - int d = diag (xoff, xlim, yoff, ylim); - int c = cost; - int f = fdiag[fdiagoff + d]; - int b = bdiag[bdiagoff + d]; - - if (c == 1) - { - /* This should be impossible, because it implies that - one of the two subsequences is empty, - and that case was handled above without calling `diag'. - Let's verify that this is true. */ - throw new IllegalArgumentException("Empty subsequence"); - } - else - { - /* Use that point to split this problem into two subproblems. */ - compareseq (xoff, b, yoff, b - d); - /* This used to use f instead of b, - but that is incorrect! - It is not necessarily the case that diagonal d - has a snake from b to f. */ - compareseq (b, xlim, b - d, ylim); - } - } - } - - /** Discard lines from one file that have no matches in the other file. - */ - - private void discard_confusing_lines() { - filevec[0].discard_confusing_lines(filevec[1]); - filevec[1].discard_confusing_lines(filevec[0]); - } - - private boolean inhibit = false; - - /** Adjust inserts/deletes of blank lines to join changes - as much as possible. - */ - - private void shift_boundaries() { - if (inhibit) - return; - filevec[0].shift_boundaries(filevec[1]); - filevec[1].shift_boundaries(filevec[0]); - } - - public interface ScriptBuilder { - /** Scan the tables of which lines are inserted and deleted, - producing an edit script. - @param changed0 true for lines in first file which do not match 2nd - @param len0 number of lines in first file - @param changed1 true for lines in 2nd file which do not match 1st - @param len1 number of lines in 2nd file - @return a linked list of changes - or null - */ - public change build_script( - boolean[] changed0,int len0, - boolean[] changed1,int len1 - ); - } - - /** Scan the tables of which lines are inserted and deleted, - producing an edit script in reverse order. */ - - static class ReverseScript implements ScriptBuilder { - public change build_script( - final boolean[] changed0,int len0, - final boolean[] changed1,int len1) - { - change script = null; - int i0 = 0, i1 = 0; - while (i0 < len0 || i1 < len1) { - if (changed0[1+i0] || changed1[1+i1]) { - int line0 = i0, line1 = i1; - - /* Find # lines changed here in each file. */ - while (changed0[1+i0]) ++i0; - while (changed1[1+i1]) ++i1; - - /* Record this change. */ - script = new change(line0, line1, i0 - line0, i1 - line1, script); - } - - /* We have reached lines in the two files that match each other. */ - i0++; i1++; - } - - return script; - } - } - - static class ForwardScript implements ScriptBuilder { - /** Scan the tables of which lines are inserted and deleted, - producing an edit script in forward order. */ - public change build_script( - final boolean[] changed0,int len0, - final boolean[] changed1,int len1) - { - change script = null; - int i0 = len0, i1 = len1; - - while (i0 >= 0 || i1 >= 0) - { - if (changed0[i0] || changed1[i1]) - { - int line0 = i0, line1 = i1; - - /* Find # lines changed here in each file. */ - while (changed0[i0]) --i0; - while (changed1[i1]) --i1; - - /* Record this change. */ - script = new change(i0, i1, line0 - i0, line1 - i1, script); - } - - /* We have reached lines in the two files that match each other. */ - i0--; i1--; - } - - return script; - } - } - - /** Standard ScriptBuilders. */ - public final static ScriptBuilder - forwardScript = new ForwardScript(), - reverseScript = new ReverseScript(); - - /* Report the differences of two files. DEPTH is the current directory - depth. */ - public final change diff_2(final boolean reverse) { - return diff(reverse ? reverseScript : forwardScript); - } - - /** Get the results of comparison as an edit script. The script - is described by a list of changes. The standard ScriptBuilder - implementations provide for forward and reverse edit scripts. - Alternate implementations could, for instance, list common elements - instead of differences. - @param bld an object to build the script from change flags - @return the head of a list of changes - */ - public change diff(final ScriptBuilder bld) { - - /* Some lines are obviously insertions or deletions - because they don't match anything. Detect them now, - and avoid even thinking about them in the main comparison algorithm. */ - - discard_confusing_lines (); - - /* Now do the main comparison algorithm, considering just the - undiscarded lines. */ - - xvec = filevec[0].undiscarded; - yvec = filevec[1].undiscarded; - - int diags = - filevec[0].nondiscarded_lines + filevec[1].nondiscarded_lines + 3; - fdiag = new int[diags]; - fdiagoff = filevec[1].nondiscarded_lines + 1; - bdiag = new int[diags]; - bdiagoff = filevec[1].nondiscarded_lines + 1; - - compareseq (0, filevec[0].nondiscarded_lines, - 0, filevec[1].nondiscarded_lines); - fdiag = null; - bdiag = null; - - /* Modify the results slightly to make them prettier - in cases where that can validly be done. */ - - shift_boundaries (); - - /* Get the results of comparison in the form of a chain - of `struct change's -- an edit script. */ - return bld.build_script( - filevec[0].changed_flag, - filevec[0].buffered_lines, - filevec[1].changed_flag, - filevec[1].buffered_lines - ); - - } - - /** The result of comparison is an "edit script": a chain of change objects. - Each change represents one place where some lines are deleted - and some are inserted. - - LINE0 and LINE1 are the first affected lines in the two files (origin 0). - DELETED is the number of lines deleted here from file 0. - INSERTED is the number of lines inserted here in file 1. - - If DELETED is 0 then LINE0 is the number of the line before - which the insertion was done; vice versa for INSERTED and LINE1. */ - - public static class change { - /** Previous or next edit command. */ - public change link; - /** # lines of file 1 changed here. */ - public final int inserted; - /** # lines of file 0 changed here. */ - public final int deleted; - /** Line number of 1st deleted line. */ - public final int line0; - /** Line number of 1st inserted line. */ - public final int line1; - - /** Cons an additional entry onto the front of an edit script OLD. - LINE0 and LINE1 are the first affected lines in the two files (origin 0). - DELETED is the number of lines deleted here from file 0. - INSERTED is the number of lines inserted here in file 1. - - If DELETED is 0 then LINE0 is the number of the line before - which the insertion was done; vice versa for INSERTED and LINE1. */ - public change(int line0, int line1, int deleted, int inserted, change old) { - this.line0 = line0; - this.line1 = line1; - this.inserted = inserted; - this.deleted = deleted; - this.link = old; - //System.err.println(line0+","+line1+","+inserted+","+deleted); - } - } - - /** Data on one input file being compared. - */ - - class file_data { - - /** Allocate changed array for the results of comparison. */ - void clear() { - /* Allocate a flag for each line of each file, saying whether that line - is an insertion or deletion. - Allocate an extra element, always zero, at each end of each IndexedSeq. - */ - changed_flag = new boolean[buffered_lines + 2]; - } - - /** Return equiv_count[I] as the number of lines in this file - that fall in equivalence class I. - @return the array of equivalence class counts. - */ - int[] equivCount() { - int[] equiv_count = new int[equiv_max]; - for (int i = 0; i < buffered_lines; ++i) - ++equiv_count[equivs[i]]; - return equiv_count; - } - - /** Discard lines that have no matches in another file. - - A line which is discarded will not be considered by the actual - comparison algorithm; it will be as if that line were not in the file. - The file's `realindexes' table maps virtual line numbers - (which don't count the discarded lines) into real line numbers; - this is how the actual comparison algorithm produces results - that are comprehensible when the discarded lines are counted. -<p> - When we discard a line, we also mark it as a deletion or insertion - so that it will be printed in the output. - @param f the other file - */ - void discard_confusing_lines(file_data f) { - clear(); - /* Set up table of which lines are going to be discarded. */ - final byte[] discarded = discardable(f.equivCount()); - - /* Don't really discard the provisional lines except when they occur - in a run of discardables, with nonprovisionals at the beginning - and end. */ - filterDiscards(discarded); - - /* Actually discard the lines. */ - discard(discarded); - } - - /** Mark to be discarded each line that matches no line of another file. - If a line matches many lines, mark it as provisionally discardable. - @see equivCount() - @param counts The count of each equivalence number for the other file. - @return 0=nondiscardable, 1=discardable or 2=provisionally discardable - for each line - */ - - private byte[] discardable(final int[] counts) { - final int end = buffered_lines; - final byte[] discards = new byte[end]; - final int[] equivs = this.equivs; - int many = 5; - int tem = end / 64; - - /* Multiply MANY by approximate square root of number of lines. - That is the threshold for provisionally discardable lines. */ - while ((tem = tem >> 2) > 0) - many *= 2; - - for (int i = 0; i < end; i++) - { - int nmatch; - if (equivs[i] == 0) - continue; - nmatch = counts[equivs[i]]; - if (nmatch == 0) - discards[i] = 1; - else if (nmatch > many) - discards[i] = 2; - } - return discards; - } - - /** Don't really discard the provisional lines except when they occur - in a run of discardables, with nonprovisionals at the beginning - and end. */ - - private void filterDiscards(final byte[] discards) { - final int end = buffered_lines; - - for (int i = 0; i < end; i++) - { - /* Cancel provisional discards not in middle of run of discards. */ - if (discards[i] == 2) - discards[i] = 0; - else if (discards[i] != 0) - { - /* We have found a nonprovisional discard. */ - int j; - int length; - int provisional = 0; - - /* Find end of this run of discardable lines. - Count how many are provisionally discardable. */ - for (j = i; j < end; j++) - { - if (discards[j] == 0) - break; - if (discards[j] == 2) - ++provisional; - } - - /* Cancel provisional discards at end, and shrink the run. */ - while (j > i && discards[j - 1] == 2) { - discards[--j] = 0; --provisional; - } - - /* Now we have the length of a run of discardable lines - whose first and last are not provisional. */ - length = j - i; - - /* If 1/4 of the lines in the run are provisional, - cancel discarding of all provisional lines in the run. */ - if (provisional * 4 > length) - { - while (j > i) - if (discards[--j] == 2) - discards[j] = 0; - } - else - { - int consec; - int minimum = 1; - int tem = length / 4; - - /* MINIMUM is approximate square root of LENGTH/4. - A subrun of two or more provisionals can stand - when LENGTH is at least 16. - A subrun of 4 or more can stand when LENGTH >= 64. */ - while ((tem = tem >> 2) > 0) - minimum *= 2; - minimum++; - - /* Cancel any subrun of MINIMUM or more provisionals - within the larger run. */ - for (j = 0, consec = 0; j < length; j++) - if (discards[i + j] != 2) - consec = 0; - else if (minimum == ++consec) - /* Back up to start of subrun, to cancel it all. */ - j -= consec; - else if (minimum < consec) - discards[i + j] = 0; - - /* Scan from beginning of run - until we find 3 or more nonprovisionals in a row - or until the first nonprovisional at least 8 lines in. - Until that point, cancel any provisionals. */ - for (j = 0, consec = 0; j < length; j++) - { - if (j >= 8 && discards[i + j] == 1) - break; - if (discards[i + j] == 2) { - consec = 0; discards[i + j] = 0; - } - else if (discards[i + j] == 0) - consec = 0; - else - consec++; - if (consec == 3) - break; - } - - /* I advances to the last line of the run. */ - i += length - 1; - - /* Same thing, from end. */ - for (j = 0, consec = 0; j < length; j++) - { - if (j >= 8 && discards[i - j] == 1) - break; - if (discards[i - j] == 2) { - consec = 0; discards[i - j] = 0; - } - else if (discards[i - j] == 0) - consec = 0; - else - consec++; - if (consec == 3) - break; - } - } - } - } - } - - /** Actually discard the lines. - @param discards flags lines to be discarded - */ - private void discard(final byte[] discards) { - final int end = buffered_lines; - int j = 0; - for (int i = 0; i < end; ++i) - if (no_discards || discards[i] == 0) - { - undiscarded[j] = equivs[i]; - realindexes[j++] = i; - } - else - changed_flag[1+i] = true; - nondiscarded_lines = j; - } - - file_data(Object[] data, Hashtable<Object, Integer> h) { - buffered_lines = data.length; - - equivs = new int[buffered_lines]; - undiscarded = new int[buffered_lines]; - realindexes = new int[buffered_lines]; - - for (int i = 0; i < data.length; ++i) { - Integer ir = h.get(data[i]); - if (ir == null) - h.put(data[i], new Integer(equivs[i] = equiv_max++)); - else - equivs[i] = ir.intValue(); - } - } - - /** Adjust inserts/deletes of blank lines to join changes - as much as possible. - - We do something when a run of changed lines include a blank - line at one end and have an excluded blank line at the other. - We are free to choose which blank line is included. - `compareseq' always chooses the one at the beginning, - but usually it is cleaner to consider the following blank line - to be the "change". The only exception is if the preceding blank line - would join this change to other changes. - @param f the file being compared against - */ - - void shift_boundaries(file_data f) { - final boolean[] changed = changed_flag; - final boolean[] other_changed = f.changed_flag; - int i = 0; - int j = 0; - int i_end = buffered_lines; - int preceding = -1; - int other_preceding = -1; - - for (;;) - { - int start, end, other_start; - - /* Scan forwards to find beginning of another run of changes. - Also keep track of the corresponding point in the other file. */ - - while (i < i_end && !changed[1+i]) - { - while (other_changed[1+j++]) - /* Non-corresponding lines in the other file - will count as the preceding batch of changes. */ - other_preceding = j; - i++; - } - - if (i == i_end) - break; - - start = i; - other_start = j; - - for (;;) - { - /* Now find the end of this run of changes. */ - - while (i < i_end && changed[1+i]) i++; - end = i; - - /* If the first changed line matches the following unchanged one, - and this run does not follow right after a previous run, - and there are no lines deleted from the other file here, - then classify the first changed line as unchanged - and the following line as changed in its place. */ - - /* You might ask, how could this run follow right after another? - Only because the previous run was shifted here. */ - - if (end != i_end - && equivs[start] == equivs[end] - && !other_changed[1+j] - && end != i_end - && !((preceding >= 0 && start == preceding) - || (other_preceding >= 0 - && other_start == other_preceding))) - { - changed[1+end++] = true; - changed[1+start++] = false; - ++i; - /* Since one line-that-matches is now before this run - instead of after, we must advance in the other file - to keep in synch. */ - ++j; - } - else - break; - } - - preceding = i; - other_preceding = j; - } - } - - /** Number of elements (lines) in this file. */ - final int buffered_lines; - - /** IndexedSeq, indexed by line number, containing an equivalence code for - each line. It is this IndexedSeq that is actually compared with that - of another file to generate differences. */ - private final int[] equivs; - - /** IndexedSeq, like the previous one except that - the elements for discarded lines have been squeezed out. */ - final int[] undiscarded; - - /** IndexedSeq mapping virtual line numbers (not counting discarded lines) - to real ones (counting those lines). Both are origin-0. */ - final int[] realindexes; - - /** Total number of nondiscarded lines. */ - int nondiscarded_lines; - - /** Array, indexed by real origin-1 line number, - containing true for a line that is an insertion or a deletion. - The results of comparison are stored here. */ - boolean[] changed_flag; - - } -} diff --git a/src/partest/scala/tools/partest/nest/DiffPrint.java b/src/partest/scala/tools/partest/nest/DiffPrint.java deleted file mode 100644 index 31f9a1bc79..0000000000 --- a/src/partest/scala/tools/partest/nest/DiffPrint.java +++ /dev/null @@ -1,606 +0,0 @@ - -package scala.tools.partest.nest; - -import java.io.*; -import java.util.Vector; -import java.util.Date; -//import com.objectspace.jgl.predicates.UnaryPredicate; - -interface UnaryPredicate { - boolean execute(Object obj); -} - -/** A simple framework for printing change lists produced by <code>Diff</code>. - @see bmsi.util.Diff - @author Stuart D. Gathman - Copyright (C) 2000 Business Management Systems, Inc. -<p> - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. -<p> - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -<p> - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -public class DiffPrint { - /** A Base class for printing edit scripts produced by Diff. - This class divides the change list into "hunks", and calls - <code>print_hunk</code> for each hunk. Various utility methods - are provided as well. - */ - public static abstract class Base { - protected Base(Object[] a,Object[] b, Writer w) { - outfile = new PrintWriter(w); - file0 = a; - file1 = b; - } - /** Set to ignore certain kinds of lines when printing - an edit script. For example, ignoring blank lines or comments. - */ - protected UnaryPredicate ignore = null; - - /** Set to the lines of the files being compared. - */ - protected Object[] file0, file1; - - /** Divide SCRIPT into pieces by calling HUNKFUN and - print each piece with PRINTFUN. - Both functions take one arg, an edit script. - - PRINTFUN takes a subscript which belongs together (with a null - link at the end) and prints it. */ - public void print_script(Diff.change script) { - Diff.change next = script; - - while (next != null) - { - Diff.change t, end; - - /* Find a set of changes that belong together. */ - t = next; - end = hunkfun(next); - - /* Disconnect them from the rest of the changes, - making them a hunk, and remember the rest for next iteration. */ - next = end.link; - end.link = null; - //if (DEBUG) - // debug_script(t); - - /* Print this hunk. */ - print_hunk(t); - - /* Reconnect the script so it will all be freed properly. */ - end.link = next; - } - outfile.flush(); - } - - /** Called with the tail of the script - and returns the last link that belongs together with the start - of the tail. */ - - protected Diff.change hunkfun(Diff.change hunk) { - return hunk; - } - - protected int first0, last0, first1, last1, deletes, inserts; - protected PrintWriter outfile; - - /** Look at a hunk of edit script and report the range of lines in each file - that it applies to. HUNK is the start of the hunk, which is a chain - of `struct change'. The first and last line numbers of file 0 are stored - in *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1. - Note that these are internal line numbers that count from 0. - - If no lines from file 0 are deleted, then FIRST0 is LAST0+1. - - Also set *DELETES nonzero if any lines of file 0 are deleted - and set *INSERTS nonzero if any lines of file 1 are inserted. - If only ignorable lines are inserted or deleted, both are - set to 0. */ - - protected void analyze_hunk(Diff.change hunk) { - int f0, l0 = 0, f1, l1 = 0, show_from = 0, show_to = 0; - int i; - Diff.change next; - boolean nontrivial = (ignore == null); - - show_from = show_to = 0; - - f0 = hunk.line0; - f1 = hunk.line1; - - for (next = hunk; next != null; next = next.link) - { - l0 = next.line0 + next.deleted - 1; - l1 = next.line1 + next.inserted - 1; - show_from += next.deleted; - show_to += next.inserted; - for (i = next.line0; i <= l0 && ! nontrivial; i++) - if (!ignore.execute(file0[i])) - nontrivial = true; - for (i = next.line1; i <= l1 && ! nontrivial; i++) - if (!ignore.execute(file1[i])) - nontrivial = true; - } - - first0 = f0; - last0 = l0; - first1 = f1; - last1 = l1; - - /* If all inserted or deleted lines are ignorable, - tell the caller to ignore this hunk. */ - - if (!nontrivial) - show_from = show_to = 0; - - deletes = show_from; - inserts = show_to; - } - - /** Print the script header which identifies the files compared. */ - protected void print_header(String filea, String fileb) { } - - protected abstract void print_hunk(Diff.change hunk); - - protected void print_1_line(String pre,Object linbuf) { - outfile.println(pre + linbuf.toString()); - } - - /** Print a pair of line numbers with SEPCHAR, translated for file FILE. - If the two numbers are identical, print just one number. - - Args A and B are internal line numbers. - We print the translated (real) line numbers. */ - - protected void print_number_range (char sepchar, int a, int b) { - /* Note: we can have B < A in the case of a range of no lines. - In this case, we should print the line number before the range, - which is B. */ - if (++b > ++a) - outfile.print("" + a + sepchar + b); - else - outfile.print(b); - } - - public static char change_letter(int inserts, int deletes) { - if (inserts == 0) - return 'd'; - else if (deletes == 0) - return 'a'; - else - return 'c'; - } - } - - /** Print a change list in the standard diff format. - */ - public static class NormalPrint extends Base { - - public NormalPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - /** Print a hunk of a normal diff. - This is a contiguous portion of a complete edit script, - describing changes in consecutive lines. */ - - protected void print_hunk (Diff.change hunk) { - - /* Determine range of line numbers involved in each file. */ - analyze_hunk(hunk); - if (deletes == 0 && inserts == 0) - return; - - /* Print out the line number header for this hunk */ - print_number_range (',', first0, last0); - outfile.print(change_letter(inserts, deletes)); - print_number_range (',', first1, last1); - outfile.println(); - - /* Print the lines that the first file has. */ - if (deletes != 0) - for (int i = first0; i <= last0; i++) - print_1_line ("< ", file0[i]); - - if (inserts != 0 && deletes != 0) - outfile.println("---"); - - /* Print the lines that the second file has. */ - if (inserts != 0) - for (int i = first1; i <= last1; i++) - print_1_line ("> ", file1[i]); - } - } - - /** Prints an edit script in a format suitable for input to <code>ed</code>. - The edit script must be generated with the reverse option to - be useful as actual <code>ed</code> input. - */ - public static class EdPrint extends Base { - - public EdPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - /** Print a hunk of an ed diff */ - protected void print_hunk(Diff.change hunk) { - - /* Determine range of line numbers involved in each file. */ - analyze_hunk (hunk); - if (deletes == 0 && inserts == 0) - return; - - /* Print out the line number header for this hunk */ - print_number_range (',', first0, last0); - outfile.println(change_letter(inserts, deletes)); - - /* Print new/changed lines from second file, if needed */ - if (inserts != 0) - { - boolean inserting = true; - for (int i = first1; i <= last1; i++) - { - /* Resume the insert, if we stopped. */ - if (! inserting) - outfile.println(i - first1 + first0 + "a"); - inserting = true; - - /* If the file's line is just a dot, it would confuse `ed'. - So output it with a double dot, and set the flag LEADING_DOT - so that we will output another ed-command later - to change the double dot into a single dot. */ - - if (".".equals(file1[i])) - { - outfile.println(".."); - outfile.println("."); - /* Now change that double dot to the desired single dot. */ - outfile.println(i - first1 + first0 + 1 + "s/^\\.\\././"); - inserting = false; - } - else - /* Line is not `.', so output it unmodified. */ - print_1_line ("", file1[i]); - } - - /* End insert mode, if we are still in it. */ - if (inserting) - outfile.println("."); - } - } - } - - /** Prints an edit script in context diff format. This and its - 'unified' variation is used for source code patches. - */ - public static class ContextPrint extends Base { - - protected int context = 3; - - public ContextPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - protected void print_context_label (String mark, File inf, String label) { - if (label != null) - outfile.println(mark + ' ' + label); - else if (inf.lastModified() > 0) - // FIXME: use DateFormat to get precise format needed. - outfile.println( - mark + ' ' + inf.getPath() + '\t' + new Date(inf.lastModified()) - ); - else - /* Don't pretend that standard input is ancient. */ - outfile.println(mark + ' ' + inf.getPath()); - } - - public void print_header(String filea,String fileb) { - print_context_label ("***", new File(filea), filea); - print_context_label ("---", new File(fileb), fileb); - } - - /** If function_regexp defined, search for start of function. */ - private String find_function(Object[] lines, int start) { - return null; - } - - protected void print_function(Object[] file,int start) { - String function = find_function (file0, first0); - if (function != null) { - outfile.print(" "); - outfile.print( - (function.length() < 40) ? function : function.substring(0,40) - ); - } - } - - protected void print_hunk(Diff.change hunk) { - - /* Determine range of line numbers involved in each file. */ - - analyze_hunk (hunk); - - if (deletes == 0 && inserts == 0) - return; - - /* Include a context's width before and after. */ - - first0 = Math.max(first0 - context, 0); - first1 = Math.max(first1 - context, 0); - last0 = Math.min(last0 + context, file0.length - 1); - last1 = Math.min(last1 + context, file1.length - 1); - - - outfile.print("***************"); - - /* If we looked for and found a function this is part of, - include its name in the header of the diff section. */ - print_function (file0, first0); - - outfile.println(); - outfile.print("*** "); - print_number_range (',', first0, last0); - outfile.println(" ****"); - - if (deletes != 0) { - Diff.change next = hunk; - - for (int i = first0; i <= last0; i++) { - /* Skip past changes that apply (in file 0) - only to lines before line I. */ - - while (next != null && next.line0 + next.deleted <= i) - next = next.link; - - /* Compute the marking for line I. */ - - String prefix = " "; - if (next != null && next.line0 <= i) - /* The change NEXT covers this line. - If lines were inserted here in file 1, this is "changed". - Otherwise it is "deleted". */ - prefix = (next.inserted > 0) ? "!" : "-"; - - print_1_line (prefix, file0[i]); - } - } - - outfile.print("--- "); - print_number_range (',', first1, last1); - outfile.println(" ----"); - - if (inserts != 0) { - Diff.change next = hunk; - - for (int i = first1; i <= last1; i++) { - /* Skip past changes that apply (in file 1) - only to lines before line I. */ - - while (next != null && next.line1 + next.inserted <= i) - next = next.link; - - /* Compute the marking for line I. */ - - String prefix = " "; - if (next != null && next.line1 <= i) - /* The change NEXT covers this line. - If lines were deleted here in file 0, this is "changed". - Otherwise it is "inserted". */ - prefix = (next.deleted > 0) ? "!" : "+"; - - print_1_line (prefix, file1[i]); - } - } - } - } - - /** Prints an edit script in context diff format. This and its - 'unified' variation is used for source code patches. - */ - public static class UnifiedPrint extends ContextPrint { - - public UnifiedPrint(Object[] a,Object[] b, Writer w) { - super(a,b,w); - } - - public void print_header(String filea,String fileb) { - print_context_label ("---", new File(filea), filea); - print_context_label ("+++", new File(fileb), fileb); - } - - private void print_number_range (int a, int b) { - //translate_range (file, a, b, &trans_a, &trans_b); - - /* Note: we can have B < A in the case of a range of no lines. - In this case, we should print the line number before the range, - which is B. */ - if (b < a) - outfile.print(b + ",0"); - else - super.print_number_range(',',a,b); - } - - protected void print_hunk(Diff.change hunk) { - /* Determine range of line numbers involved in each file. */ - analyze_hunk (hunk); - - if (deletes == 0 && inserts == 0) - return; - - /* Include a context's width before and after. */ - - first0 = Math.max(first0 - context, 0); - first1 = Math.max(first1 - context, 0); - last0 = Math.min(last0 + context, file0.length - 1); - last1 = Math.min(last1 + context, file1.length - 1); - - - - outfile.print("@@ -"); - print_number_range (first0, last0); - outfile.print(" +"); - print_number_range (first1, last1); - outfile.print(" @@"); - - /* If we looked for and found a function this is part of, - include its name in the header of the diff section. */ - print_function(file0,first0); - - outfile.println(); - - Diff.change next = hunk; - int i = first0; - int j = first1; - - while (i <= last0 || j <= last1) { - - /* If the line isn't a difference, output the context from file 0. */ - - if (next == null || i < next.line0) { - outfile.print(' '); - print_1_line ("", file0[i++]); - j++; - } - else { - /* For each difference, first output the deleted part. */ - - int k = next.deleted; - while (k-- > 0) { - outfile.print('-'); - print_1_line ("", file0[i++]); - } - - /* Then output the inserted part. */ - - k = next.inserted; - while (k-- > 0) { - outfile.print('+'); - print_1_line ("", file1[j++]); - } - - /* We're done with this hunk, so on to the next! */ - - next = next.link; - } - } - } - } - - - /** Read a text file into an array of String. This provides basic diff - functionality. A more advanced diff utility will use specialized - objects to represent the text lines, with options to, for example, - convert sequences of whitespace to a single space for comparison - purposes. - */ - static String[] slurp(String file) throws IOException { - BufferedReader rdr = new BufferedReader(new FileReader(file)); - Vector<String> s = new Vector<String>(); - for (;;) { - String line = rdr.readLine(); - if (line == null) break; - s.addElement(line); - } - String[] a = new String[s.size()]; - s.copyInto(a); - return a; - } - - public static void main(String[] argv) throws IOException { - String filea = argv[argv.length - 2]; - String fileb = argv[argv.length - 1]; - String[] a = slurp(filea); - String[] b = slurp(fileb); - Diff d = new Diff(a,b); - char style = 'n'; - for (int i = 0; i < argv.length - 2; ++i) { - String f = argv[i]; - if (f.startsWith("-")) { - for (int j = 1; j < f.length(); ++j) { - switch (f.charAt(j)) { - case 'e': // Ed style - style = 'e'; break; - case 'c': // Context diff - style = 'c'; break; - case 'u': - style = 'u'; break; - } - } - } - } - boolean reverse = style == 'e'; - Diff.change script = d.diff_2(reverse); - if (script == null) - System.err.println("No differences"); - else { - Base p; - Writer w = new OutputStreamWriter(System.out); - switch (style) { - case 'e': - p = new EdPrint(a,b,w); break; - case 'c': - p = new ContextPrint(a,b,w); break; - case 'u': - p = new UnifiedPrint(a,b,w); break; - default: - p = new NormalPrint(a,b,w); - } - p.print_header(filea,fileb); - p.print_script(script); - } - } - - public static void doDiff(String[] argv, Writer w) throws IOException { - String filea = argv[argv.length - 2]; - String fileb = argv[argv.length - 1]; - String[] a = slurp(filea); - String[] b = slurp(fileb); - Diff d = new Diff(a,b); - char style = 'n'; - for (int i = 0; i < argv.length - 2; ++i) { - String f = argv[i]; - if (f.startsWith("-")) { - for (int j = 1; j < f.length(); ++j) { - switch (f.charAt(j)) { - case 'e': // Ed style - style = 'e'; break; - case 'c': // Context diff - style = 'c'; break; - case 'u': - style = 'u'; break; - } - } - } - } - boolean reverse = style == 'e'; - Diff.change script = d.diff_2(reverse); - if (script == null) - w.write("No differences\n"); - else { - Base p; - switch (style) { - case 'e': - p = new EdPrint(a,b,w); break; - case 'c': - p = new ContextPrint(a,b,w); break; - case 'u': - p = new UnifiedPrint(a,b,w); break; - default: - p = new NormalPrint(a,b,w); - } - p.print_header(filea,fileb); - p.print_script(script); - } - } - -} diff --git a/src/partest/scala/tools/partest/nest/DirectCompiler.scala b/src/partest/scala/tools/partest/nest/DirectCompiler.scala new file mode 100644 index 0000000000..650b6c35c8 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/DirectCompiler.scala @@ -0,0 +1,105 @@ +/* NEST (New Scala Test) + * Copyright 2007-2013 LAMP/EPFL + * @author Philipp Haller + */ + +package scala.tools.partest +package nest + +import scala.tools.nsc.{ Global, Settings, CompilerCommand, FatalError } +import scala.tools.nsc.reporters.{ Reporter, ConsoleReporter } +import scala.tools.nsc.util.{ FakePos, stackTraceString } +import scala.tools.nsc.Properties.{ setProp, propOrEmpty } +import scala.reflect.io.AbstractFile +import scala.reflect.internal.util.Position +import java.io.{ BufferedReader, PrintWriter, FileReader, Writer, FileWriter } + +class ExtConsoleReporter(settings: Settings, val writer: PrintWriter) extends ConsoleReporter(settings, Console.in, writer) { + shortname = true + // override def error(pos: Position, msg: String): Unit +} + +class TestSettings(cp: String, error: String => Unit) extends Settings(error) { + def this(cp: String) = this(cp, _ => ()) + + nowarnings.value = false + encoding.value = "UTF-8" + classpath.value = cp +} + +class PartestGlobal(settings: Settings, reporter: Reporter) extends Global(settings, reporter) { + // override def abort(msg: String): Nothing + // override def globalError(msg: String): Unit + // override def supplementErrorMessage(msg: String): String +} +class DirectCompiler(val fileManager: FileManager) { + def newGlobal(settings: Settings, reporter: Reporter): PartestGlobal = + new PartestGlobal(settings, reporter) + + def newGlobal(settings: Settings, logWriter: FileWriter): Global = + newGlobal(settings, new ExtConsoleReporter(settings, new PrintWriter(logWriter))) + + def newSettings(): TestSettings = new TestSettings(fileManager.LATEST_LIB) + def newSettings(outdir: String): TestSettings = { + val cp = ClassPath.join(fileManager.LATEST_LIB, outdir) + val s = new TestSettings(cp) + s.outdir.value = outdir + s + } + + def compile(runner: Runner, opts0: List[String], sources: List[File]): TestState = { + import runner._ + + val testSettings = new TestSettings(ClassPath.join(fileManager.LATEST_LIB, outDir.getPath)) + val logWriter = new FileWriter(logFile) + val srcDir = if (testFile.isDirectory) testFile else Path(testFile).parent.jfile + val opts = fileManager.updatePluginPath(opts0, AbstractFile getDirectory outDir, AbstractFile getDirectory srcDir) + val command = new CompilerCommand(opts, testSettings) + val global = newGlobal(testSettings, logWriter) + val reporter = global.reporter.asInstanceOf[ExtConsoleReporter] + def errorCount = reporter.ERROR.count + + def defineSettings(s: Settings) = { + s.outputDirs setSingleOutput outDir.getPath + // adding codelib.jar to the classpath + // codelib provides the possibility to override standard reify + // this shields the massive amount of reification tests from changes in the API + prependToClasspaths(s, codelib) + s.classpath append fileManager.CLASSPATH // adding this why? + + // add the instrumented library version to classpath + if (kind == "specialized") + prependToClasspaths(s, speclib) + + // check that option processing succeeded + opts0.isEmpty || command.ok + } + + if (!defineSettings(testSettings)) + if (opts0.isEmpty) + reporter.error(null, s"bad settings: $testSettings") + else + reporter.error(null, opts0.mkString("bad options: ", space, "")) + + def ids = sources.map(_.testIdent) mkString space + vlog(s"% scalac $ids") + + def execCompile() = + if (command.shouldStopWithInfo) { + logWriter append (command getInfoMessage global) + runner genFail "compilation stopped with info" + } else { + new global.Run compile sources.map(_.getPath) + if (!reporter.hasErrors) runner.genPass() + else { + reporter.printSummary() + reporter.writer.close() + runner.genFail(s"compilation failed with $errorCount errors") + } + } + + try { execCompile() } + catch { case t: Throwable => reporter.error(null, t.getMessage) ; runner.genCrash(t) } + finally { logWriter.close() } + } +} diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala index 7e4c3b842c..49dd39c344 100644 --- a/src/partest/scala/tools/partest/nest/DirectRunner.scala +++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala @@ -3,14 +3,12 @@ * @author Philipp Haller */ -// $Id$ - package scala.tools.partest package nest import java.io.File import scala.util.Properties.setProp -import scala.tools.nsc.util.ScalaClassLoader +import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional } import scala.tools.nsc.io.Path import scala.collection.{ mutable, immutable } import java.util.concurrent._ @@ -22,41 +20,33 @@ trait DirectRunner { import PartestDefaults.numThreads - def denotesTestFile(arg: String) = Path(arg).hasExtension("scala", "res", "xml") - def denotesTestDir(arg: String) = Path(arg).ifDirectory(_.files.nonEmpty) exists (x => x) - def denotesTestPath(arg: String) = denotesTestDir(arg) || denotesTestFile(arg) - - /** No duplicate, no empty directories, don't mess with this unless - * you like partest hangs. - */ - def onlyValidTestPaths[T](args: List[T]): List[T] = { - args.distinct filter (arg => denotesTestPath("" + arg) || { - NestUI.warning("Discarding invalid test path '%s'\n" format arg) - false - }) - } - def runTestsForFiles(_kindFiles: List[File], kind: String): immutable.Map[String, TestState] = { - System.setProperty("line.separator", "\n") + Thread.setDefaultUncaughtExceptionHandler( + new Thread.UncaughtExceptionHandler { + def uncaughtException(thread: Thread, t: Throwable) { + val t1 = Exceptional unwrap t + System.err.println(s"Uncaught exception on thread $thread: $t1") + t1.printStackTrace() + } + } + ) + def runTestsForFiles(kindFiles: List[File], kind: String): List[TestState] = { - val allUrls = PathSettings.scalaCheck.toURL :: fileManager.latestUrls - val scalaCheckParentClassLoader = ScalaClassLoader.fromURLs(allUrls) - val kindFiles = onlyValidTestPaths(_kindFiles) - val pool = Executors.newFixedThreadPool(numThreads) - val manager = new RunnerManager(kind, fileManager, TestRunParams(scalaCheckParentClassLoader)) - val futures = kindFiles map (f => (f, pool submit callable(manager runTest f))) toMap + NestUI.resetTestNumber() - pool.shutdown() + val allUrls = PathSettings.scalaCheck.toURL :: fileManager.latestUrls + val parentClassLoader = ScalaClassLoader fromURLs allUrls + val pool = Executors newFixedThreadPool numThreads + val manager = new RunnerManager(kind, fileManager, TestRunParams(parentClassLoader)) + val futures = kindFiles map (f => pool submit callable(manager runTest f)) + pool.shutdown() try if (!pool.awaitTermination(4, TimeUnit.HOURS)) - NestUI.warning("Thread pool timeout elapsed before all tests were complete!") + NestUI warning "Thread pool timeout elapsed before all tests were complete!" catch { case t: InterruptedException => - NestUI.warning("Thread pool was interrupted") + NestUI warning "Thread pool was interrupted" t.printStackTrace() } - for ((file, future) <- futures) yield { - val state = if (future.isCancelled) TestState.Timeout else future.get - (file.getAbsolutePath, state) - } + futures map (_.get) } } diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index a32c56e973..25371b7d54 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -12,39 +12,36 @@ import java.io.{File, FilenameFilter, IOException, StringWriter, FileInputStream, FileOutputStream, BufferedReader, FileReader, PrintWriter, FileWriter} import java.net.URI -import scala.tools.nsc.io.{ Path, Directory, File => SFile } +import scala.reflect.io.AbstractFile import scala.collection.mutable trait FileUtil { /** - * Compares two files using a Java implementation of the GNU diff - * available at http://www.bmsi.com/java/#diff. + * Compares two files using difflib to produce a unified diff. * * @param f1 the first file to be compared * @param f2 the second file to be compared - * @return the text difference between the compared files + * @return the unified diff of the compared files or the empty string if they're equal */ def compareFiles(f1: File, f2: File): String = { - val diffWriter = new StringWriter - val args = Array(f1.getAbsolutePath(), f2.getAbsolutePath()) - - DiffPrint.doDiff(args, diffWriter) - val res = diffWriter.toString - if (res startsWith "No") "" else res + compareContents(io.Source.fromFile(f1).getLines.toSeq, io.Source.fromFile(f2).getLines.toSeq, f1.getName, f2.getName) } - def compareContents(lines1: Seq[String], lines2: Seq[String]): String = { - val xs1 = lines1.toArray[AnyRef] - val xs2 = lines2.toArray[AnyRef] - - val diff = new Diff(xs1, xs2) - val change = diff.diff_2(false) - val writer = new StringWriter - val p = new DiffPrint.NormalPrint(xs1, xs2, writer) - - p.print_script(change) - val res = writer.toString - if (res startsWith "No ") "" - else res + + /** + * Compares two lists of lines using difflib to produce a unified diff. + * + * @param origLines the first seq of lines to be compared + * @param newLines the second seq of lines to be compared + * @param origName file name to be used in unified diff for `origLines` + * @param newName file name to be used in unified diff for `newLines` + * @return the unified diff of the `origLines` and `newLines` or the empty string if they're equal + */ + def compareContents(origLines: Seq[String], newLines: Seq[String], origName: String = "a", newName: String = "b"): String = { + import collection.JavaConverters._ + + val diff = difflib.DiffUtils.diff(origLines.asJava, newLines.asJava) + if (diff.getDeltas.isEmpty) "" + else difflib.DiffUtils.generateUnifiedDiff(origName, newName, origLines.asJava, diff, 1).asScala.mkString("\n") } } object FileUtil extends FileUtil { } @@ -65,16 +62,19 @@ trait FileManager extends FileUtil { var LATEST_ACTORS: String protected def relativeToLibrary(what: String): String = { - if (LATEST_LIB endsWith ".jar") { - (SFile(LATEST_LIB).parent / s"scala-$what.jar").toAbsolute.path - } - else { + def jarname = if (what startsWith "scala") s"$what.jar" else s"scala-$what.jar" + if (LATEST_LIB endsWith ".jar") + (SFile(LATEST_LIB).parent / jarname).toAbsolute.path + else (SFile(LATEST_LIB).parent.parent / "classes" / what).toAbsolute.path - } } def latestScaladoc = relativeToLibrary("scaladoc") def latestInteractive = relativeToLibrary("interactive") - def latestPaths = List(LATEST_LIB, LATEST_REFLECT, LATEST_COMP, LATEST_PARTEST, LATEST_ACTORS, latestScaladoc, latestInteractive) + def latestScalapFile = relativeToLibrary("scalap") + def latestPaths = List( + LATEST_LIB, LATEST_REFLECT, LATEST_COMP, LATEST_PARTEST, LATEST_ACTORS, + latestScalapFile, latestScaladoc, latestInteractive + ) def latestFiles = latestPaths map (p => new java.io.File(p)) def latestUrls = latestFiles map (_.toURI.toURL) @@ -131,4 +131,34 @@ trait FileManager extends FileUtil { f.printlnAll(f.lines.toList map replace: _*) } + + /** Massage args to merge plugins and fix paths. + * Plugin path can be relative to test root, or cwd is out. + * While we're at it, mix in the baseline options, too. + * That's how ant passes in the plugins dir. + */ + def updatePluginPath(args: List[String], out: AbstractFile, srcdir: AbstractFile): List[String] = { + val dir = testRootDir + // The given path, or the output dir if ".", or a temp dir if output is virtual (since plugin loading doesn't like virtual) + def pathOrCwd(p: String) = + if (p == ".") { + val plugxml = "scalac-plugin.xml" + val pout = if (out.isVirtual) Directory.makeTemp() else Path(out.path) + val srcpath = Path(srcdir.path) + val pd = (srcpath / plugxml).toFile + if (pd.exists) pd copyTo (pout / plugxml) + pout + } else Path(p) + def absolutize(path: String) = pathOrCwd(path) match { + case x if x.isAbsolute => x.path + case x => (dir / x).toAbsolute.path + } + + val xprefix = "-Xplugin:" + val (xplugs, others) = args partition (_ startsWith xprefix) + val Xplugin = if (xplugs.isEmpty) Nil else List(xprefix + + (xplugs map (_ stripPrefix xprefix) flatMap (_ split pathSeparator) map absolutize mkString pathSeparator) + ) + SCALAC_OPTS.toList ::: others ::: Xplugin + } } diff --git a/src/partest/scala/tools/partest/nest/NestUI.scala b/src/partest/scala/tools/partest/nest/NestUI.scala index df90b22448..2e203bfd91 100644 --- a/src/partest/scala/tools/partest/nest/NestUI.scala +++ b/src/partest/scala/tools/partest/nest/NestUI.scala @@ -3,14 +3,37 @@ * @author Philipp Haller */ -// $Id$ - package scala.tools.partest package nest import java.io.PrintWriter +class Colors(enabled: => Boolean) { + import Console._ + + val bold = colored(BOLD) + val yellow = colored(YELLOW) + val green = colored(GREEN) + val blue = colored(BLUE) + val red = colored(RED) + val red_b = colored(RED_B) + val green_b = colored(GREEN_B) + val cyan = colored(CYAN) + val magenta = colored(MAGENTA) + + private def colored(code: String): String => String = + s => if (enabled) code + s + RESET else s +} + object NestUI { + private val testNum = new java.util.concurrent.atomic.AtomicInteger(1) + // @volatile private var testNumber = 1 + private def testNumber = "%3d" format testNum.getAndIncrement() + def resetTestNumber() = testNum set 1 + + var colorEnabled = sys.props contains "partest.colors" + val color = new Colors(colorEnabled) + import color._ val NONE = 0 val SOME = 1 @@ -22,11 +45,57 @@ object NestUI { private var _warning = "" private var _default = "" + private var dotCount = 0 + private val DotWidth = 72 + + def leftFlush() { + if (dotCount != 0) { + normal("\n") + dotCount = 0 + } + } + + def statusLine(state: TestState) = { + import state._ + val word = bold( + if (isSkipped) yellow("--") + else if (isOk) green("ok") + else red("!!") + ) + word + f" $testNumber%3s - $testIdent%-40s$reasonString" + } + + def reportTest(state: TestState) = { + if (isTerse && state.isOk) { + if (dotCount >= DotWidth) { + outline("\n.") + dotCount = 1 + } + else { + outline(".") + dotCount += 1 + } + } + else echo(statusLine(state)) + } + + def echo(message: String): Unit = synchronized { + leftFlush() + print(message + "\n") + } + def chatty(msg: String) = if (isVerbose) echo(msg) + + def echoSkipped(msg: String) = echo(yellow(msg)) + def echoPassed(msg: String) = echo(bold(green(msg))) + def echoFailed(msg: String) = echo(bold(red(msg))) + def echoMixed(msg: String) = echo(bold(yellow(msg))) + def echoWarning(msg: String) = echo(bold(red(msg))) + def initialize(number: Int) = number match { case MANY => _outline = Console.BOLD + Console.BLACK _success = Console.BOLD + Console.GREEN - _failure = Console.BOLD + Console.RED + _failure = Console.BOLD + Console.RED _warning = Console.BOLD + Console.YELLOW _default = Console.RESET case SOME => @@ -61,10 +130,7 @@ object NestUI { } def usage() { - println("Usage: NestRunner [<options>] [<testfile> ..] [<resfile>]") - println(" <testfile>: list of files ending in '.scala'") - println(" <resfile>: a file not ending in '.scala'") - println(" <options>:") + println("Usage: NestRunner [options] [test test ...]") println println(" Test categories:") println(" --all run all tests") @@ -74,16 +140,12 @@ object NestUI { println(" --jvm run JVM backend tests") println(" --res run resident compiler tests") println(" --scalacheck run ScalaCheck tests") - println(" --script run script runner tests") - println(" --shootout run shootout tests") println(" --instrumented run instrumented tests") println(" --presentation run presentation compiler tests") - println(" --grep <expr> run all tests whose source file contains <expr>") println println(" Other options:") println(" --pack pick compiler/reflect/library in build/pack, and run all tests") - println(" --show-log show log") - println(" --show-diff show diff between log and check file") + println(" --grep <expr> run all tests whose source file contains <expr>") println(" --failed run only those tests that failed during the last run") println(" --update-check instead of failing tests with output change, update checkfile. (Use with care!)") println(" --verbose show progress information") @@ -100,11 +162,28 @@ object NestUI { } var _verbose = false + var _debug = false + var _terse = false + def isVerbose = _verbose + def isDebug = _debug + def isTerse = _terse + + def setVerbose() { + _verbose = true + } + def setDebug() { + _debug = true + } + def setTerse() { + _terse = true + } def verbose(msg: String) { - if (_verbose) { - outline("debug: ") - println(msg) - } + if (isVerbose) + System.err.println(msg) + } + def debug(msg: String) { + if (isDebug) + System.err.println(msg) } } diff --git a/src/partest/scala/tools/partest/nest/PathSettings.scala b/src/partest/scala/tools/partest/nest/PathSettings.scala index 02651c527b..7c005b4f61 100644 --- a/src/partest/scala/tools/partest/nest/PathSettings.scala +++ b/src/partest/scala/tools/partest/nest/PathSettings.scala @@ -71,6 +71,9 @@ object PathSettings { findJar(buildPackLibDir.files ++ srcLibDir.files, "scalacheck") getOrElse { sys.error("No scalacheck jar found in '%s' or '%s'".format(buildPackLibDir, srcLibDir)) } + + lazy val diffUtils: File = + findJar(buildPackLibDir.files, "diffutils") getOrElse sys.error(s"No diffutils.jar found in '$buildPackLibDir'.") } class PathSettings() { diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala index 05cae7b238..9780e82cd9 100644 --- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -51,10 +51,13 @@ class ReflectiveRunner { new ConsoleFileManager // this is a workaround for https://issues.scala-lang.org/browse/SI-5433 - // when that bug is fixed, the addition of PathSettings.srcCodeLib can be removed + // when that bug is fixed, the addition of PathSettings.srcCodeLib can be removed // we hack into the classloader that will become parent classloader for scalac // this way we ensure that reflective macro lookup will pick correct Code.lift - val sepUrls = PathSettings.srcCodeLib.toURI.toURL :: fileManager.latestUrls + // it's also used to inject diffutils into the classpath when running partest from the test/partest script + val srcCodeLibAndDiff = List(PathSettings.srcCodeLib, PathSettings.diffUtils) + val sepUrls = srcCodeLibAndDiff.map(_.toURI.toURL) ::: fileManager.latestUrls + // this seems to be the core classloader that determines which classes can be found when running partest from the test/partest script val sepLoader = new URLClassLoader(sepUrls.toArray, null) if (isPartestDebug) diff --git a/src/partest/scala/tools/partest/nest/Runner.scala b/src/partest/scala/tools/partest/nest/Runner.scala new file mode 100644 index 0000000000..fc56818bfc --- /dev/null +++ b/src/partest/scala/tools/partest/nest/Runner.scala @@ -0,0 +1,628 @@ +/* NEST (New Scala Test) + * Copyright 2007-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala.tools.partest +package nest + +import java.io.{ Console => _, _ } +import java.net.URL +import scala.tools.nsc.Properties.{ jdkHome, javaHome, propOrElse, propOrEmpty } +import scala.util.Properties.{ envOrElse, isWin } +import scala.tools.nsc.{ Settings, CompilerCommand, Global } +import scala.tools.nsc.io.{ AbstractFile, PlainFile, Path, Directory, File => SFile } +import scala.tools.nsc.reporters.ConsoleReporter +import scala.tools.nsc.util.{ ClassPath, FakePos, ScalaClassLoader, stackTraceString } +import ClassPath.{ join, split } +import scala.tools.scalap.scalax.rules.scalasig.ByteCode +import scala.collection.{ mutable, immutable } +import scala.sys.process.Process +import java.util.concurrent.{ Executors, TimeUnit, TimeoutException } +import PartestDefaults.{ javaCmd, javacCmd } +import scala.tools.scalap.Main.decompileScala + +trait PartestRunSettings { + def gitPath: Path + def reportPath: Path + def logPath: Path + + def testPaths: List[Path] + + def gitDiffOptions: List[String] + def extraScalacOptions: List[String] + def extraJavaOptions: List[String] +} + +class TestTranscript { + import NestUI.color._ + private val buf = mutable.ListBuffer[String]() + private def pass(s: String) = bold(green("% ")) + s + private def fail(s: String) = bold(red("% ")) + s + + def add(action: String): this.type = { buf += action ; this } + def append(text: String) { val s = buf.last ; buf.trimEnd(1) ; buf += (s + text) } + + // Colorize prompts according to pass/fail + def fail: List[String] = buf.toList match { + case Nil => Nil + case xs => (xs.init map pass) :+ fail(xs.last) + } +} + +class Runner(val testFile: File, fileManager: FileManager) { + import fileManager._ + + // Override to true to have the outcome of this test displayed + // whether it passes or not; in general only failures are reported, + // except for a . per passing test to show progress. + def isEnumeratedTest = false + + def testRunParams: TestRunParams = ??? + + private var _lastState: TestState = null + private var _transcript = new TestTranscript + + def lastState = if (_lastState == null) TestState.Uninitialized(testFile) else _lastState + def setLastState(s: TestState) = _lastState = s + def transcript: List[String] = _transcript.fail ++ logFile.fileLines + def pushTranscript(msg: String) = _transcript add msg + + val parentFile = testFile.getParentFile + val kind = parentFile.getName + val fileBase = basename(testFile.getName) + val logFile = new File(parentFile, s"$fileBase-$kind.log") + val outFile = logFile changeExtension "obj" + val checkFile = testFile changeExtension "check" + val flagsFile = testFile changeExtension "flags" + val testIdent = testFile.testIdent // e.g. pos/t1234 + + lazy val outDir = { outFile.mkdirs() ; outFile } + + type RanOneTest = (Boolean, LogContext) + + def showCrashInfo(t: Throwable) { + System.err.println("Crashed running test $testIdent: " + t) + if (!isPartestTerse) + System.err.println(stackTraceString(t)) + } + protected def crashHandler: PartialFunction[Throwable, TestState] = { + case t: InterruptedException => + genTimeout() + case t: Throwable => + showCrashInfo(t) + logFile.appendAll(stackTraceString(t)) + genCrash(t) + } + + def genPass() = TestState.Pass(testFile) + def genFail(reason: String) = TestState.Fail(testFile, reason, _transcript.fail) + def genTimeout() = TestState.Fail(testFile, "timed out", _transcript.fail) + def genCrash(caught: Throwable) = TestState.Crash(testFile, caught, _transcript.fail) + + def speclib = PathSettings.srcSpecLib.toString // specialization lib + def codelib = PathSettings.srcCodeLib.toString // reify lib + + // Prepend to a classpath, but without incurring duplicate entries + def prependTo(classpath: String, path: String): String = { + val segments = ClassPath split classpath + + if (segments startsWith path) classpath + else ClassPath.join(path :: segments distinct: _*) + } + + def prependToJavaClasspath(path: String) { + val jcp = sys.props.getOrElse("java.class.path", "") + prependTo(jcp, path) match { + case `jcp` => + case cp => sys.props("java.class.path") = cp + } + } + def prependToClasspaths(s: Settings, path: String) { + prependToJavaClasspath(path) + val scp = s.classpath.value + prependTo(scp, path) match { + case `scp` => + case cp => s.classpath.value = cp + } + } + + private def workerError(msg: String): Unit = System.err.println("Error: " + msg) + + def javac(files: List[File]): TestState = { + // compile using command-line javac compiler + val args = Seq( + javacCmd, + "-d", + outDir.getAbsolutePath, + "-classpath", + join(outDir.toString, CLASSPATH) + ) ++ files.map(_.getAbsolutePath) + + pushTranscript(args mkString " ") + val captured = StreamCapture(runCommand(args, logFile)) + if (captured.result) genPass() else { + logFile appendAll captured.stderr + genFail("java compilation failed") + } + } + + def testPrompt = kind match { + case "res" => "nsc> " + case _ => "% " + } + + def nextTestAction[T](body: => T)(failFn: PartialFunction[T, TestState]): T = { + val result = body + setLastState( if (failFn isDefinedAt result) failFn(result) else genPass() ) + result + } + def nextTestActionExpectTrue[T](reason: String, body: => Boolean): Boolean = { + nextTestAction(body) { case false => genFail(reason) } + } + + private def assembleTestCommand(outDir: File, logFile: File): List[String] = { + // check whether there is a ".javaopts" file + val argsFile = testFile changeExtension "javaopts" + val argString = file2String(argsFile) + if (argString != "") + NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, argString)) + + val testFullPath = testFile.getAbsolutePath + + // Note! As this currently functions, JAVA_OPTS must precede argString + // because when an option is repeated to java only the last one wins. + // That means until now all the .javaopts files were being ignored because + // they all attempt to change options which are also defined in + // partest.java_opts, leading to debug output like: + // + // debug: Found javaopts file 'files/shootout/message.scala-2.javaopts', using options: '-Xss32k' + // debug: java -Xss32k -Xss2m -Xms256M -Xmx1024M -classpath [...] + val extras = if (isPartestDebug) List("-Dpartest.debug=true") else Nil + val propertyOptions = List( + "-Dfile.encoding=UTF-8", + "-Djava.library.path="+logFile.getParentFile.getAbsolutePath, + "-Dpartest.output="+outDir.getAbsolutePath, + "-Dpartest.lib="+LATEST_LIB, + "-Dpartest.reflect="+LATEST_REFLECT, + "-Dpartest.cwd="+outDir.getParent, + "-Dpartest.test-path="+testFullPath, + "-Dpartest.testname="+fileBase, + "-Djavacmd="+javaCmd, + "-Djavaccmd="+javacCmd, + "-Duser.language=en", + "-Duser.country=US" + ) ++ extras + + val classpath = if (extraClasspath != "") join(extraClasspath, CLASSPATH) else CLASSPATH + + javaCmd +: ( + (JAVA_OPTS.split(' ') ++ extraJavaOptions.split(' ') ++ argString.split(' ')).map(_.trim).filter(_ != "").toList ++ Seq( + "-classpath", + join(outDir.toString, classpath) + ) ++ propertyOptions ++ Seq( + "scala.tools.nsc.MainGenericRunner", + "-usejavacp", + "Test", + "jvm" + ) + ) + } + + /** Runs command redirecting standard out and + * error out to output file. + */ + private def runCommand(args: Seq[String], outFile: File): Boolean = { + (Process(args) #> outFile !) == 0 + } + + private def execTest(outDir: File, logFile: File): Boolean = { + val cmd = assembleTestCommand(outDir, logFile) + + pushTranscript(cmd.mkString(" \\\n ") + " > " + logFile.getName) + nextTestActionExpectTrue("non-zero exit code", runCommand(cmd, logFile)) || { + _transcript append logFile.fileContents + false + } + } + + override def toString = s"""Test($testIdent, lastState = $lastState)""" + + def newTestWriters() = { + val swr = new StringWriter + val wr = new PrintWriter(swr, true) + // diff = "" + + ((swr, wr)) + } + + def fail(what: Any) = { + NestUI.verbose("scalac: compilation of "+what+" failed\n") + false + } + + /** Filter the diff for conditional blocks. + * The check file can contain lines of the form: + * `#partest java7` + * where the line contains a conventional flag name. + * In the diff output, these lines have the form: + * `> #partest java7` + * Blocks which don't apply are filtered out, + * and what remains is the desired diff. + * Line edit commands such as `0a1,6` don't count + * as diff, so return a nonempty diff only if + * material diff output was seen. + * Filtering the diff output (instead of every check + * file) means that we only post-process a test that + * might be failing, in the normal case. + */ + def diffilter(d: String) = { + import scala.util.Properties.javaVersion + val prefix = "#partest" + val margin = "> " + val leader = margin + prefix + // use lines in block so labeled? Default to sure, go ahead. + def retainOn(f: String) = f match { + case "java7" => javaVersion startsWith "1.7" + case "java6" => javaVersion startsWith "1.6" + case _ => true + } + if (d contains prefix) { + val sb = new StringBuilder + var retain = true // use the current line + var material = false // saw a line of diff + for (line <- d.lines) + if (line startsWith leader) { + val rest = (line stripPrefix leader).trim + retain = retainOn(rest) + } else if (retain) { + if (line startsWith margin) material = true + sb ++= line + sb ++= EOL + } + if (material) sb.toString else "" + } else d + } + + def currentDiff = ( + if (checkFile.canRead) diffilter(compareFiles(logFile, checkFile)) + else compareContents(augmentString(file2String(logFile)).lines.toList, Nil) + ) + + val gitRunner = List("/usr/local/bin/git", "/usr/bin/git") map (f => new java.io.File(f)) find (_.canRead) + val gitDiffOptions = "--ignore-space-at-eol --no-index " + propOrEmpty("partest.git_diff_options") + // --color=always --word-diff + + def gitDiff(f1: File, f2: File): Option[String] = { + try gitRunner map { git => + val cmd = s"$git diff $gitDiffOptions $f1 $f2" + val diff = Process(cmd).lines_!.drop(4).map(_ + "\n").mkString + + "\n" + diff + } + catch { case t: Exception => None } + } + + /** This does something about absolute paths and file separator + * chars before diffing. + */ + def normalizeLog() { + // squashing // in paths also munges line comments, so save this innovation for another time. + // (There are line comments in the "stub implementations" error output.) + //val slashes = """[/\\]+""".r + //def squashSlashes(s: String) = slashes replaceAllIn (s, "/") + def squashSlashes(s: String) = s replace ('\\', '/') + val base = squashSlashes(parentFile.getAbsolutePath + File.separator) + val quoted = """\Q%s\E""" format base + val baseless = (if (isWin) "(?i)" + quoted else quoted).r + def canonicalize(s: String) = baseless replaceAllIn (squashSlashes(s), "") + logFile mapInPlace canonicalize + } + + def diffIsOk: Boolean = { + val diff = currentDiff + val ok: Boolean = (diff == "") || { + fileManager.updateCheck && { + NestUI.verbose("Updating checkfile " + checkFile) + checkFile writeAll file2String(logFile) + true + } + } + pushTranscript(s"diff $logFile $checkFile") + nextTestAction(ok) { + case false => + // Get a word-highlighted diff from git if we can find it + val bestDiff = if (ok) "" else { + if (checkFile.canRead) + gitDiff(logFile, checkFile) getOrElse { + s"diff $logFile $checkFile\n$diff" + } + else diff + } + _transcript append bestDiff + genFail("output differs") + // TestState.fail("output differs", "output differs", + // genFail("output differs") + // TestState.Fail("output differs", bestDiff) + } + } + + /** 1. Creates log file and output directory. + * 2. Runs script function, providing log file and output directory as arguments. + */ + def runInContext(body: => Boolean): (Boolean, LogContext) = { + val (swr, wr) = newTestWriters() + val succeeded = body + (succeeded, LogContext(logFile, swr, wr)) + } + + /** Grouped files in group order, and lex order within each group. */ + def groupedFiles(dir: File): List[List[File]] = { + val testFiles = dir.listFiles.toList filter (_.isJavaOrScala) + val grouped = testFiles groupBy (_.group) + grouped.keys.toList.sorted map (k => grouped(k) sortBy (_.getName)) + } + + def newCompiler = new DirectCompiler(fileManager) + + def attemptCompile(sources: List[File]): TestState = { + val state = newCompiler.compile(this, flagsForCompilation(sources), sources) + if (!state.isOk) + _transcript append ("\n" + file2String(logFile)) + + state + } + + // snort or scarf all the contributing flags files + def flagsForCompilation(sources: List[File]): List[String] = { + def argsplitter(s: String) = words(s) filter (_.nonEmpty) + val perTest = argsplitter(flagsFile.fileContents) + val perGroup = if (testFile.isDirectory) { + sources flatMap { f => SFile(Path(f) changeExtension "flags").safeSlurp map argsplitter getOrElse Nil } + } else Nil + perTest ++ perGroup + } + + abstract class CompileRound { + def fs: List[File] + def result: TestState + def description: String + + def fsString = fs map (_.toString stripPrefix parentFile.toString + "/") mkString " " + def isOk = result.isOk + def mkScalacString(): String = { + val flags = file2String(flagsFile) match { + case "" => "" + case s => " " + s + } + s"""scalac $fsString""" + } + override def toString = description + ( if (result.isOk) "" else "\n" + result.status ) + } + case class OnlyJava(fs: List[File]) extends CompileRound { + def description = s"""javac $fsString""" + lazy val result = { pushTranscript(description) ; javac(fs) } + } + case class OnlyScala(fs: List[File]) extends CompileRound { + def description = mkScalacString() + lazy val result = { pushTranscript(description) ; attemptCompile(fs) } + } + case class ScalaAndJava(fs: List[File]) extends CompileRound { + def description = mkScalacString() + lazy val result = { pushTranscript(description) ; attemptCompile(fs) } + } + + def compilationRounds(file: File): List[CompileRound] = { + val grouped = if (file.isDirectory) groupedFiles(file) else List(List(file)) + + (grouped map mixedCompileGroup).flatten + } + def mixedCompileGroup(allFiles: List[File]): List[CompileRound] = { + val (scalaFiles, javaFiles) = allFiles partition (_.isScala) + val isMixed = javaFiles.nonEmpty && scalaFiles.nonEmpty + val round1 = if (scalaFiles.isEmpty) None else Some(ScalaAndJava(allFiles)) + val round2 = if (javaFiles.isEmpty) None else Some(OnlyJava(javaFiles)) + val round3 = if (!isMixed) None else Some(OnlyScala(scalaFiles)) + + List(round1, round2, round3).flatten + } + + def runNegTest() = runInContext { + val rounds = compilationRounds(testFile) + + if (rounds forall (x => nextTestActionExpectTrue("compilation failed", x.isOk))) + nextTestActionExpectTrue("expected compilation failure", false) + else { + normalizeLog // put errors in a normal form + diffIsOk + } + } + + def runTestCommon(andAlso: => Boolean): (Boolean, LogContext) = runInContext { + compilationRounds(testFile).forall(x => nextTestActionExpectTrue("compilation failed", x.isOk)) && andAlso + } + + // Apache Ant 1.6 or newer + def ant(args: Seq[String], output: File): Boolean = { + val antDir = Directory(envOrElse("ANT_HOME", "/opt/ant/")) + val antLibDir = Directory(antDir / "lib") + val antLauncherPath = SFile(antLibDir / "ant-launcher.jar").path + val antOptions = + if (NestUI._verbose) List("-verbose", "-noinput") + else List("-noinput") + val cmd = javaCmd +: ( + JAVA_OPTS.split(' ').map(_.trim).filter(_ != "") ++ Seq( + "-classpath", + antLauncherPath, + "org.apache.tools.ant.launch.Launcher" + ) ++ antOptions ++ args + ) + + runCommand(cmd, output) + } + + def runAntTest(): (Boolean, LogContext) = { + val (swr, wr) = newTestWriters() + + val succeeded = try { + val binary = "-Dbinary="+( + if (fileManager.LATEST_LIB endsWith "build/quick/classes/library") "quick" + else if (fileManager.LATEST_LIB endsWith "build/pack/lib/scala-library.jar") "pack" + else if (fileManager.LATEST_LIB endsWith "dists/latest/lib/scala-library.jar/") "latest" + else "installed" + ) + val args = Array(binary, "-logfile", logFile.getPath, "-file", testFile.getPath) + NestUI.verbose("ant "+args.mkString(" ")) + + pushTranscript(s"ant ${args.mkString(" ")}") + nextTestActionExpectTrue("ant failed", ant(args, logFile)) && diffIsOk + } + catch { // *catch-all* + case e: Exception => + NestUI.warning("caught "+e) + false + } + + (succeeded, LogContext(logFile, swr, wr)) + } + + def extraClasspath = kind match { + case "specialized" => PathSettings.srcSpecLib.toString + case _ => "" + } + def extraJavaOptions = kind match { + case "instrumented" => "-javaagent:"+PathSettings.instrumentationAgentLib + case _ => "" + } + + def runScalacheckTest() = runTestCommon { + NestUI.verbose("compilation of "+testFile+" succeeded\n") + + val outURL = outDir.getAbsoluteFile.toURI.toURL + val logWriter = new PrintStream(new FileOutputStream(logFile), true) + + Output.withRedirected(logWriter) { + // this classloader is test specific: its parent contains library classes and others + ScalaClassLoader.fromURLs(List(outURL), testRunParams.scalaCheckParentClassLoader).run("Test", Nil) + } + + NestUI.verbose(file2String(logFile)) + // obviously this must be improved upon + val lines = SFile(logFile).lines map (_.trim) filterNot (_ == "") toBuffer; + lines.forall(x => !x.startsWith("!")) || { + NestUI.normal("ScalaCheck test failed. Output:\n") + lines foreach (x => NestUI.normal(x + "\n")) + false + } + } + + def runResidentTest() = { + // simulate resident compiler loop + val prompt = "\nnsc> " + val (swr, wr) = newTestWriters() + + NestUI.verbose(this+" running test "+fileBase) + val dir = parentFile + val resFile = new File(dir, fileBase + ".res") + + // run compiler in resident mode + // $SCALAC -d "$os_dstbase".obj -Xresident -sourcepath . "$@" + val sourcedir = logFile.getParentFile.getAbsoluteFile + val sourcepath = sourcedir.getAbsolutePath+File.separator + NestUI.verbose("sourcepath: "+sourcepath) + + val argList = List( + "-d", outDir.getAbsoluteFile.getPath, + "-Xresident", + "-sourcepath", sourcepath) + + // configure input/output files + val logOut = new FileOutputStream(logFile) + val logWriter = new PrintStream(logOut, true) + val resReader = new BufferedReader(new FileReader(resFile)) + val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut), true) + + // create compiler + val settings = new Settings(workerError) + settings.sourcepath.value = sourcepath + settings.classpath.value = fileManager.CLASSPATH + val reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter) + val command = new CompilerCommand(argList, settings) + object compiler extends Global(command.settings, reporter) + + def resCompile(line: String): Boolean = { + // NestUI.verbose("compiling "+line) + val cmdArgs = (line split ' ').toList map (fs => new File(dir, fs).getAbsolutePath) + // NestUI.verbose("cmdArgs: "+cmdArgs) + val sett = new Settings(workerError) + sett.sourcepath.value = sourcepath + val command = new CompilerCommand(cmdArgs, sett) + // "scalac " + command.files.mkString(" ") + pushTranscript("scalac " + command.files.mkString(" ")) + nextTestActionExpectTrue( + "compilation failed", + command.ok && { + (new compiler.Run) compile command.files + !reporter.hasErrors + } + ) + } + def loop(): Boolean = { + logWriter.print(prompt) + resReader.readLine() match { + case null | "" => logWriter.close() ; true + case line => resCompile(line) && loop() + } + } + // res/t687.res depends on ignoring its compilation failure + // and just looking at the diff, so I made them all do that + // because this is long enough. + if (!Output.withRedirected(logWriter)(try loop() finally resReader.close())) + setLastState(genPass()) + + normalizeLog // put errors in a normal form + (diffIsOk, LogContext(logFile, swr, wr)) + } + + def run(): TestState = { + if (kind == "neg" || (kind endsWith "-neg")) runNegTest() + else kind match { + case "pos" => runTestCommon(true) + case "ant" => runAntTest() + case "scalacheck" => runScalacheckTest() + case "res" => runResidentTest() + case "scalap" => runScalapTest() + case "script" => runScriptTest() + case _ => runTestCommon(execTest(outDir, logFile) && diffIsOk) + } + + lastState + } + + def runScalapTest() = runTestCommon { + val isPackageObject = testFile.getName startsWith "package" + val className = testFile.getName.stripSuffix(".scala").capitalize + (if (!isPackageObject) "" else ".package") + val loader = ScalaClassLoader.fromURLs(List(outDir.toURI.toURL), this.getClass.getClassLoader) + val byteCode = ByteCode forClass (loader loadClass className) + val result = decompileScala(byteCode.bytes, isPackageObject) + + logFile writeAll result + diffIsOk + } + def runScriptTest() = { + import scala.sys.process._ + val (swr, wr) = newTestWriters() + + val args = file2String(testFile changeExtension "args") + val cmdFile = if (isWin) testFile changeExtension "bat" else testFile + val succeeded = (((cmdFile + " " + args) #> logFile !) == 0) && diffIsOk + + (succeeded, LogContext(logFile, swr, wr)) + } + + def cleanup() { + if (lastState.isOk) + logFile.delete() + if (!isPartestDebug) + Directory(outDir).deleteRecursively() + } +} diff --git a/src/partest/scala/tools/partest/nest/RunnerManager.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala index 8f28277a6c..1c689714c7 100644 --- a/src/partest/scala/tools/partest/nest/RunnerManager.scala +++ b/src/partest/scala/tools/partest/nest/RunnerManager.scala @@ -8,8 +8,6 @@ package nest import java.io._ import java.net.URL -import java.util.{ Timer, TimerTask } - import scala.tools.nsc.Properties.{ jdkHome, javaHome, propOrElse } import scala.util.Properties.{ envOrElse, isWin } import scala.tools.nsc.{ Settings, CompilerCommand, Global } @@ -22,6 +20,7 @@ import scala.collection.{ mutable, immutable } import scala.sys.process._ import java.util.concurrent.{ Executors, TimeUnit, TimeoutException } import PartestDefaults.{ javaCmd, javacCmd } +import scala.tools.scalap.Main.decompileScala class LogContext(val file: File, val writers: Option[(StringWriter, PrintWriter)]) @@ -75,689 +74,22 @@ object Output { class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunParams) { import fileManager._ - - val compileMgr = new CompileManager(fileManager) fileManager.CLASSPATH += File.pathSeparator + PathSettings.scalaCheck - - private def compareFiles(f1: File, f2: File): String = - try fileManager.compareFiles(f1, f2) - catch { case t: Exception => t.toString } - - /** This does something about absolute paths and file separator - * chars before diffing. - */ - private def replaceSlashes(dir: File, s: String): String = { - val base = (dir.getAbsolutePath + File.separator).replace('\\', '/') - var regex = """\Q%s\E""" format base - if (isWin) regex = "(?i)" + regex - s.replace('\\', '/').replaceAll(regex, "") - } - - private def workerError(msg: String): Unit = System.err.println("Error: " + msg) - - private def printInfoStart(file: File, printer: PrintWriter) { - NestUI.outline("testing: ", printer) - val filesdir = file.getAbsoluteFile.getParentFile.getParentFile - val testdir = filesdir.getParentFile - val totalWidth = 56 - val name = { - // 1. try with [...]/files/run/test.scala - val name = file.getAbsolutePath drop testdir.getAbsolutePath.length - if (name.length <= totalWidth) name - // 2. try with [...]/run/test.scala - else file.getAbsolutePath drop filesdir.getAbsolutePath.length - } - NestUI.normal("[...]%s%s".format(name, " " * (totalWidth - name.length)), printer) - } - - private def printInfoEnd(success: Boolean, printer: PrintWriter) { - NestUI.normal("[", printer) - if (success) NestUI.success(" OK ", printer) - else NestUI.failure("FAILED", printer) - NestUI.normal("]\n", printer) - } - - private def printInfoTimeout(printer: PrintWriter) { - NestUI.normal("[", printer) - NestUI.failure("TIMOUT", printer) - NestUI.normal("]\n", printer) - } - - private def javac(outDir: File, files: List[File], output: File): CompilationOutcome = { - // compile using command-line javac compiler - val args = Seq( - javacCmd, - "-d", - outDir.getAbsolutePath, - "-classpath", - join(outDir.toString, CLASSPATH) - ) ++ files.map("" + _) - - try if (runCommand(args, output)) CompileSuccess else CompileFailed - catch exHandler(output, "javac command failed:\n" + args.map(" " + _ + "\n").mkString + "\n", CompilerCrashed) - } - - /** Runs command redirecting standard out and error out to output file. - * Overloaded to accept a sequence of arguments. - */ - private def runCommand(args: Seq[String], outFile: File): Boolean = { - NestUI.verbose("running command:\n"+args.map(" " + _ + "\n").mkString) - runCommandImpl(Process(args), outFile) - } - - /** Runs command redirecting standard out and error out to output file. - * Overloaded to accept a single string = concatenated command + arguments. - */ - private def runCommand(command: String, outFile: File): Boolean = { - NestUI.verbose("running command:"+command) - runCommandImpl(Process(command), outFile) - } - - private def runCommandImpl(process: => ProcessBuilder, outFile: File): Boolean = { - val exitCode = (process #> outFile !) - // normalize line endings - // System.getProperty("line.separator") should be "\n" here - // so reading a file and writing it back should convert all CRLFs to LFs - SFile(outFile).printlnAll(SFile(outFile).lines.toList: _*) - exitCode == 0 - } - - @inline private def isJava(f: File) = SFile(f) hasExtension "java" - @inline private def isScala(f: File) = SFile(f) hasExtension "scala" - @inline private def isJavaOrScala(f: File) = isJava(f) || isScala(f) - - private def outputLogFile(logFile: File) { - val lines = SFile(logFile).lines - if (lines.nonEmpty) { - NestUI.normal("Log file '" + logFile + "': \n") - lines foreach (x => NestUI.normal(x + "\n")) - } - } - private def logStackTrace(logFile: File, t: Throwable, msg: String): Boolean = { - SFile(logFile).writeAll(msg, stackTraceString(t)) - outputLogFile(logFile) // if running the test threw an exception, output log file - false - } - - private def exHandler[T](logFile: File, msg: String, value: T): PartialFunction[Throwable, T] = { - case e: Exception => logStackTrace(logFile, e, msg) ; value - } - - class Runner(testFile: File) { - var testDiff: String = "" - var passed: Option[Boolean] = None - - val fileBase = basename(testFile.getName) - val logFile = fileManager.getLogFile(testFile, kind) - val parent = testFile.getParentFile - val outDir = new File(parent, "%s-%s.obj".format(fileBase, kind)) - def toDelete = if (isPartestDebug) Nil else List( - if (passed exists (x => x)) Some(logFile) else None, - if (outDir.isDirectory) Some(outDir) else None - ).flatten - - private def createOutputDir(): File = { - outDir.mkdirs() - outDir - } - - private def execTest(outDir: File, logFile: File, classpathPrefix: String = "", javaOpts: String = ""): Boolean = { - // check whether there is a ".javaopts" file - val argsFile = new File(logFile.getParentFile, fileBase + ".javaopts") - val argString = file2String(argsFile) - if (argString != "") - NestUI.verbose("Found javaopts file '%s', using options: '%s'".format(argsFile, argString)) - - val testFullPath = { - val d = new File(logFile.getParentFile, fileBase) - if (d.isDirectory) d.getAbsolutePath - else { - val f = new File(logFile.getParentFile, fileBase + ".scala") - if (f.isFile) f.getAbsolutePath - else "" - } - } - - // Note! As this currently functions, JAVA_OPTS must precede argString - // because when an option is repeated to java only the last one wins. - // That means until now all the .javaopts files were being ignored because - // they all attempt to change options which are also defined in - // partest.java_opts, leading to debug output like: - // - // debug: Found javaopts file 'files/shootout/message.scala-2.javaopts', using options: '-Xss32k' - // debug: java -Xss32k -Xss2m -Xms256M -Xmx1024M -classpath [...] - val extras = if (isPartestDebug) List("-Dpartest.debug=true") else Nil - val propertyOptions = List( - "-Dfile.encoding=UTF-8", - "-Djava.library.path="+logFile.getParentFile.getAbsolutePath, - "-Dpartest.output="+outDir.getAbsolutePath, - "-Dpartest.lib="+LATEST_LIB, - "-Dpartest.reflect="+LATEST_REFLECT, - "-Dpartest.comp="+LATEST_COMP, - "-Dpartest.cwd="+outDir.getParent, - "-Dpartest.test-path="+testFullPath, - "-Dpartest.testname="+fileBase, - "-Djavacmd="+javaCmd, - "-Djavaccmd="+javacCmd, - "-Duser.language=en", - "-Duser.country=US" - ) ++ extras - - val classpath = if (classpathPrefix != "") join(classpathPrefix, CLASSPATH) else CLASSPATH - val cmd = javaCmd +: ( - (JAVA_OPTS.split(' ') ++ javaOpts.split(' ') ++ argString.split(' ')).map(_.trim).filter(_ != "") ++ Seq( - "-classpath", - join(outDir.toString, classpath) - ) ++ propertyOptions ++ Seq( - "scala.tools.nsc.MainGenericRunner", - "-usejavacp", - "Test", - "jvm" - ) - ) - - runCommand(cmd, logFile) - } - - private def getCheckFilePath(dir: File, suffix: String) = { - def chkFile(s: String) = (Directory(dir) / "%s%s.check".format(fileBase, s)).toFile - - if (chkFile("").isFile || suffix == "") chkFile("") - else chkFile("-" + suffix) - } - - private def compareOutput(dir: File, logFile: File): String = { - val checkFile = getCheckFilePath(dir, kind) - val diff = - if (checkFile.canRead) compareFiles(logFile, checkFile.jfile) - else file2String(logFile) - - // if check file exists, compare with log file - if (diff != "" && fileManager.updateCheck) { - NestUI.verbose("Updating checkfile " + checkFile.jfile) - val toWrite = if (checkFile.exists) checkFile else getCheckFilePath(dir, "") - toWrite writeAll file2String(logFile) - "" - } - else diff - } - - def newTestWriters() = { - val swr = new StringWriter - val wr = new PrintWriter(swr, true) - - ((swr, wr)) - } - - def diffCheck(testFile: File, diff: String) = { - testDiff = diff - testDiff == "" - } - - /** 1. Creates log file and output directory. - * 2. Runs script function, providing log file and output directory as arguments. - */ - def runInContext(file: File, script: (File, File) => Boolean): (Boolean, LogContext) = { - val (swr, wr) = newTestWriters() - printInfoStart(file, wr) - - NestUI.verbose(this+" running test "+fileBase) - val outDir = createOutputDir() - NestUI.verbose("output directory: "+outDir) - - // run test-specific code - val succeeded = try { - if (isPartestDebug) { - val (result, millis) = timed(script(logFile, outDir)) - fileManager.recordTestTiming(file.getPath, millis) - result - } - else script(logFile, outDir) - } - catch exHandler(logFile, "", false) - - (succeeded, LogContext(logFile, swr, wr)) - } - - def groupedFiles(dir: File): List[List[File]] = { - val testFiles = dir.listFiles.toList filter isJavaOrScala - - def isInGroup(f: File, num: Int) = SFile(f).stripExtension endsWith ("_" + num) - val groups = (0 to 9).toList map (num => (testFiles filter (f => isInGroup(f, num))).sorted) - val noGroupSuffix = (testFiles filterNot (groups.flatten contains)).sorted - - noGroupSuffix :: groups filterNot (_.isEmpty) - } - - def compileFilesIn(dir: File, logFile: File, outDir: File): CompilationOutcome = { - def compileGroup(g: List[File]): CompilationOutcome = { - val (scalaFiles, javaFiles) = g partition isScala - val allFiles = javaFiles ++ scalaFiles - - /* The test can contain both java and scala files, each of which should be compiled with the corresponding - * compiler. Since the source files can reference each other both ways (java referencing scala classes and - * vice versa, the partest compilation routine attempts to reach a "bytecode fixpoint" between the two - * compilers -- that's when bytecode generated by each compiler implements the signatures expected by the other. - * - * In theory this property can't be guaranteed, as neither compiler can know what signatures the other - * compiler expects and how to implement them. (see SI-1240 for the full story) - * - * In practice, this happens in 3 steps: - * STEP1: Feed all the files to scalac if there are also non-Scala sources. - * It will parse java files and obtain their expected signatures and generate bytecode for scala files - * STEP2: Feed the java files to javac if there are any. - * It will generate the bytecode for the java files and link to the scalac-generated bytecode for scala - * STEP3: (Re-)compile the scala sources so they link to the correct - * java signatures, in case the signatures deduced by scalac from the source files were wrong. Since the - * bytecode for java is already in place, we only feed the scala files to scalac so it will take the - * java signatures from the existing javac-generated bytecode. - * Note that no artifacts are deleted before this step. - */ - List(1, 2, 3).foldLeft(CompileSuccess: CompilationOutcome) { - case (CompileSuccess, 1) if scalaFiles.nonEmpty && javaFiles.nonEmpty => - compileMgr.attemptCompile(Some(outDir), allFiles, kind, logFile) - case (CompileSuccess, 2) if javaFiles.nonEmpty => - javac(outDir, javaFiles, logFile) - case (CompileSuccess, 3) if scalaFiles.nonEmpty => - // TODO: Do we actually need this? SI-1240 is known to require this, but we don't know if other tests - // require it: https://groups.google.com/forum/?fromgroups#!topic/scala-internals/rFDKAcOKciU - compileMgr.attemptCompile(Some(outDir), scalaFiles, kind, logFile) - - case (outcome, _) => outcome - } - } - groupedFiles(dir).foldLeft(CompileSuccess: CompilationOutcome) { - case (CompileSuccess, files) => compileGroup(files) - case (outcome, _) => outcome - } - } - - def runTestCommon(file: File, expectFailure: Boolean)( - onSuccess: (File, File) => Boolean, - onFail: (File, File) => Unit = (_, _) => ()): (Boolean, LogContext) = - { - runInContext(file, (logFile: File, outDir: File) => { - val outcome = ( - if (file.isDirectory) compileFilesIn(file, logFile, outDir) - else compileMgr.attemptCompile(None, List(file), kind, logFile) - ) - val result = ( - if (expectFailure) outcome.isNegative - else outcome.isPositive - ) - - if (result) onSuccess(logFile, outDir) - else { onFail(logFile, outDir) ; false } - }) - } - - def runJvmTest(file: File): (Boolean, LogContext) = - runTestCommon(file, expectFailure = false)((logFile, outDir) => { - val dir = file.getParentFile - - // adding codelib.jar to the classpath - // codelib provides the possibility to override standard reify - // this shields the massive amount of reification tests from changes in the API - execTest(outDir, logFile, PathSettings.srcCodeLib.toString) && { - // cannot replace paths here since this also inverts slashes - // which affects a bunch of tests - //fileManager.mapFile(logFile, replaceSlashes(dir, _)) - diffCheck(file, compareOutput(dir, logFile)) - } - }) - - // Apache Ant 1.6 or newer - def ant(args: Seq[String], output: File): Boolean = { - val antDir = Directory(envOrElse("ANT_HOME", "/opt/ant/")) - val antLibDir = Directory(antDir / "lib") - val antLauncherPath = SFile(antLibDir / "ant-launcher.jar").path - val antOptions = - if (NestUI._verbose) List("-verbose", "-noinput") - else List("-noinput") - val cmd = javaCmd +: ( - JAVA_OPTS.split(' ').map(_.trim).filter(_ != "") ++ Seq( - "-classpath", - antLauncherPath, - "org.apache.tools.ant.launch.Launcher" - ) ++ antOptions ++ args - ) - - try runCommand(cmd, output) - catch exHandler(output, "ant command '" + cmd + "' failed:\n", false) - } - - def runAntTest(file: File): (Boolean, LogContext) = { - val (swr, wr) = newTestWriters() - printInfoStart(file, wr) - - NestUI.verbose(this+" running test "+fileBase) - - val succeeded = try { - val binary = "-Dbinary="+( - if (fileManager.LATEST_LIB endsWith "build/quick/classes/library") "quick" - else if (fileManager.LATEST_LIB endsWith "build/pack/lib/scala-library.jar") "pack" - else if (fileManager.LATEST_LIB endsWith "dists/latest/lib/scala-library.jar/") "latest" - else "installed" - ) - val args = Array(binary, "-logfile", logFile.path, "-file", file.path) - NestUI.verbose("ant "+args.mkString(" ")) - ant(args, logFile) && diffCheck(file, compareOutput(file.getParentFile, logFile)) - } - catch { // *catch-all* - case e: Exception => - NestUI.verbose("caught "+e) - false - } - - (succeeded, LogContext(logFile, swr, wr)) - } - - def runSpecializedTest(file: File): (Boolean, LogContext) = - runTestCommon(file, expectFailure = false)((logFile, outDir) => { - val dir = file.getParentFile - - // adding the instrumented library to the classpath - ( execTest(outDir, logFile, PathSettings.srcSpecLib.toString) && - diffCheck(file, compareOutput(dir, logFile)) - ) - }) - - def runInstrumentedTest(file: File): (Boolean, LogContext) = - runTestCommon(file, expectFailure = false)((logFile, outDir) => { - val dir = file.getParentFile - - // adding the javagent option with path to instrumentation agent - execTest(outDir, logFile, javaOpts = "-javaagent:"+PathSettings.instrumentationAgentLib) && - diffCheck(file, compareOutput(dir, logFile)) - }) - - def processSingleFile(file: File): (Boolean, LogContext) = kind match { - case "scalacheck" => - val succFn: (File, File) => Boolean = { (logFile, outDir) => - NestUI.verbose("compilation of "+file+" succeeded\n") - - val outURL = outDir.getAbsoluteFile.toURI.toURL - val logWriter = new PrintStream(new FileOutputStream(logFile), true) - - Output.withRedirected(logWriter) { - // this classloader is test specific: its parent contains library classes and others - ScalaClassLoader.fromURLs(List(outURL), params.scalaCheckParentClassLoader).run("Test", Nil) - } - - NestUI.verbose(file2String(logFile)) - // obviously this must be improved upon - val lines = SFile(logFile).lines map (_.trim) filterNot (_ == "") toBuffer; - lines.forall(x => !x.startsWith("!")) || { - NestUI.normal("ScalaCheck test failed. Output:\n") - lines foreach (x => NestUI.normal(x + "\n")) - false - } - } - runTestCommon(file, expectFailure = false)( - succFn, - (logFile, outDir) => outputLogFile(logFile) - ) - - case "pos" => - runTestCommon(file, expectFailure = false)( - (logFile, outDir) => true, - (_, _) => () - ) - - case "neg" => - runTestCommon(file, expectFailure = true)((logFile, outDir) => { - // compare log file to check file - val dir = file.getParentFile - - // diff is contents of logFile - fileManager.mapFile(logFile, replaceSlashes(dir, _)) - diffCheck(file, compareOutput(dir, logFile)) - }) - - case "run" | "jvm" => - runJvmTest(file) - - case "specialized" => - runSpecializedTest(file) - - case "instrumented" => - runInstrumentedTest(file) - - case "presentation" => - runJvmTest(file) // for the moment, it's exactly the same as for a run test - - case "ant" => - runAntTest(file) - - case "res" => { - // simulate resident compiler loop - val prompt = "\nnsc> " - - val (swr, wr) = newTestWriters() - printInfoStart(file, wr) - - NestUI.verbose(this+" running test "+fileBase) - val dir = file.getParentFile - val outDir = createOutputDir() - val resFile = new File(dir, fileBase + ".res") - NestUI.verbose("outDir: "+outDir) - NestUI.verbose("logFile: "+logFile) - //NestUI.verbose("logFileErr: "+logFileErr) - NestUI.verbose("resFile: "+resFile) - - // run compiler in resident mode - // $SCALAC -d "$os_dstbase".obj -Xresident -sourcepath . "$@" - val sourcedir = logFile.getParentFile.getAbsoluteFile - val sourcepath = sourcedir.getAbsolutePath+File.separator - NestUI.verbose("sourcepath: "+sourcepath) - - val argList = List( - "-d", outDir.getAbsoluteFile.getPath, - "-Xresident", - "-sourcepath", sourcepath) - - // configure input/output files - val logOut = new FileOutputStream(logFile) - val logWriter = new PrintStream(logOut, true) - val resReader = new BufferedReader(new FileReader(resFile)) - val logConsoleWriter = new PrintWriter(new OutputStreamWriter(logOut), true) - - // create compiler - val settings = new Settings(workerError) - settings.sourcepath.value = sourcepath - settings.classpath.value = fileManager.CLASSPATH - val reporter = new ConsoleReporter(settings, scala.Console.in, logConsoleWriter) - val command = new CompilerCommand(argList, settings) - object compiler extends Global(command.settings, reporter) - - val resCompile = (line: String) => { - NestUI.verbose("compiling "+line) - val cmdArgs = (line split ' ').toList map (fs => new File(dir, fs).getAbsolutePath) - NestUI.verbose("cmdArgs: "+cmdArgs) - val sett = new Settings(workerError) - sett.sourcepath.value = sourcepath - val command = new CompilerCommand(cmdArgs, sett) - command.ok && { - (new compiler.Run) compile command.files - !reporter.hasErrors - } - } - - def loop(action: String => Boolean): Boolean = { - logWriter.print(prompt) - resReader.readLine() match { - case null | "" => logWriter.flush() ; true - case line => action(line) && loop(action) - } - } - - Output.withRedirected(logWriter) { - try loop(resCompile) - finally resReader.close() - } - fileManager.mapFile(logFile, replaceSlashes(dir, _)) - - (diffCheck(file, compareOutput(dir, logFile)), LogContext(logFile, swr, wr)) - } - - case "shootout" => - val (swr, wr) = newTestWriters() - printInfoStart(file, wr) - - NestUI.verbose(this+" running test "+fileBase) - val outDir = createOutputDir() - - // 2. define file {outDir}/test.scala that contains code to compile/run - val testFile = new File(outDir, "test.scala") - NestUI.verbose("outDir: "+outDir) - NestUI.verbose("logFile: "+logFile) - NestUI.verbose("testFile: "+testFile) - - // 3. cat {test}.scala.runner {test}.scala > testFile - val runnerFile = new File(parent, fileBase+".scala.runner") - val bodyFile = new File(parent, fileBase+".scala") - SFile(testFile).writeAll( - file2String(runnerFile), - file2String(bodyFile) - ) - - // 4. compile testFile - val ok = compileMgr.attemptCompile(None, List(testFile), kind, logFile) eq CompileSuccess - NestUI.verbose("compilation of " + testFile + (if (ok) "succeeded" else "failed")) - val result = ok && { - execTest(outDir, logFile) && { - NestUI.verbose(this+" finished running "+fileBase) - diffCheck(file, compareOutput(parent, logFile)) - } - } - - (result, LogContext(logFile, swr, wr)) - - case "scalap" => - runInContext(file, (logFile: File, outDir: File) => { - val sourceDir = Directory(if (file.isFile) file.getParent else file) - val sources = sourceDir.files filter (_ hasExtension "scala") map (_.jfile) toList - val results = sourceDir.files filter (_.name == "result.test") map (_.jfile) toList - - if (sources.length != 1 || results.length != 1) { - NestUI.warning("Misconfigured scalap test directory: " + sourceDir + " \n") - false - } - else { - val resFile = results.head - // 2. Compile source file - - if (!compileMgr.attemptCompile(Some(outDir), sources, kind, logFile).isPositive) { - NestUI.normal("compilerMgr failed to compile %s to %s".format(sources mkString ", ", outDir)) - false - } - else { - // 3. Decompile file and compare results - val isPackageObject = sourceDir.name startsWith "package" - val className = sourceDir.name.capitalize + (if (!isPackageObject) "" else ".package") - val url = outDir.toURI.toURL - val loader = ScalaClassLoader.fromURLs(List(url), this.getClass.getClassLoader) - val clazz = loader.loadClass(className) - - val byteCode = ByteCode.forClass(clazz) - val result = scala.tools.scalap.Main.decompileScala(byteCode.bytes, isPackageObject) - - SFile(logFile) writeAll result - diffCheck(file, compareFiles(logFile, resFile)) - } - } - }) - - case "script" => - val (swr, wr) = newTestWriters() - printInfoStart(file, wr) - - NestUI.verbose(this+" running test "+fileBase) - - // check whether there is an args file - val argsFile = new File(file.getParentFile, fileBase+".args") - NestUI.verbose("argsFile: "+argsFile) - val argString = file2String(argsFile) - val succeeded = try { - val cmdString = - if (isWin) { - val batchFile = new File(file.getParentFile, fileBase+".bat") - NestUI.verbose("batchFile: "+batchFile) - batchFile.getAbsolutePath - } - else file.getAbsolutePath - - val ok = runCommand(cmdString+argString, logFile) - ( ok && diffCheck(file, compareOutput(file.getParentFile, logFile)) ) - } - catch { case e: Exception => NestUI.verbose("caught "+e) ; false } - - (succeeded, LogContext(logFile, swr, wr)) - } - - private def crashContext(t: Throwable): LogContext = { - try { - logStackTrace(logFile, t, "Possible compiler crash during test of: " + testFile + "\n") - LogContext(logFile) - } - catch { case t: Throwable => LogContext(null) } - } - - def run(): (Boolean, LogContext) = { - val result = try processSingleFile(testFile) catch { case t: Throwable => (false, crashContext(t)) } - passed = Some(result._1) - result - } - - def reportResult(writers: Option[(StringWriter, PrintWriter)]) { - writers foreach { case (swr, wr) => - if (passed.isEmpty) printInfoTimeout(wr) - else printInfoEnd(passed.get, wr) - wr.flush() - swr.flush() - NestUI.normal(swr.toString) - - if (passed exists (x => !x)) { - if (fileManager.showDiff || isPartestDebug) - NestUI.normal(testDiff) - if (fileManager.showLog) - showLog(logFile) - } - } - toDelete foreach (_.deleteRecursively()) - } - } + fileManager.CLASSPATH += File.pathSeparator + PathSettings.diffUtils // needed to put diffutils on test/partest's classpath def runTest(testFile: File): TestState = { - val runner = new Runner(testFile) + val runner = new Runner(testFile, fileManager) { + override def testRunParams = params + } // when option "--failed" is provided execute test only if log // is present (which means it failed before) if (fileManager.failed && !runner.logFile.canRead) - return TestState.Ok - - val (success, ctx) = runner.run() - val state = if (success) TestState.Ok else TestState.Fail - - runner.reportResult(ctx.writers) - state - } - - private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = - fs flatMap (s => Option(AbstractFile getFile (pre + s))) toSet - - private def copyTestFiles(testDir: File, destDir: File) { - val invalidExts = List("changes", "svn", "obj") - testDir.listFiles.toList filter ( - f => (isJavaOrScala(f) && f.isFile) || - (f.isDirectory && !(invalidExts.contains(SFile(f).extension)))) foreach - { f => fileManager.copyFile(f, destDir) } - } - - private def showLog(logFile: File) { - file2String(logFile) match { - case "" if logFile.canRead => () - case "" => NestUI.failure("Couldn't open log file: " + logFile + "\n") - case s => NestUI.normal(s) + runner.genPass() + else { + val (state, elapsed) = timed(runner.run()) + NestUI.reportTest(state) + runner.cleanup() + state } } } diff --git a/src/partest/scala/tools/partest/nest/SBTRunner.scala b/src/partest/scala/tools/partest/nest/SBTRunner.scala index 20f9c701d5..1cf3aa858f 100644 --- a/src/partest/scala/tools/partest/nest/SBTRunner.scala +++ b/src/partest/scala/tools/partest/nest/SBTRunner.scala @@ -1,3 +1,6 @@ +/* NEST (New Scala Test) + * Copyright 2007-2013 LAMP/EPFL + */ package scala.tools.partest package nest @@ -21,7 +24,7 @@ object SBTRunner extends DirectRunner { val testRootDir: Directory = Directory(testRootPath) } - def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String):java.util.Map[String, TestState] = { + def reflectiveRunTestsForFiles(kindFiles: Array[File], kind: String): java.util.List[TestState] = { def failedOnlyIfRequired(files:List[File]):List[File]={ if (fileManager.failed) files filter (x => fileManager.logFileExists(x, kind)) else files } @@ -33,7 +36,7 @@ object SBTRunner extends DirectRunner { scalacOptions: Seq[String] = Seq(), justFailedTests: Boolean = false) - def mainReflect(args: Array[String]): java.util.Map[String, String] = { + def mainReflect(args: Array[String]): java.util.List[TestState] = { setProp("partest.debug", "true") val Argument = new scala.util.matching.Regex("-(.*)") @@ -46,7 +49,7 @@ object SBTRunner extends DirectRunner { case x => sys.error("Unknown command line options: " + x) } val config = parseArgs(args, CommandLineOptions()) - fileManager.SCALAC_OPTS ++= config.scalacOptions + fileManager.SCALAC_OPTS = config.scalacOptions fileManager.CLASSPATH = config.classpath getOrElse sys.error("No classpath set") def findClasspath(jar: String, name: String): Option[String] = { @@ -67,22 +70,14 @@ object SBTRunner extends DirectRunner { // TODO - Make this a flag? //fileManager.updateCheck = true // Now run and report... - val runs = config.tests.filterNot(_._2.isEmpty) - (for { - (testType, files) <- runs - (path, result) <- reflectiveRunTestsForFiles(files,testType).asScala - } yield (path, fixResult(result))).seq.asJava - } - def fixResult(result: TestState): String = result match { - case TestState.Ok => "OK" - case TestState.Fail => "FAIL" - case TestState.Timeout => "TIMEOUT" + val runs = config.tests.filterNot(_._2.isEmpty) + val result = runs.toList flatMap { case (kind, files) => reflectiveRunTestsForFiles(files, kind).asScala } + + result.asJava } + def main(args: Array[String]): Unit = { - val failures = ( - for ((path, result) <- mainReflect(args).asScala ; if result != TestState.Ok) yield - path + ( if (result == TestState.Fail) " [FAILED]" else " [TIMEOUT]" ) - ) + val failures = mainReflect(args).asScala collect { case s if !s.isOk => s.longStatus } // Re-list all failures so we can go figure out what went wrong. failures foreach System.err.println if(!failures.isEmpty) sys.exit(1) diff --git a/src/partest/scala/tools/partest/nest/StreamCapture.scala b/src/partest/scala/tools/partest/nest/StreamCapture.scala new file mode 100644 index 0000000000..dc155b1787 --- /dev/null +++ b/src/partest/scala/tools/partest/nest/StreamCapture.scala @@ -0,0 +1,53 @@ +/* NEST (New Scala Test) + * Copyright 2007-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala.tools.partest +package nest + +import java.io.{ Console => _, _ } + +object StreamCapture { + case class Captured[T](stdout: String, stderr: String, result: T) { + override def toString = s""" + |result: $result + |[stdout] + |$stdout + |[stderr] + |$stderr""".stripMargin.trim + } + + private def mkStream = { + val swr = new StringWriter + val wr = new PrintWriter(swr, true) + val ostream = new PrintStream(new OutputStream { def write(b: Int): Unit = wr write b }, true) // autoFlush = true + + (ostream, () => { ostream.close() ; swr.toString }) + } + + def savingSystem[T](body: => T): T = { + val savedOut = System.out + val savedErr = System.err + try body + finally { + System setErr savedErr + System setOut savedOut + } + } + + def apply[T](body: => T): Captured[T] = { + val (outstream, stdoutFn) = mkStream + val (errstream, stderrFn) = mkStream + + val result = savingSystem { + System setOut outstream + System setErr errstream + Console.withOut(outstream) { + Console.withErr(errstream) { + body + } + } + } + Captured(stdoutFn(), stderrFn(), result) + } +} diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala deleted file mode 100644 index 880c6e431b..0000000000 --- a/src/partest/scala/tools/partest/nest/TestFile.scala +++ /dev/null @@ -1,80 +0,0 @@ -/* NEST (New Scala Test) - * Copyright 2007-2013 LAMP/EPFL - * @author Philipp Haller - */ - -// $Id$ - -package scala.tools.partest -package nest - -import java.io.{ File => JFile } -import scala.tools.nsc.Settings -import scala.tools.nsc.util.ClassPath -import scala.tools.nsc.io._ -import scala.util.Properties.{ propIsSet, propOrElse, setProp } - -trait TestFileCommon { - def file: JFile - def kind: String - - val dir = file.toAbsolute.parent - val fileBase = file.stripExtension - val flags = dir / (fileBase + ".flags") ifFile (f => f.slurp().trim) - - lazy val objectDir = dir / (fileBase + "-" + kind + ".obj") createDirectory true - def setOutDirTo = objectDir -} - -abstract class TestFile(val kind: String) extends TestFileCommon { - def file: JFile - def fileManager: FileManager - - def defineSettings(settings: Settings, setOutDir: Boolean) = { - settings.classpath append dir.path - if (setOutDir) - settings.outputDirs setSingleOutput setOutDirTo.path - - // adding codelib.jar to the classpath - // codelib provides the possibility to override standard reify - // this shields the massive amount of reification tests from changes in the API - settings.classpath prepend PathSettings.srcCodeLib.toString - if (propIsSet("java.class.path")) setProp("java.class.path", PathSettings.srcCodeLib.toString + ";" + propOrElse("java.class.path", "")) - - // have to catch bad flags somewhere - (flags forall (f => settings.processArgumentString(f)._1)) && { - settings.classpath append fileManager.CLASSPATH - true - } - } - - override def toString(): String = "%s %s".format(kind, file) -} - -case class PosTestFile(file: JFile, fileManager: FileManager) extends TestFile("pos") -case class NegTestFile(file: JFile, fileManager: FileManager) extends TestFile("neg") -case class RunTestFile(file: JFile, fileManager: FileManager) extends TestFile("run") -case class ScalaCheckTestFile(file: JFile, fileManager: FileManager) extends TestFile("scalacheck") -case class JvmTestFile(file: JFile, fileManager: FileManager) extends TestFile("jvm") -case class ShootoutTestFile(file: JFile, fileManager: FileManager) extends TestFile("shootout") { - override def setOutDirTo = file.parent -} -case class ScalapTestFile(file: JFile, fileManager: FileManager) extends TestFile("scalap") { - override def setOutDirTo = file.parent -} -case class SpecializedTestFile(file: JFile, fileManager: FileManager) extends TestFile("specialized") { - override def defineSettings(settings: Settings, setOutDir: Boolean): Boolean = { - super.defineSettings(settings, setOutDir) && { - // add the instrumented library version to classpath - settings.classpath prepend PathSettings.srcSpecLib.toString - // @partest maintainer: if we use a custom Scala build (specified via --classpath) - // then the classes provided by it will come earlier than instrumented.jar in the resulting classpath - // this entire classpath business needs a thorough solution - if (propIsSet("java.class.path")) setProp("java.class.path", PathSettings.srcSpecLib.toString + ";" + propOrElse("java.class.path", "")) - true - } - } -} -case class PresentationTestFile(file: JFile, fileManager: FileManager) extends TestFile("presentation") -case class AntTestFile(file: JFile, fileManager: FileManager) extends TestFile("ant") -case class InstrumentedTestFile(file: JFile, fileManager: FileManager) extends TestFile("instrumented") diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index 2b2ce2e435..9e21b0f6ba 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -4,31 +4,102 @@ package scala.tools -import java.io.{ FileNotFoundException, File => JFile } -import nsc.io.{ Path, Directory, File => SFile } -import scala.tools.util.PathResolver -import nsc.Properties.{ propOrElse, propOrNone, propOrEmpty } import scala.sys.process.javaVmArguments import java.util.concurrent.Callable +import scala.tools.partest.nest.NestUI +import scala.tools.nsc.util.{ ScalaClassLoader, Exceptional } -package partest { - class TestState { } - object TestState { - val Ok = new TestState - val Fail = new TestState - val Timeout = new TestState +package object partest { + type File = java.io.File + type SFile = scala.reflect.io.File + type Directory = scala.reflect.io.Directory + type Path = scala.reflect.io.Path + type PathResolver = scala.tools.util.PathResolver + type ClassPath[T] = scala.tools.nsc.util.ClassPath[T] + type StringWriter = java.io.StringWriter + + val SFile = scala.reflect.io.File + val Directory = scala.reflect.io.Directory + val Path = scala.reflect.io.Path + val PathResolver = scala.tools.util.PathResolver + val ClassPath = scala.tools.nsc.util.ClassPath + + val space = "\u0020" + val EOL = scala.compat.Platform.EOL + def onull(s: String) = if (s == null) "" else s + def oempty(xs: String*) = xs filterNot (x => x == null || x == "") + def ojoin(xs: String*): String = oempty(xs: _*) mkString space + def nljoin(xs: String*): String = oempty(xs: _*) mkString EOL + + def setUncaughtHandler() = { + Thread.setDefaultUncaughtExceptionHandler( + new Thread.UncaughtExceptionHandler { + def uncaughtException(thread: Thread, t: Throwable) { + val t1 = Exceptional unwrap t + System.err.println(s"Uncaught exception on thread $thread: $t1") + t1.printStackTrace() + } + } + ) } -} -package object partest { - import nest.NestUI + /** Sources have a numerical group, specified by name_7 and so on. */ + private val GroupPattern = """.*_(\d+)""".r + + implicit class FileOps(val f: File) { + private def sf = SFile(f) + + def testIdent = { + f.toString split """[/\\]+""" takeRight 2 mkString "/" // e.g. pos/t1234 + } + + def mapInPlace(mapFn: String => String): Unit = + writeAll(fileLines.map(x => mapFn(x) + "\n"): _*) + + def appendAll(strings: String*): Unit = sf.appendAll(strings: _*) + def writeAll(strings: String*): Unit = sf.writeAll(strings: _*) + def absolutePathSegments: List[String] = f.getAbsolutePath split """[/\\]+""" toList + + def isJava = f.isFile && (sf hasExtension "java") + def isScala = f.isFile && (sf hasExtension "scala") + def isJavaOrScala = isJava || isScala + + def extension = sf.extension + def hasExtension(ext: String) = sf hasExtension ext + def changeExtension(ext: String): File = (sf changeExtension ext).jfile + + /** The group number for this source file, or -1 for no group. */ + def group: Int = + sf.stripExtension match { + case GroupPattern(g) if g.toInt >= 0 => g.toInt + case _ => -1 + } + + def fileContents: String = try sf.slurp() catch { case _: java.io.FileNotFoundException => "" } + def fileLines: List[String] = augmentString(fileContents).lines.toList + } + + implicit class PathOps(p: Path) extends FileOps(p.jfile) { } + + implicit class Copier(val f: SFile) extends AnyVal { + def copyTo(dest: Path): Unit = dest.toFile writeAll f.slurp(scala.io.Codec.UTF8) + } - implicit private[partest] def temporaryPath2File(x: Path): JFile = x.jfile - implicit private[partest] def temporaryFile2Path(x: JFile): Path = Path(x) + implicit def temporaryPath2File(x: Path): File = x.jfile + implicit def stringPathToJavaFile(path: String): File = new File(path) implicit lazy val postfixOps = scala.language.postfixOps implicit lazy val implicitConversions = scala.language.implicitConversions + def fileSeparator = java.io.File.separator + def pathSeparator = java.io.File.pathSeparator + + def pathToTestIdent(path: Path) = path.jfile.testIdent + + def canonicalizeSlashes(line: String) = line.replaceAll("""[/\\]+""", "/") + + def words(s: String): List[String] = (s.trim split "\\s+").toList + def timed[T](body: => T): (T, Long) = { val t1 = System.currentTimeMillis val result = body @@ -39,15 +110,40 @@ package object partest { def callable[T](body: => T): Callable[T] = new Callable[T] { override def call() = body } - def file2String(f: JFile) = - try SFile(f).slurp(scala.io.Codec.UTF8) - catch { case _: FileNotFoundException => "" } + def file2String(f: File): String = f.fileContents def basename(name: String): String = Path(name).stripExtension - def resultsToStatistics(results: Iterable[(_, TestState)]): (Int, Int) = { - val (files, failures) = results map (_._2 == TestState.Ok) partition (_ == true) - (files.size, failures.size) + /** In order to allow for spaces in flags/options, this + * parses .flags, .javaopts, javacopts etc files as follows: + * If it is exactly one line, it is split (naively) on spaces. + * If it contains more than one line, each line is its own + * token, spaces and all. + */ + def readOptionsFile(file: File): List[String] = { + file.fileLines match { + case x :: Nil => words(x) + case xs => xs map (_.trim) + } + } + + def findProgram(name: String): Option[File] = { + val pathDirs = sys.env("PATH") match { + case null => List("/usr/local/bin", "/usr/bin", "/bin") + case path => path split "[:;]" filterNot (_ == "") toList + } + pathDirs.iterator map (d => new File(d, name)) find (_.canExecute) + } + + def now = (new java.util.Date).toString + def elapsedString(millis: Long): String = { + val elapsedSecs = millis/1000 + val elapsedMins = elapsedSecs/60 + val elapsedHrs = elapsedMins/60 + val dispMins = elapsedMins - elapsedHrs * 60 + val dispSecs = elapsedSecs - elapsedMins * 60 + + "%02d:%02d:%02d".format(elapsedHrs, dispMins, dispSecs) } def vmArgString = javaVmArguments.mkString( @@ -62,13 +158,10 @@ package object partest { } def showAllJVMInfo() { - NestUI.verbose(vmArgString) - NestUI.verbose(allPropertiesString) + vlog(vmArgString) + vlog(allPropertiesString) } - def isPartestDebug: Boolean = - propOrEmpty("partest.debug") == "true" - import scala.language.experimental.macros /** @@ -117,4 +210,10 @@ package object partest { a.tree))))), a.tree)) } + + def isPartestTerse = NestUI.isTerse + def isPartestDebug = NestUI.isDebug + def isPartestVerbose = NestUI.isVerbose + + def vlog(msg: => String) = if (isPartestVerbose) System.err.println(msg) } diff --git a/src/reflect/scala/reflect/api/Annotations.scala b/src/reflect/scala/reflect/api/Annotations.scala index 09eaf7afb4..f87e10c792 100644 --- a/src/reflect/scala/reflect/api/Annotations.scala +++ b/src/reflect/scala/reflect/api/Annotations.scala @@ -45,12 +45,6 @@ trait Annotations { self: Universe => */ type Annotation >: Null <: AnyRef with AnnotationApi - /** A tag that preserves the identity of the `Annotation` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AnnotationTag: ClassTag[Annotation] - /** The constructor/extractor for `Annotation` instances. * @group Extractors */ @@ -90,11 +84,6 @@ trait Annotations { self: Universe => */ type JavaArgument >: Null <: AnyRef - /** A tag that preserves the identity of the `JavaArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val JavaArgumentTag: ClassTag[JavaArgument] /** A literal argument to a Java annotation as `"Use X instead"` in `@Deprecated("Use X instead")` * @template @@ -102,12 +91,6 @@ trait Annotations { self: Universe => */ type LiteralArgument >: Null <: AnyRef with JavaArgument with LiteralArgumentApi - /** A tag that preserves the identity of the `LiteralArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val LiteralArgumentTag: ClassTag[LiteralArgument] - /** The constructor/extractor for `LiteralArgument` instances. * @group Extractors */ @@ -137,12 +120,6 @@ trait Annotations { self: Universe => */ type ArrayArgument >: Null <: AnyRef with JavaArgument with ArrayArgumentApi - /** A tag that preserves the identity of the `ArrayArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ArrayArgumentTag: ClassTag[ArrayArgument] - /** The constructor/extractor for `ArrayArgument` instances. * @group Extractors */ @@ -172,12 +149,6 @@ trait Annotations { self: Universe => */ type NestedArgument >: Null <: AnyRef with JavaArgument with NestedArgumentApi - /** A tag that preserves the identity of the `NestedArgument` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NestedArgumentTag: ClassTag[NestedArgument] - /** The constructor/extractor for `NestedArgument` instances. * @group Extractors */ @@ -200,4 +171,4 @@ trait Annotations { self: Universe => /** The underlying nested annotation. */ def annotation: Annotation } -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/api/Constants.scala b/src/reflect/scala/reflect/api/Constants.scala index f3d75c3c00..0b7dd5582a 100644 --- a/src/reflect/scala/reflect/api/Constants.scala +++ b/src/reflect/scala/reflect/api/Constants.scala @@ -183,12 +183,6 @@ trait Constants { */ type Constant >: Null <: AnyRef with ConstantApi - /** A tag that preserves the identity of the `Constant` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ConstantTag: ClassTag[Constant] - /** The constructor/extractor for `Constant` instances. * @group Extractors */ diff --git a/src/reflect/scala/reflect/api/FlagSets.scala b/src/reflect/scala/reflect/api/FlagSets.scala index 4357aec9c9..712236cce1 100644 --- a/src/reflect/scala/reflect/api/FlagSets.scala +++ b/src/reflect/scala/reflect/api/FlagSets.scala @@ -61,12 +61,6 @@ trait FlagSets { self: Universe => */ type FlagSet - /** A tag that preserves the identity of the `FlagSet` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FlagSetTag: ClassTag[FlagSet] - /** The API of `FlagSet` instances. * The main source of information about flag sets is the [[scala.reflect.api.FlagSets]] page. * @group Flags diff --git a/src/reflect/scala/reflect/api/ImplicitTags.scala b/src/reflect/scala/reflect/api/ImplicitTags.scala index 3f377d6cff..fdc1d9017b 100644 --- a/src/reflect/scala/reflect/api/ImplicitTags.scala +++ b/src/reflect/scala/reflect/api/ImplicitTags.scala @@ -1,108 +1,116 @@ package scala.reflect package api +/** Tags which preserve the identity of abstract types in the face of erasure. + * Can be used for pattern matching, instance tests, serialization and the like. + * @group Tags + */ trait ImplicitTags { - self: Types => + self: Universe => - /** A tag that preserves the identity of the `Type` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeTagg: ClassTag[Type] - - /** A tag that preserves the identity of the `SingletonType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SingletonTypeTag: ClassTag[SingletonType] - - /** A tag that preserves the identity of the `ThisType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ThisTypeTag: ClassTag[ThisType] - - /** A tag that preserves the identity of the `SingleType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SingleTypeTag: ClassTag[SingleType] - - /** A tag that preserves the identity of the `SuperType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SuperTypeTag: ClassTag[SuperType] - - /** A tag that preserves the identity of the `ConstantType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ConstantTypeTag: ClassTag[ConstantType] - - /** A tag that preserves the identity of the `TypeRef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeRefTag: ClassTag[TypeRef] - - /** A tag that preserves the identity of the `CompoundType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val CompoundTypeTag: ClassTag[CompoundType] - - /** A tag that preserves the identity of the `RefinedType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val RefinedTypeTag: ClassTag[RefinedType] - - /** A tag that preserves the identity of the `ClassInfoType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ + // Tags for Types. + implicit val AnnotatedTypeTag: ClassTag[AnnotatedType] + implicit val BoundedWildcardTypeTag: ClassTag[BoundedWildcardType] implicit val ClassInfoTypeTag: ClassTag[ClassInfoType] - - /** A tag that preserves the identity of the `MethodType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ + implicit val CompoundTypeTag: ClassTag[CompoundType] + implicit val ConstantTypeTag: ClassTag[ConstantType] + implicit val ExistentialTypeTag: ClassTag[ExistentialType] implicit val MethodTypeTag: ClassTag[MethodType] - - /** A tag that preserves the identity of the `NullaryMethodType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ implicit val NullaryMethodTypeTag: ClassTag[NullaryMethodType] - - /** A tag that preserves the identity of the `PolyType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ implicit val PolyTypeTag: ClassTag[PolyType] - - /** A tag that preserves the identity of the `ExistentialType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ExistentialTypeTag: ClassTag[ExistentialType] - - /** A tag that preserves the identity of the `AnnotatedType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AnnotatedTypeTag: ClassTag[AnnotatedType] - - /** A tag that preserves the identity of the `TypeBounds` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ + implicit val RefinedTypeTag: ClassTag[RefinedType] + implicit val SingleTypeTag: ClassTag[SingleType] + implicit val SingletonTypeTag: ClassTag[SingletonType] + implicit val SuperTypeTag: ClassTag[SuperType] + implicit val ThisTypeTag: ClassTag[ThisType] implicit val TypeBoundsTag: ClassTag[TypeBounds] + implicit val TypeRefTag: ClassTag[TypeRef] + implicit val TypeTagg: ClassTag[Type] - /** A tag that preserves the identity of the `BoundedWildcardType` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val BoundedWildcardTypeTag: ClassTag[BoundedWildcardType] + // Tags for Names. + implicit val NameTag: ClassTag[Name] + implicit val TermNameTag: ClassTag[TermName] + implicit val TypeNameTag: ClassTag[TypeName] + + // Tags for Scopes. + implicit val ScopeTag: ClassTag[Scope] + implicit val MemberScopeTag: ClassTag[MemberScope] + + // Tags for Annotations. + implicit val AnnotationTag: ClassTag[Annotation] + implicit val JavaArgumentTag: ClassTag[JavaArgument] + implicit val LiteralArgumentTag: ClassTag[LiteralArgument] + implicit val ArrayArgumentTag: ClassTag[ArrayArgument] + implicit val NestedArgumentTag: ClassTag[NestedArgument] + + // Tags for Symbols. + implicit val TermSymbolTag: ClassTag[TermSymbol] + implicit val MethodSymbolTag: ClassTag[MethodSymbol] + implicit val SymbolTag: ClassTag[Symbol] + implicit val TypeSymbolTag: ClassTag[TypeSymbol] + implicit val ModuleSymbolTag: ClassTag[ModuleSymbol] + implicit val ClassSymbolTag: ClassTag[ClassSymbol] + implicit val FreeTermSymbolTag: ClassTag[FreeTermSymbol] + implicit val FreeTypeSymbolTag: ClassTag[FreeTypeSymbol] + + // Tags for misc Tree relatives. + implicit val PositionTag: ClassTag[Position] + implicit val ConstantTag: ClassTag[Constant] + implicit val FlagSetTag: ClassTag[FlagSet] + implicit val ModifiersTag: ClassTag[Modifiers] + + // Tags for Trees. WTF. + implicit val AlternativeTag: ClassTag[Alternative] + implicit val AnnotatedTag: ClassTag[Annotated] + implicit val AppliedTypeTreeTag: ClassTag[AppliedTypeTree] + implicit val ApplyTag: ClassTag[Apply] + implicit val AssignOrNamedArgTag: ClassTag[AssignOrNamedArg] + implicit val AssignTag: ClassTag[Assign] + implicit val BindTag: ClassTag[Bind] + implicit val BlockTag: ClassTag[Block] + implicit val CaseDefTag: ClassTag[CaseDef] + implicit val ClassDefTag: ClassTag[ClassDef] + implicit val CompoundTypeTreeTag: ClassTag[CompoundTypeTree] + implicit val DefDefTag: ClassTag[DefDef] + implicit val DefTreeTag: ClassTag[DefTree] + implicit val ExistentialTypeTreeTag: ClassTag[ExistentialTypeTree] + implicit val FunctionTag: ClassTag[Function] + implicit val GenericApplyTag: ClassTag[GenericApply] + implicit val IdentTag: ClassTag[Ident] + implicit val IfTag: ClassTag[If] + implicit val ImplDefTag: ClassTag[ImplDef] + implicit val ImportSelectorTag: ClassTag[ImportSelector] + implicit val ImportTag: ClassTag[Import] + implicit val LabelDefTag: ClassTag[LabelDef] + implicit val LiteralTag: ClassTag[Literal] + implicit val MatchTag: ClassTag[Match] + implicit val MemberDefTag: ClassTag[MemberDef] + implicit val ModuleDefTag: ClassTag[ModuleDef] + implicit val NameTreeTag: ClassTag[NameTree] + implicit val NewTag: ClassTag[New] + implicit val PackageDefTag: ClassTag[PackageDef] + implicit val RefTreeTag: ClassTag[RefTree] + implicit val ReferenceToBoxedTag: ClassTag[ReferenceToBoxed] + implicit val ReturnTag: ClassTag[Return] + implicit val SelectFromTypeTreeTag: ClassTag[SelectFromTypeTree] + implicit val SelectTag: ClassTag[Select] + implicit val SingletonTypeTreeTag: ClassTag[SingletonTypeTree] + implicit val StarTag: ClassTag[Star] + implicit val SuperTag: ClassTag[Super] + implicit val SymTreeTag: ClassTag[SymTree] + implicit val TemplateTag: ClassTag[Template] + implicit val TermTreeTag: ClassTag[TermTree] + implicit val ThisTag: ClassTag[This] + implicit val ThrowTag: ClassTag[Throw] + implicit val TreeTag: ClassTag[Tree] + implicit val TryTag: ClassTag[Try] + implicit val TypTreeTag: ClassTag[TypTree] + implicit val TypeApplyTag: ClassTag[TypeApply] + implicit val TypeBoundsTreeTag: ClassTag[TypeBoundsTree] + implicit val TypeDefTag: ClassTag[TypeDef] + implicit val TypeTreeTag: ClassTag[TypeTree] + implicit val TypedTag: ClassTag[Typed] + implicit val UnApplyTag: ClassTag[UnApply] + implicit val ValDefTag: ClassTag[ValDef] + implicit val ValOrDefDefTag: ClassTag[ValOrDefDef] } diff --git a/src/reflect/scala/reflect/api/Names.scala b/src/reflect/scala/reflect/api/Names.scala index 6290b88d33..e7840a13fb 100644 --- a/src/reflect/scala/reflect/api/Names.scala +++ b/src/reflect/scala/reflect/api/Names.scala @@ -43,34 +43,16 @@ trait Names { */ type Name >: Null <: NameApi - /** A tag that preserves the identity of the `Name` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NameTag: ClassTag[Name] - /** The abstract type of names representing terms. * @group Names */ type TypeName >: Null <: Name - /** A tag that preserves the identity of the `TypeName` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeNameTag: ClassTag[TypeName] - /** The abstract type of names representing types. * @group Names */ type TermName >: Null <: Name - /** A tag that preserves the identity of the `TermName` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TermNameTag: ClassTag[TermName] - /** The API of Name instances. * @group API */ diff --git a/src/reflect/scala/reflect/api/Positions.scala b/src/reflect/scala/reflect/api/Positions.scala index 87f00fdb88..6edf8e13e4 100644 --- a/src/reflect/scala/reflect/api/Positions.scala +++ b/src/reflect/scala/reflect/api/Positions.scala @@ -19,13 +19,6 @@ trait Positions { * @group Positions */ type Position >: Null <: scala.reflect.api.Position { type Pos = Position } - - /** A tag that preserves the identity of the `Position` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val PositionTag: ClassTag[Position] - /** A special "missing" position. * @group Positions */ diff --git a/src/reflect/scala/reflect/api/Printers.scala b/src/reflect/scala/reflect/api/Printers.scala index d9e05e77c1..162fe1296b 100644 --- a/src/reflect/scala/reflect/api/Printers.scala +++ b/src/reflect/scala/reflect/api/Printers.scala @@ -163,6 +163,8 @@ trait Printers { self: Universe => import scala.language.implicitConversions implicit def booleanToBooleanFlag(value: Boolean): BooleanFlag = BooleanFlag(Some(value)) implicit def optionToBooleanFlag(value: Option[Boolean]): BooleanFlag = BooleanFlag(value) + import scala.reflect.internal.settings.MutableSettings + implicit def settingToBooleanFlag(setting: MutableSettings#BooleanSetting): BooleanFlag = BooleanFlag(Some(setting.value)) } /** @group Printers */ diff --git a/src/reflect/scala/reflect/api/Scopes.scala b/src/reflect/scala/reflect/api/Scopes.scala index 7f9799393c..4bab6b6a04 100644 --- a/src/reflect/scala/reflect/api/Scopes.scala +++ b/src/reflect/scala/reflect/api/Scopes.scala @@ -33,12 +33,6 @@ trait Scopes { self: Universe => */ trait ScopeApi extends Iterable[Symbol] - /** A tag that preserves the identity of the `Scope` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ScopeTag: ClassTag[Scope] - /** Create a new scope with the given initial elements. * @group Scopes */ @@ -61,10 +55,4 @@ trait Scopes { self: Universe => */ def sorted: List[Symbol] } - - /** A tag that preserves the identity of the `MemberScope` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MemberScopeTag: ClassTag[MemberScope] -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index dbad3dd478..7225919de5 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -61,12 +61,6 @@ trait Symbols { self: Universe => */ type Symbol >: Null <: SymbolApi - /** A tag that preserves the identity of the `Symbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SymbolTag: ClassTag[Symbol] - /** The type of type symbols representing type, class, and trait declarations, * as well as type parameters. * @group Symbols @@ -74,12 +68,6 @@ trait Symbols { self: Universe => */ type TypeSymbol >: Null <: Symbol with TypeSymbolApi - /** A tag that preserves the identity of the `TypeSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeSymbolTag: ClassTag[TypeSymbol] - /** The type of term symbols representing val, var, def, and object declarations as * well as packages and value parameters. * @group Symbols @@ -87,72 +75,36 @@ trait Symbols { self: Universe => */ type TermSymbol >: Null <: Symbol with TermSymbolApi - /** A tag that preserves the identity of the `TermSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TermSymbolTag: ClassTag[TermSymbol] - /** The type of method symbols representing def declarations. * @group Symbols * @template */ type MethodSymbol >: Null <: TermSymbol with MethodSymbolApi - /** A tag that preserves the identity of the `MethodSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MethodSymbolTag: ClassTag[MethodSymbol] - /** The type of module symbols representing object declarations. * @group Symbols * @template */ type ModuleSymbol >: Null <: TermSymbol with ModuleSymbolApi - /** A tag that preserves the identity of the `ModuleSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ModuleSymbolTag: ClassTag[ModuleSymbol] - /** The type of class symbols representing class and trait definitions. * @group Symbols * @template */ type ClassSymbol >: Null <: TypeSymbol with ClassSymbolApi - /** A tag that preserves the identity of the `ClassSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ClassSymbolTag: ClassTag[ClassSymbol] - /** The type of free terms introduced by reification. * @group Symbols * @template */ type FreeTermSymbol >: Null <: TermSymbol with FreeTermSymbolApi - /** A tag that preserves the identity of the `FreeTermSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FreeTermSymbolTag: ClassTag[FreeTermSymbol] - /** The type of free types introduced by reification. * @group Symbols * @template */ type FreeTypeSymbol >: Null <: TypeSymbol with FreeTypeSymbolApi - /** A tag that preserves the identity of the `FreeTypeSymbol` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FreeTypeSymbolTag: ClassTag[FreeTypeSymbol] - /** A special "missing" symbol. Commonly used in the API to denote a default or empty value. * @group Symbols * @template diff --git a/src/reflect/scala/reflect/api/Trees.scala b/src/reflect/scala/reflect/api/Trees.scala index 18985fe83d..99b5ef87f8 100644 --- a/src/reflect/scala/reflect/api/Trees.scala +++ b/src/reflect/scala/reflect/api/Trees.scala @@ -60,12 +60,6 @@ trait Trees { self: Universe => */ type Tree >: Null <: TreeApi - /** A tag that preserves the identity of the `Tree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TreeTag: ClassTag[Tree] - /** The API that all trees support. * The main source of information about trees is the [[scala.reflect.api.Trees]] page. * @group API @@ -230,12 +224,6 @@ trait Trees { self: Universe => */ type TermTree >: Null <: AnyRef with Tree with TermTreeApi - /** A tag that preserves the identity of the `TermTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TermTreeTag: ClassTag[TermTree] - /** The API that all term trees support * @group API */ @@ -249,12 +237,6 @@ trait Trees { self: Universe => */ type TypTree >: Null <: AnyRef with Tree with TypTreeApi - /** A tag that preserves the identity of the `TypTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypTreeTag: ClassTag[TypTree] - /** The API that all typ trees support * @group API */ @@ -267,12 +249,6 @@ trait Trees { self: Universe => */ type SymTree >: Null <: AnyRef with Tree with SymTreeApi - /** A tag that preserves the identity of the `SymTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SymTreeTag: ClassTag[SymTree] - /** The API that all sym trees support * @group API */ @@ -287,12 +263,6 @@ trait Trees { self: Universe => */ type NameTree >: Null <: AnyRef with Tree with NameTreeApi - /** A tag that preserves the identity of the `NameTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NameTreeTag: ClassTag[NameTree] - /** The API that all name trees support * @group API */ @@ -311,12 +281,6 @@ trait Trees { self: Universe => */ type RefTree >: Null <: SymTree with NameTree with RefTreeApi - /** A tag that preserves the identity of the `RefTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val RefTreeTag: ClassTag[RefTree] - /** The API that all ref trees support * @group API */ @@ -337,12 +301,6 @@ trait Trees { self: Universe => */ type DefTree >: Null <: SymTree with NameTree with DefTreeApi - /** A tag that preserves the identity of the `DefTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val DefTreeTag: ClassTag[DefTree] - /** The API that all def trees support * @group API */ @@ -358,12 +316,6 @@ trait Trees { self: Universe => */ type MemberDef >: Null <: DefTree with MemberDefApi - /** A tag that preserves the identity of the `MemberDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MemberDefTag: ClassTag[MemberDef] - /** The API that all member defs support * @group API */ @@ -378,12 +330,6 @@ trait Trees { self: Universe => */ type PackageDef >: Null <: MemberDef with PackageDefApi - /** A tag that preserves the identity of the `PackageDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val PackageDefTag: ClassTag[PackageDef] - /** The constructor/extractor for `PackageDef` instances. * @group Extractors */ @@ -417,12 +363,6 @@ trait Trees { self: Universe => */ type ImplDef >: Null <: MemberDef with ImplDefApi - /** A tag that preserves the identity of the `ImplDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ImplDefTag: ClassTag[ImplDef] - /** The API that all impl defs support * @group API */ @@ -437,12 +377,6 @@ trait Trees { self: Universe => */ type ClassDef >: Null <: ImplDef with ClassDefApi - /** A tag that preserves the identity of the `ClassDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ClassDefTag: ClassTag[ClassDef] - /** The constructor/extractor for `ClassDef` instances. * @group Extractors */ @@ -488,12 +422,6 @@ trait Trees { self: Universe => */ type ModuleDef >: Null <: ImplDef with ModuleDefApi - /** A tag that preserves the identity of the `ModuleDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ModuleDefTag: ClassTag[ModuleDef] - /** The constructor/extractor for `ModuleDef` instances. * @group Extractors */ @@ -534,12 +462,6 @@ trait Trees { self: Universe => */ type ValOrDefDef >: Null <: MemberDef with ValOrDefDefApi - /** A tag that preserves the identity of the `ValOrDefDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ValOrDefDefTag: ClassTag[ValOrDefDef] - /** The API that all val defs and def defs support * @group API */ @@ -571,12 +493,6 @@ trait Trees { self: Universe => */ type ValDef >: Null <: ValOrDefDef with ValDefApi - /** A tag that preserves the identity of the `ValDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ValDefTag: ClassTag[ValDef] - /** The constructor/extractor for `ValDef` instances. * @group Extractors */ @@ -626,12 +542,6 @@ trait Trees { self: Universe => */ type DefDef >: Null <: ValOrDefDef with DefDefApi - /** A tag that preserves the identity of the `DefDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val DefDefTag: ClassTag[DefDef] - /** The constructor/extractor for `DefDef` instances. * @group Extractors */ @@ -681,12 +591,6 @@ trait Trees { self: Universe => */ type TypeDef >: Null <: MemberDef with TypeDefApi - /** A tag that preserves the identity of the `TypeDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeDefTag: ClassTag[TypeDef] - /** The constructor/extractor for `TypeDef` instances. * @group Extractors */ @@ -746,12 +650,6 @@ trait Trees { self: Universe => */ type LabelDef >: Null <: DefTree with TermTree with LabelDefApi - /** A tag that preserves the identity of the `LabelDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val LabelDefTag: ClassTag[LabelDef] - /** The constructor/extractor for `LabelDef` instances. * @group Extractors */ @@ -808,12 +706,6 @@ trait Trees { self: Universe => */ type ImportSelector >: Null <: AnyRef with ImportSelectorApi - /** A tag that preserves the identity of the `ImportSelector` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ImportSelectorTag: ClassTag[ImportSelector] - /** The constructor/extractor for `ImportSelector` instances. * @group Extractors */ @@ -860,12 +752,6 @@ trait Trees { self: Universe => */ type Import >: Null <: SymTree with ImportApi - /** A tag that preserves the identity of the `Import` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ImportTag: ClassTag[Import] - /** The constructor/extractor for `Import` instances. * @group Extractors */ @@ -918,12 +804,6 @@ trait Trees { self: Universe => */ type Template >: Null <: SymTree with TemplateApi - /** A tag that preserves the identity of the `Template` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TemplateTag: ClassTag[Template] - /** The constructor/extractor for `Template` instances. * @group Extractors */ @@ -976,12 +856,6 @@ trait Trees { self: Universe => */ type Block >: Null <: TermTree with BlockApi - /** A tag that preserves the identity of the `Block` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val BlockTag: ClassTag[Block] - /** The constructor/extractor for `Block` instances. * @group Extractors */ @@ -1021,12 +895,6 @@ trait Trees { self: Universe => */ type CaseDef >: Null <: AnyRef with Tree with CaseDefApi - /** A tag that preserves the identity of the `CaseDef` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val CaseDefTag: ClassTag[CaseDef] - /** The constructor/extractor for `CaseDef` instances. * @group Extractors */ @@ -1074,12 +942,6 @@ trait Trees { self: Universe => */ type Alternative >: Null <: TermTree with AlternativeApi - /** A tag that preserves the identity of the `Alternative` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AlternativeTag: ClassTag[Alternative] - /** The constructor/extractor for `Alternative` instances. * @group Extractors */ @@ -1112,12 +974,6 @@ trait Trees { self: Universe => */ type Star >: Null <: TermTree with StarApi - /** A tag that preserves the identity of the `Star` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val StarTag: ClassTag[Star] - /** The constructor/extractor for `Star` instances. * @group Extractors */ @@ -1153,12 +1009,6 @@ trait Trees { self: Universe => */ type Bind >: Null <: DefTree with BindApi - /** A tag that preserves the identity of the `Bind` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val BindTag: ClassTag[Bind] - /** The constructor/extractor for `Bind` instances. * @group Extractors */ @@ -1222,12 +1072,6 @@ trait Trees { self: Universe => */ type UnApply >: Null <: TermTree with UnApplyApi - /** A tag that preserves the identity of the `UnApply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val UnApplyTag: ClassTag[UnApply] - /** The constructor/extractor for `UnApply` instances. * @group Extractors */ @@ -1264,12 +1108,6 @@ trait Trees { self: Universe => */ type Function >: Null <: TermTree with SymTree with FunctionApi - /** A tag that preserves the identity of the `Function` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val FunctionTag: ClassTag[Function] - /** The constructor/extractor for `Function` instances. * @group Extractors */ @@ -1308,12 +1146,6 @@ trait Trees { self: Universe => */ type Assign >: Null <: TermTree with AssignApi - /** A tag that preserves the identity of the `Assign` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AssignTag: ClassTag[Assign] - /** The constructor/extractor for `Assign` instances. * @group Extractors */ @@ -1350,12 +1182,6 @@ trait Trees { self: Universe => */ type AssignOrNamedArg >: Null <: TermTree with AssignOrNamedArgApi - /** A tag that preserves the identity of the `AssignOrNamedArg` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AssignOrNamedArgTag: ClassTag[AssignOrNamedArg] - /** The constructor/extractor for `AssignOrNamedArg` instances. * @group Extractors */ @@ -1397,12 +1223,6 @@ trait Trees { self: Universe => */ type If >: Null <: TermTree with IfApi - /** A tag that preserves the identity of the `If` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val IfTag: ClassTag[If] - /** The constructor/extractor for `If` instances. * @group Extractors */ @@ -1454,12 +1274,6 @@ trait Trees { self: Universe => */ type Match >: Null <: TermTree with MatchApi - /** A tag that preserves the identity of the `Match` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val MatchTag: ClassTag[Match] - /** The constructor/extractor for `Match` instances. * @group Extractors */ @@ -1495,12 +1309,6 @@ trait Trees { self: Universe => */ type Return >: Null <: TermTree with SymTree with ReturnApi - /** A tag that preserves the identity of the `Return` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ReturnTag: ClassTag[Return] - /** The constructor/extractor for `Return` instances. * @group Extractors */ @@ -1533,12 +1341,6 @@ trait Trees { self: Universe => */ type Try >: Null <: TermTree with TryApi - /** A tag that preserves the identity of the `Try` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TryTag: ClassTag[Try] - /** The constructor/extractor for `Try` instances. * @group Extractors */ @@ -1577,12 +1379,6 @@ trait Trees { self: Universe => */ type Throw >: Null <: TermTree with ThrowApi - /** A tag that preserves the identity of the `Throw` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ThrowTag: ClassTag[Throw] - /** The constructor/extractor for `Throw` instances. * @group Extractors */ @@ -1613,12 +1409,6 @@ trait Trees { self: Universe => */ type New >: Null <: TermTree with NewApi - /** A tag that preserves the identity of the `New` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val NewTag: ClassTag[New] - /** The constructor/extractor for `New` instances. * @group Extractors */ @@ -1669,12 +1459,6 @@ trait Trees { self: Universe => */ type Typed >: Null <: TermTree with TypedApi - /** A tag that preserves the identity of the `Typed` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypedTag: ClassTag[Typed] - /** The constructor/extractor for `Typed` instances. * @group Extractors */ @@ -1708,12 +1492,6 @@ trait Trees { self: Universe => */ type GenericApply >: Null <: TermTree with GenericApplyApi - /** A tag that preserves the identity of the `GenericApply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val GenericApplyTag: ClassTag[GenericApply] - /** The API that all applies support * @group API */ @@ -1735,12 +1513,6 @@ trait Trees { self: Universe => */ type TypeApply >: Null <: GenericApply with TypeApplyApi - /** A tag that preserves the identity of the `TypeApply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeApplyTag: ClassTag[TypeApply] - /** The constructor/extractor for `TypeApply` instances. * @group Extractors */ @@ -1779,12 +1551,6 @@ trait Trees { self: Universe => */ type Apply >: Null <: GenericApply with ApplyApi - /** A tag that preserves the identity of the `Apply` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ApplyTag: ClassTag[Apply] - /** The constructor/extractor for `Apply` instances. * @group Extractors */ @@ -1822,12 +1588,6 @@ trait Trees { self: Universe => */ type Super >: Null <: TermTree with SuperApi - /** A tag that preserves the identity of the `Super` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SuperTag: ClassTag[Super] - /** The constructor/extractor for `Super` instances. * @group Extractors */ @@ -1874,12 +1634,6 @@ trait Trees { self: Universe => */ type This >: Null <: TermTree with SymTree with ThisApi - /** A tag that preserves the identity of the `This` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ThisTag: ClassTag[This] - /** The constructor/extractor for `This` instances. * @group Extractors */ @@ -1915,12 +1669,6 @@ trait Trees { self: Universe => */ type Select >: Null <: RefTree with SelectApi - /** A tag that preserves the identity of the `Select` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SelectTag: ClassTag[Select] - /** The constructor/extractor for `Select` instances. * @group Extractors */ @@ -1960,12 +1708,6 @@ trait Trees { self: Universe => */ type Ident >: Null <: RefTree with IdentApi - /** A tag that preserves the identity of the `Ident` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val IdentTag: ClassTag[Ident] - /** The constructor/extractor for `Ident` instances. * @group Extractors */ @@ -2005,12 +1747,6 @@ trait Trees { self: Universe => */ type ReferenceToBoxed >: Null <: TermTree with ReferenceToBoxedApi - /** A tag that preserves the identity of the `ReferenceToBoxed` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ReferenceToBoxedTag: ClassTag[ReferenceToBoxed] - /** The constructor/extractor for `ReferenceToBoxed` instances. * @group Extractors */ @@ -2055,12 +1791,6 @@ trait Trees { self: Universe => */ type Literal >: Null <: TermTree with LiteralApi - /** A tag that preserves the identity of the `Literal` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val LiteralTag: ClassTag[Literal] - /** The constructor/extractor for `Literal` instances. * @group Extractors */ @@ -2094,12 +1824,6 @@ trait Trees { self: Universe => */ type Annotated >: Null <: AnyRef with Tree with AnnotatedApi - /** A tag that preserves the identity of the `Annotated` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AnnotatedTag: ClassTag[Annotated] - /** The constructor/extractor for `Annotated` instances. * @group Extractors */ @@ -2134,12 +1858,6 @@ trait Trees { self: Universe => */ type SingletonTypeTree >: Null <: TypTree with SingletonTypeTreeApi - /** A tag that preserves the identity of the `SingletonTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SingletonTypeTreeTag: ClassTag[SingletonTypeTree] - /** The constructor/extractor for `SingletonTypeTree` instances. * @group Extractors */ @@ -2170,12 +1888,6 @@ trait Trees { self: Universe => */ type SelectFromTypeTree >: Null <: TypTree with RefTree with SelectFromTypeTreeApi - /** A tag that preserves the identity of the `SelectFromTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val SelectFromTypeTreeTag: ClassTag[SelectFromTypeTree] - /** The constructor/extractor for `SelectFromTypeTree` instances. * @group Extractors */ @@ -2217,12 +1929,6 @@ trait Trees { self: Universe => */ type CompoundTypeTree >: Null <: TypTree with CompoundTypeTreeApi - /** A tag that preserves the identity of the `CompoundTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val CompoundTypeTreeTag: ClassTag[CompoundTypeTree] - /** The constructor/extractor for `CompoundTypeTree` instances. * @group Extractors */ @@ -2253,12 +1959,6 @@ trait Trees { self: Universe => */ type AppliedTypeTree >: Null <: TypTree with AppliedTypeTreeApi - /** A tag that preserves the identity of the `AppliedTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val AppliedTypeTreeTag: ClassTag[AppliedTypeTree] - /** The constructor/extractor for `AppliedTypeTree` instances. * @group Extractors */ @@ -2301,12 +2001,6 @@ trait Trees { self: Universe => */ type TypeBoundsTree >: Null <: TypTree with TypeBoundsTreeApi - /** A tag that preserves the identity of the `TypeBoundsTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeBoundsTreeTag: ClassTag[TypeBoundsTree] - /** The constructor/extractor for `TypeBoundsTree` instances. * @group Extractors */ @@ -2344,12 +2038,6 @@ trait Trees { self: Universe => */ type ExistentialTypeTree >: Null <: TypTree with ExistentialTypeTreeApi - /** A tag that preserves the identity of the `ExistentialTypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val ExistentialTypeTreeTag: ClassTag[ExistentialTypeTree] - /** The constructor/extractor for `ExistentialTypeTree` instances. * @group Extractors */ @@ -2387,12 +2075,6 @@ trait Trees { self: Universe => */ type TypeTree >: Null <: TypTree with TypeTreeApi - /** A tag that preserves the identity of the `TypeTree` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Tags - */ - implicit val TypeTreeTag: ClassTag[TypeTree] - /** The constructor/extractor for `TypeTree` instances. * @group Extractors */ @@ -2990,12 +2672,6 @@ trait Trees { self: Universe => */ type Modifiers >: Null <: AnyRef with ModifiersApi - /** A tag that preserves the identity of the `Modifiers` abstract type from erasure. - * Can be used for pattern matching, instance tests, serialization and likes. - * @group Traversal - */ - implicit val ModifiersTag: ClassTag[Modifiers] - /** The API that all Modifiers support * @group API */ diff --git a/src/reflect/scala/reflect/api/Types.scala b/src/reflect/scala/reflect/api/Types.scala index e5140f23e5..1152f97350 100644 --- a/src/reflect/scala/reflect/api/Types.scala +++ b/src/reflect/scala/reflect/api/Types.scala @@ -50,7 +50,7 @@ package api * * @contentDiagram hideNodes "*Api" */ -trait Types extends ImplicitTags { +trait Types { self: Universe => /** The type of Scala types, and also Scala type signatures. diff --git a/src/reflect/scala/reflect/api/Universe.scala b/src/reflect/scala/reflect/api/Universe.scala index 4928b8bb38..16e533cf7b 100644 --- a/src/reflect/scala/reflect/api/Universe.scala +++ b/src/reflect/scala/reflect/api/Universe.scala @@ -68,6 +68,7 @@ abstract class Universe extends Symbols with Exprs with TypeTags with TagInterop + with ImplicitTags with StandardDefinitions with StandardNames with BuildUtils @@ -93,4 +94,4 @@ abstract class Universe extends Symbols // implementation is hardwired to `scala.reflect.reify.Taggers` // using the mechanism implemented in `scala.tools.reflect.FastTrack` def reify[T](expr: T): Expr[T] = ??? // macro -}
\ No newline at end of file +} diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala index 78f7438429..faf61e5205 100644 --- a/src/reflect/scala/reflect/internal/ClassfileConstants.scala +++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala @@ -72,6 +72,9 @@ object ClassfileConstants { final val CONSTANT_METHODREF = 10 final val CONSTANT_INTFMETHODREF = 11 final val CONSTANT_NAMEANDTYPE = 12 + final val CONSTANT_METHODHANDLE = 15 + final val CONSTANT_METHODTYPE = 16 + final val CONSTANT_INVOKEDYNAMIC = 18 // tags describing the type of a literal in attribute values final val BYTE_TAG = 'B' @@ -306,7 +309,7 @@ object ClassfileConstants { final val invokespecial = 0xb7 final val invokestatic = 0xb8 final val invokeinterface = 0xb9 - final val xxxunusedxxxx = 0xba + final val invokedynamic = 0xba final val new_ = 0xbb final val newarray = 0xbc diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 55954196f6..5392daf674 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -244,8 +244,8 @@ trait Definitions extends api.StandardDefinitions { (sym eq NoSymbol) || sym.isConstructor || sym.isPrivateLocal - || isUniversalMember(sym) ) + def isUnimportableUnlessRenamed(sym: Symbol) = isUnimportable(sym) || isUniversalMember(sym) def isImportable(sym: Symbol) = !isUnimportable(sym) /** Is this type equivalent to Any, AnyVal, or AnyRef? */ @@ -973,7 +973,7 @@ trait Definitions extends api.StandardDefinitions { getMemberIfDefined(owner, name) orElse { if (phase.flatClasses && name.isTypeName && !owner.isPackageObjectOrClass) { val pkg = owner.owner - val flatname = nme.flattenedName(owner.name, name) + val flatname = tpnme.flattenedName(owner.name, name) getMember(pkg, flatname) } else fatalMissingSymbol(owner, name) diff --git a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala index 281a32caf6..34bd400186 100644 --- a/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala +++ b/src/reflect/scala/reflect/internal/ExistentialsAndSkolems.scala @@ -31,4 +31,81 @@ trait ExistentialsAndSkolems { } (new Deskolemizer).typeSkolems } + + def isRawParameter(sym: Symbol) = // is it a type parameter leaked by a raw type? + sym.isTypeParameter && sym.owner.isJavaDefined + + /** If we map a set of hidden symbols to their existential bounds, we + * have a problem: the bounds may themselves contain references to the + * hidden symbols. So this recursively calls existentialBound until + * the typeSymbol is not amongst the symbols being hidden. + */ + private def existentialBoundsExcludingHidden(hidden: List[Symbol]): Map[Symbol, Type] = { + def safeBound(t: Type): Type = + if (hidden contains t.typeSymbol) safeBound(t.typeSymbol.existentialBound.bounds.hi) else t + + def hiBound(s: Symbol): Type = safeBound(s.existentialBound.bounds.hi) match { + case tp @ RefinedType(parents, decls) => + val parents1 = parents mapConserve safeBound + if (parents eq parents1) tp + else copyRefinedType(tp, parents1, decls) + case tp => tp + } + + // Hanging onto lower bound in case anything interesting + // happens with it. + mapFrom(hidden)(s => s.existentialBound match { + case TypeBounds(lo, hi) => TypeBounds(lo, hiBound(s)) + case _ => hiBound(s) + }) + } + + /** Given a set `rawSyms` of term- and type-symbols, and a type + * `tp`, produce a set of fresh type parameters and a type so that + * it can be abstracted to an existential type. Every type symbol + * `T` in `rawSyms` is mapped to a clone. Every term symbol `x` of + * type `T` in `rawSyms` is given an associated type symbol of the + * following form: + * + * type x.type <: T with Singleton + * + * The name of the type parameter is `x.type`, to produce nice + * diagnostics. The Singleton parent ensures that the type + * parameter is still seen as a stable type. Type symbols in + * rawSyms are fully replaced by the new symbols. Term symbols are + * also replaced, except for term symbols of an Ident tree, where + * only the type of the Ident is changed. + */ + final def existentialTransform[T](rawSyms: List[Symbol], tp: Type, rawOwner: Option[Symbol] = None)(creator: (List[Symbol], Type) => T): T = { + val allBounds = existentialBoundsExcludingHidden(rawSyms) + val typeParams: List[Symbol] = rawSyms map { sym => + val name = sym.name match { + case x: TypeName => x + case x => tpnme.singletonName(x) + } + def rawOwner0 = rawOwner.getOrElse(abort(s"no owner provided for existential transform over raw parameter: $sym")) + val bound = allBounds(sym) + val sowner = if (isRawParameter(sym)) rawOwner0 else sym.owner + val quantified = sowner.newExistential(name, sym.pos) + + quantified setInfo bound.cloneInfo(quantified) + } + // Higher-kinded existentials are not yet supported, but this is + // tpeHK for when they are: "if a type constructor is expected/allowed, + // tpeHK must be called instead of tpe." + val typeParamTypes = typeParams map (_.tpeHK) + def doSubst(info: Type) = info.subst(rawSyms, typeParamTypes) + + creator(typeParams map (_ modifyInfo doSubst), doSubst(tp)) + } + + /** + * Compute an existential type from hidden symbols `hidden` and type `tp`. + * @param hidden The symbols that will be existentially abstracted + * @param hidden The original type + * @param rawOwner The owner for Java raw types. + */ + final def packSymbols(hidden: List[Symbol], tp: Type, rawOwner: Option[Symbol] = None): Type = + if (hidden.isEmpty) tp + else existentialTransform(hidden, tp, rawOwner)(existentialAbstraction) } diff --git a/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala b/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala new file mode 100644 index 0000000000..3d1d1bf451 --- /dev/null +++ b/src/reflect/scala/reflect/internal/JMethodOrConstructor.scala @@ -0,0 +1,45 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala.reflect +package internal + +import java.lang.{ Class => jClass } +import java.lang.annotation.{ Annotation => jAnnotation } +import java.lang.reflect.{ + Member => jMember, Constructor => jConstructor, Method => jMethod, + AnnotatedElement => jAnnotatedElement, Type => jType, + TypeVariable => jTypeVariable +} + +/** This class tries to abstract over some of the duplication + * in java.lang.reflect.{ Method, Constructor }. + */ +class JMethodOrConstructor(val member: jMember with jAnnotatedElement) { + def isVarArgs: Boolean = member match { + case m: jMethod => m.isVarArgs + case m: jConstructor[_] => m.isVarArgs + } + def typeParams: Array[_ <: jTypeVariable[_]] = member match { + case m: jMethod => m.getTypeParameters + case m: jConstructor[_] => m.getTypeParameters + } + def paramTypes: Array[jType] = member match { + case m: jMethod => m.getGenericParameterTypes + case m: jConstructor[_] => m.getGenericParameterTypes + } + def paramAnnotations: Array[Array[jAnnotation]] = member match { + case m: jMethod => m.getParameterAnnotations + case m: jConstructor[_] => m.getParameterAnnotations + } + def resultType: jType = member match { + case m: jMethod => m.getGenericReturnType + case m: jConstructor[_] => classOf[Unit] + } +} + +object JMethodOrConstructor { + implicit def liftMethodToJmoc(m: jMethod): JMethodOrConstructor = new JMethodOrConstructor(m) + implicit def liftConstructorToJmoc(m: jConstructor[_]): JMethodOrConstructor = new JMethodOrConstructor(m) +} diff --git a/src/reflect/scala/reflect/internal/JavaAccFlags.scala b/src/reflect/scala/reflect/internal/JavaAccFlags.scala new file mode 100644 index 0000000000..4be1f828d3 --- /dev/null +++ b/src/reflect/scala/reflect/internal/JavaAccFlags.scala @@ -0,0 +1,83 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Paul Phillips + */ +package scala.reflect +package internal + +import java.lang.{ Class => jClass } +import java.lang.reflect.{ Member => jMember, Constructor => jConstructor, Field => jField, Method => jMethod } +import JavaAccFlags._ +import ClassfileConstants._ + +/** A value class which encodes the access_flags (JVMS 4.1) + * for a field, method, or class. The low 16 bits are the same + * as those returned by java.lang.reflect.Member#getModifiers + * and found in the bytecode. + * + * The high bits encode whether the access flags are directly + * associated with a class, constructor, field, or method. + */ +final class JavaAccFlags private (val coded: Int) extends AnyVal { + private def has(mask: Int) = (flags & mask) != 0 + private def flagCarrierId = coded >>> 16 + private def flags = coded & 0xFFFF + + def isAbstract = has(JAVA_ACC_ABSTRACT) + def isAnnotation = has(JAVA_ACC_ANNOTATION) + def isBridge = has(JAVA_ACC_BRIDGE) + def isEnum = has(JAVA_ACC_ENUM) + def isFinal = has(JAVA_ACC_FINAL) + def isInterface = has(JAVA_ACC_INTERFACE) + def isNative = has(JAVA_ACC_NATIVE) + def isPrivate = has(JAVA_ACC_PRIVATE) + def isProtected = has(JAVA_ACC_PROTECTED) + def isPublic = has(JAVA_ACC_PUBLIC) + def isStatic = has(JAVA_ACC_STATIC) + def isStrictFp = has(JAVA_ACC_STRICT) + def isSuper = has(JAVA_ACC_SUPER) + def isSynchronized = has(JAVA_ACC_SYNCHRONIZED) + def isSynthetic = has(JAVA_ACC_SYNTHETIC) + def isTransient = has(JAVA_ACC_TRANSIENT) + def isVarargs = has(JAVA_ACC_VARARGS) + def isVolatile = has(JAVA_ACC_VOLATILE) + + /** Do these flags describe a member which has either protected or package access? + * Such access in java is encoded in scala as protected[foo] or private[foo], where + * `foo` is the defining package. + */ + def hasPackageAccessBoundary = !has(JAVA_ACC_PRIVATE | JAVA_ACC_PUBLIC) // equivalently, allows protected or package level access + def isPackageProtected = !has(JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC) + + def toJavaFlags: Int = flags + def toScalaFlags: Long = flagCarrierId match { + case Method | Constructor => FlagTranslation methodFlags flags + case Class => FlagTranslation classFlags flags + case _ => FlagTranslation fieldFlags flags + } +} + +object JavaAccFlags { + private val Unknown = 0 + private val Class = 1 + private val Field = 2 + private val Method = 3 + private val Constructor = 4 + + private def create(flagCarrier: Int, access_flags: Int): JavaAccFlags = + new JavaAccFlags((flagCarrier << 16) | (access_flags & 0xFFFF)) + + def classFlags(flags: Int): JavaAccFlags = create(Class, flags) + def methodFlags(flags: Int): JavaAccFlags = create(Method, flags) + def fieldFlags(flags: Int): JavaAccFlags = create(Field, flags) + def constructorFlags(flags: Int): JavaAccFlags = create(Constructor, flags) + + def apply(access_flags: Int): JavaAccFlags = create(Unknown, access_flags) + def apply(clazz: jClass[_]): JavaAccFlags = classFlags(clazz.getModifiers) + def apply(member: jMember): JavaAccFlags = member match { + case x: jConstructor[_] => constructorFlags(x.getModifiers) + case x: jMethod => methodFlags(x.getModifiers) + case x: jField => fieldFlags(x.getModifiers) + case _ => apply(member.getModifiers) + } +} diff --git a/src/reflect/scala/reflect/internal/Kinds.scala b/src/reflect/scala/reflect/internal/Kinds.scala index cc8dd16d69..3c49aef05a 100644 --- a/src/reflect/scala/reflect/internal/Kinds.scala +++ b/src/reflect/scala/reflect/internal/Kinds.scala @@ -145,7 +145,7 @@ trait Kinds { kindErrors = f(kindErrors) } - if (settings.debug.value) { + if (settings.debug) { log("checkKindBoundsHK expected: "+ param +" with params "+ hkparams +" by definition in "+ paramowner) log("checkKindBoundsHK supplied: "+ arg +" with params "+ hkargs +" from "+ owner) log("checkKindBoundsHK under params: "+ underHKParams +" with args "+ withHKArgs) @@ -201,7 +201,7 @@ trait Kinds { else NoKindErrors } - if (settings.debug.value && (tparams.nonEmpty || targs.nonEmpty)) log( + if (settings.debug && (tparams.nonEmpty || targs.nonEmpty)) log( "checkKindBounds0(" + tparams + ", " + targs + ", " + pre + ", " + owner + ", " + explainErrors + ")" ) diff --git a/src/reflect/scala/reflect/internal/Mirrors.scala b/src/reflect/scala/reflect/internal/Mirrors.scala index 63178d0b39..81d7619f22 100644 --- a/src/reflect/scala/reflect/internal/Mirrors.scala +++ b/src/reflect/scala/reflect/internal/Mirrors.scala @@ -46,7 +46,7 @@ trait Mirrors extends api.Mirrors { val result = if (path.isTermName) sym.suchThat(_ hasFlag MODULE) else sym if (result != NoSymbol) result else { - if (settings.debug.value) { log(sym.info); log(sym.info.members) }//debug + if (settings.debug) { log(sym.info); log(sym.info.members) }//debug thisMirror.missingHook(owner, name) orElse { MissingRequirementError.notFound((if (path.isTermName) "object " else "class ")+path+" in "+thisMirror) } diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index f8598dca7a..b8141d25f5 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -177,6 +177,12 @@ trait Names extends api.Names { /** @return the hash value of this name */ final override def hashCode(): Int = index + /** @return true if the string value of this name is equal + * to the string value of the given name or String. + */ + def string_==(that: Name): Boolean = (that ne null) && (toString == that.toString) + def string_==(that: String): Boolean = (that ne null) && (toString == that) + /**** * This has been quite useful to find places where people are comparing * a TermName and a TypeName, or a Name and a String. @@ -210,7 +216,7 @@ trait Names extends api.Names { /** @return the index of first occurrence of char c in this name, length if not found */ final def pos(c: Char): Int = pos(c, 0) - /** @return the index of first occurrence of char c in this name, length if not found */ + /** @return the index of first occurrence of s in this name, length if not found */ final def pos(s: String): Int = pos(s, 0) /** Returns the index of the first occurrence of character c in @@ -319,15 +325,18 @@ trait Names extends api.Names { final def endsWith(char: Char): Boolean = len > 0 && endChar == char final def endsWith(name: String): Boolean = endsWith(newTermName(name)) - def indexOf(ch: Char) = { - val idx = pos(ch) - if (idx == length) -1 else idx - } - def indexOf(ch: Char, fromIndex: Int) = { - val idx = pos(ch, fromIndex) - if (idx == length) -1 else idx - } - def lastIndexOf(ch: Char) = lastPos(ch) + /** Rewrite the confusing failure indication via result == length to + * the normal failure indication via result == -1. + */ + private def fixIndexOf(idx: Int): Int = if (idx == length) -1 else idx + + def indexOf(ch: Char) = fixIndexOf(pos(ch)) + def indexOf(ch: Char, fromIndex: Int) = fixIndexOf(pos(ch, fromIndex)) + def indexOf(s: String) = fixIndexOf(pos(s)) + + /** The lastPos methods already return -1 on failure. */ + def lastIndexOf(ch: Char): Int = lastPos(ch) + def lastIndexOf(s: String): Int = toString lastIndexOf s /** Replace all occurrences of `from` by `to` in * name; result is always a term name. @@ -392,9 +401,24 @@ trait Names extends api.Names { * reap the benefits because an (unused) $outer pointer so it is not single-field. */ final class NameOps[T <: Name](name: T) { - def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name - def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T] - def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T] + import NameTransformer._ + def stripSuffix(suffix: String): T = stripSuffix(suffix: TermName) + def stripSuffix(suffix: Name): T = if (name endsWith suffix) dropRight(suffix.length) else name + def take(n: Int): T = name.subName(0, n).asInstanceOf[T] + def drop(n: Int): T = name.subName(n, name.length).asInstanceOf[T] + def dropRight(n: Int): T = name.subName(0, name.length - n).asInstanceOf[T] + def dropLocal: TermName = name.toTermName stripSuffix LOCAL_SUFFIX_STRING + def dropSetter: TermName = name.toTermName stripSuffix SETTER_SUFFIX_STRING + def dropModule: T = this stripSuffix MODULE_SUFFIX_STRING + def localName: TermName = getterName append LOCAL_SUFFIX_STRING + def setterName: TermName = getterName append SETTER_SUFFIX_STRING + def getterName: TermName = dropTraitSetterSeparator.dropSetter.dropLocal + + private def dropTraitSetterSeparator: TermName = + name indexOf TRAIT_SETTER_SEPARATOR_STRING match { + case -1 => name.toTermName + case idx => name.toTermName drop idx drop TRAIT_SETTER_SEPARATOR_STRING.length + } } implicit val NameTag = ClassTag[Name](classOf[Name]) diff --git a/src/reflect/scala/reflect/internal/Positions.scala b/src/reflect/scala/reflect/internal/Positions.scala index cc32a0363f..3cf4f4a1df 100644 --- a/src/reflect/scala/reflect/internal/Positions.scala +++ b/src/reflect/scala/reflect/internal/Positions.scala @@ -107,7 +107,7 @@ trait Positions extends api.Positions { self: SymbolTable => def validate(tree: Tree, encltree: Tree): Unit = { if (!tree.isEmpty && tree.canHaveAttrs) { - if (settings.Yposdebug.value && (settings.verbose.value || settings.Yrangepos.value)) + if (settings.Yposdebug && (settings.verbose || settings.Yrangepos)) println("[%10s] %s".format("validate", treeStatus(tree, encltree))) if (!tree.pos.isDefined) diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 28837c4ae8..5b80889225 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -29,18 +29,19 @@ trait Printers extends api.Printers { self: SymbolTable => def quotedName(name: String): String = quotedName(newTermName(name), decode = false) private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { - val sym = tree.symbol - if (sym.name.toString == nme.ERROR.toString) { - "<" + quotedName(name, decoded) + ": error>" - } else if (sym != null && sym != NoSymbol) { - val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" - var suffix = "" - if (settings.uniqid.value) suffix += ("#" + sym.id) - if (settings.Yshowsymkinds.value) suffix += ("#" + sym.abbreviatedKindString) - prefix + quotedName(tree.symbol.decodedName) + suffix - } else { - quotedName(name, decoded) - } + val sym = tree.symbol + def qname = quotedName(name.dropLocal, decoded) + def qowner = quotedName(sym.owner.name.dropLocal, decoded) + def qsymbol = quotedName(sym.nameString) + + if (sym.name.toTermName == nme.ERROR) + s"<$qname: error>" + else if (sym == null || sym == NoSymbol) + qname + else if (sym.isMixinConstructor) + s"/*$qowner*/$qsymbol" + else + qsymbol } def decodedSymName(tree: Tree, name: Name) = symNameInternal(tree, name, decoded = true) @@ -168,7 +169,7 @@ trait Printers extends api.Printers { self: SymbolTable => ) def printFlags(flags: Long, privateWithin: String) { - val mask: Long = if (settings.debug.value) -1L else PrintableFlags + val mask: Long = if (settings.debug) -1L else PrintableFlags val s = flagsToString(flags & mask, privateWithin) if (s != "") print(s + " ") } @@ -375,7 +376,7 @@ trait Printers extends api.Printers { self: SymbolTable => if (!qual.isEmpty) print(symName(tree, qual) + ".") print("this") - case Select(qual @ New(tpe), name) if (!settings.debug.value) => + case Select(qual @ New(tpe), name) if !settings.debug => print(qual) case Select(qualifier, name) => @@ -546,7 +547,7 @@ trait Printers extends api.Printers { self: SymbolTable => print("pendingSuperCall") case tree: Tree => val hasSymbolField = tree.hasSymbolField && tree.symbol != NoSymbol - val isError = hasSymbolField && tree.symbol.name.toString == nme.ERROR.toString + val isError = hasSymbolField && (tree.symbol.name string_== nme.ERROR) printProduct( tree, preamble = _ => { diff --git a/src/reflect/scala/reflect/internal/PrivateWithin.scala b/src/reflect/scala/reflect/internal/PrivateWithin.scala index 9b99b94b41..5646ac82ae 100644 --- a/src/reflect/scala/reflect/internal/PrivateWithin.scala +++ b/src/reflect/scala/reflect/internal/PrivateWithin.scala @@ -1,23 +1,26 @@ package scala.reflect package internal -import ClassfileConstants._ +import java.lang.{ Class => jClass } +import java.lang.reflect.{ Member => jMember } trait PrivateWithin { self: SymbolTable => - def importPrivateWithinFromJavaFlags(sym: Symbol, jflags: Int): Symbol = { - if ((jflags & (JAVA_ACC_PRIVATE | JAVA_ACC_PROTECTED | JAVA_ACC_PUBLIC)) == 0) - // See ticket #1687 for an example of when topLevelClass is NoSymbol: it - // apparently occurs when processing v45.3 bytecode. - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - - // protected in java means package protected. #3946 - if ((jflags & JAVA_ACC_PROTECTED) != 0) - if (sym.enclosingTopLevelClass != NoSymbol) - sym.privateWithin = sym.enclosingTopLevelClass.owner - - sym + def propagatePackageBoundary(c: jClass[_], syms: Symbol*): Unit = + propagatePackageBoundary(JavaAccFlags(c), syms: _*) + def propagatePackageBoundary(m: jMember, syms: Symbol*): Unit = + propagatePackageBoundary(JavaAccFlags(m), syms: _*) + def propagatePackageBoundary(jflags: JavaAccFlags, syms: Symbol*) { + if (jflags.hasPackageAccessBoundary) + syms foreach setPackageAccessBoundary } -}
\ No newline at end of file + + // protected in java means package protected. #3946 + // See ticket #1687 for an example of when the enclosing top level class is NoSymbol; + // it apparently occurs when processing v45.3 bytecode. + def setPackageAccessBoundary(sym: Symbol): Symbol = ( + if (sym.enclosingTopLevelClass eq NoSymbol) sym + else sym setPrivateWithin sym.enclosingTopLevelClass.owner + ) +} diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index 850c497d4b..371eddbc4f 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -6,6 +6,8 @@ package scala.reflect package internal +import scala.annotation.tailrec + trait Scopes extends api.Scopes { self: SymbolTable => /** An ADT to represent the results of symbol name lookups. @@ -65,6 +67,11 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** a cache for all elements, to be used by symbol iterator. */ private var elemsCache: List[Symbol] = null + private var cachedSize = -1 + private def flushElemsCache() { + elemsCache = null + cachedSize = -1 + } /** size and mask of hash tables * todo: make hashtables grow? @@ -86,6 +93,12 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** the number of entries in this scope */ override def size: Int = { + if (cachedSize < 0) + cachedSize = directSize + + cachedSize + } + private def directSize: Int = { var s = 0 var e = elems while (e ne null) { @@ -98,7 +111,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => /** enter a scope entry */ protected def enterEntry(e: ScopeEntry) { - elemsCache = null + flushElemsCache() if (hashtable ne null) enterInHash(e) else if (size >= MIN_HASH) @@ -192,7 +205,7 @@ trait Scopes extends api.Scopes { self: SymbolTable => e1.tail = e.tail } } - elemsCache = null + flushElemsCache() } /** remove symbol */ @@ -304,16 +317,43 @@ trait Scopes extends api.Scopes { self: SymbolTable => e } + /** TODO - we can test this more efficiently than checking isSubScope + * in both directions. However the size test might be enough to quickly + * rule out most failures. + */ + def isSameScope(other: Scope) = ( + (size == other.size) // optimization - size is cached + && (this isSubScope other) + && (other isSubScope this) + ) + + def isSubScope(other: Scope) = { + def scopeContainsSym(sym: Symbol): Boolean = { + @tailrec def entryContainsSym(e: ScopeEntry): Boolean = e match { + case null => false + case _ => + val comparableInfo = sym.info.substThis(sym.owner, e.sym.owner) + (e.sym.info =:= comparableInfo) || entryContainsSym(lookupNextEntry(e)) + } + entryContainsSym(this lookupEntry sym.name) + } + other.toList forall scopeContainsSym + } + /** Return all symbols as a list in the order they were entered in this scope. */ override def toList: List[Symbol] = { if (elemsCache eq null) { - elemsCache = Nil + var symbols: List[Symbol] = Nil + var count = 0 var e = elems while ((e ne null) && e.owner == this) { - elemsCache = e.sym :: elemsCache + count += 1 + symbols ::= e.sym e = e.next } + elemsCache = symbols + cachedSize = count } elemsCache } diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index a894bd649c..ae2cf09c2e 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -86,8 +86,12 @@ trait StdNames { def flattenedName(segments: Name*): NameType = compactify(segments mkString NAME_JOIN_STRING) - val MODULE_SUFFIX_STRING: String = NameTransformer.MODULE_SUFFIX_STRING - val NAME_JOIN_STRING: String = NameTransformer.NAME_JOIN_STRING + val NAME_JOIN_STRING: String = NameTransformer.NAME_JOIN_STRING + val MODULE_SUFFIX_STRING: String = NameTransformer.MODULE_SUFFIX_STRING + val SETTER_SUFFIX_STRING: String = NameTransformer.SETTER_SUFFIX_STRING + val LOCAL_SUFFIX_STRING: String = NameTransformer.LOCAL_SUFFIX_STRING + val TRAIT_SETTER_SEPARATOR_STRING: String = NameTransformer.TRAIT_SETTER_SEPARATOR_STRING + val SINGLETON_SUFFIX: String = ".type" val ANON_CLASS_NAME: NameType = "$anon" @@ -265,7 +269,7 @@ trait StdNames { val BITMAP_PREFIX = "bitmap$" val CHECK_IF_REFUTABLE_STRING = "check$ifrefutable$" val DEFAULT_GETTER_STRING = "$default$" - val DEFAULT_GETTER_INIT_STRING = "$lessinit$greater" // CONSTRUCTOR.encoded, less is more + val DEFAULT_GETTER_INIT_STRING = NameTransformer.encode("<init>") + DEFAULT_GETTER_STRING val DO_WHILE_PREFIX = "doWhile$" val EVIDENCE_PARAM_PREFIX = "evidence$" val EXCEPTION_RESULT_PREFIX = "exceptionResult" @@ -275,7 +279,6 @@ trait StdNames { val PROTECTED_PREFIX = "protected$" val PROTECTED_SET_PREFIX = PROTECTED_PREFIX + "set" val SUPER_PREFIX_STRING = "super$" - val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$" val WHILE_PREFIX = "while$" // Compiler internal names @@ -284,10 +287,8 @@ trait StdNames { val DEFAULT_CASE: NameType = "defaultCase$" val EQEQ_LOCAL_VAR: NameType = "eqEqTemp$" val FAKE_LOCAL_THIS: NameType = "this$" - val INITIALIZER: NameType = CONSTRUCTOR // Is this buying us something? val LAZY_LOCAL: NameType = "$lzy" val LAZY_SLOW_SUFFIX: NameType = "$lzycompute" - val LOCAL_SUFFIX_STRING = " " val UNIVERSE_BUILD_PREFIX: NameType = "$u.build." val UNIVERSE_PREFIX: NameType = "$u." val UNIVERSE_SHORT: NameType = "$u" @@ -301,21 +302,16 @@ trait StdNames { val MIXIN_CONSTRUCTOR: NameType = "$init$" val MODULE_INSTANCE_FIELD: NameType = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" val OUTER: NameType = "$outer" - val OUTER_LOCAL: NameType = OUTER + LOCAL_SUFFIX_STRING // "$outer ", note the space + val OUTER_LOCAL: NameType = OUTER.localName val OUTER_SYNTH: NameType = "<outer>" // emitted by virtual pattern matcher, replaced by outer accessor in explicitouter val ROOTPKG: NameType = "_root_" val SELECTOR_DUMMY: NameType = "<unapply-selector>" val SELF: NameType = "$this" - val SETTER_SUFFIX: NameType = encode("_=") + val SETTER_SUFFIX: NameType = NameTransformer.SETTER_SUFFIX_STRING val SPECIALIZED_INSTANCE: NameType = "specInstance$" val STAR: NameType = "*" val THIS: NameType = "_$this" - @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") - def SPECIALIZED_SUFFIX_STRING = SPECIALIZED_SUFFIX.toString - @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") - def SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX.toTermName - def isConstructorName(name: Name) = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR def isExceptionResultName(name: Name) = name startsWith EXCEPTION_RESULT_PREFIX def isImplClassName(name: Name) = name endsWith IMPL_CLASS_SUFFIX @@ -345,31 +341,52 @@ trait StdNames { name.endChar == '=' && name.startChar != '=' && isOperatorPart(name.startChar) } - /** The expanded name of `name` relative to this class `base` with given `separator` - */ - def expandedName(name: TermName, base: Symbol, separator: String = EXPAND_SEPARATOR_STRING): TermName = + private def expandedNameInternal(name: TermName, base: Symbol, separator: String): TermName = newTermNameCached(base.fullName('$') + separator + name) + /** The expanded name of `name` relative to this class `base` + */ + def expandedName(name: TermName, base: Symbol) = expandedNameInternal(name, base, EXPAND_SEPARATOR_STRING) + /** The expanded setter name of `name` relative to this class `base` */ - def expandedSetterName(name: TermName, base: Symbol): TermName = - expandedName(name, base, separator = TRAIT_SETTER_SEPARATOR_STRING) + def expandedSetterName(name: TermName, base: Symbol) = expandedNameInternal(name, base, TRAIT_SETTER_SEPARATOR_STRING) - /** If `name` is an expandedName name, the original name. - * Otherwise `name` itself. - */ - def originalName(name: Name): Name = { - var i = name.length - while (i >= 2 && !(name.charAt(i - 1) == '$' && name.charAt(i - 2) == '$')) i -= 1 - if (i >= 2) { - while (i >= 3 && name.charAt(i - 3) == '$') i -= 1 - name.subName(i, name.length) - } else name + /** If `name` is an expandedName name, the original (unexpanded) name. + * Otherwise `name` itself. + * Look backward from the end of the string for "$$", and take the + * part of the string after that; but if the string is "$$$" or longer, + * be sure to retain the extra dollars. + */ + def unexpandedName(name: Name): Name = name lastIndexOf "$$" match { + case 0 | -1 => name + case idx0 => + // Sketchville - We've found $$ but if it's part of $$$ or $$$$ + // or something we need to keep the bonus dollars, so e.g. foo$$$outer + // has an original name of $outer. + var idx = idx0 + while (idx > 0 && name.charAt(idx - 1) == '$') + idx -= 1 + name drop idx + 2 } + @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") + def SPECIALIZED_SUFFIX_STRING = SPECIALIZED_SUFFIX.toString + @deprecated("Use SPECIALIZED_SUFFIX", "2.10.0") + def SPECIALIZED_SUFFIX_NAME: TermName = SPECIALIZED_SUFFIX.toTermName + + @deprecated("Use unexpandedName", "2.11.0") def originalName(name: Name): Name = unexpandedName(name) + @deprecated("Use Name#dropModule", "2.11.0") def stripModuleSuffix(name: Name): Name = name.dropModule + @deprecated("Use Name#dropLocal", "2.11.0") def localToGetter(name: TermName): TermName = name.dropLocal + @deprecated("Use Name#dropLocal", "2.11.0") def dropLocalSuffix(name: Name): TermName = name.dropLocal + @deprecated("Use Name#localName", "2.11.0") def getterToLocal(name: TermName): TermName = name.localName + @deprecated("Use Name#setterName", "2.11.0") def getterToSetter(name: TermName): TermName = name.setterName + @deprecated("Use Name#getterName", "2.11.0") def getterName(name: TermName): TermName = name.getterName + @deprecated("Use Name#getterName", "2.11.0") def setterToGetter(name: TermName): TermName = name.getterName + def unspecializedName(name: Name): Name = ( if (name endsWith SPECIALIZED_SUFFIX) - name.subName(0, name.lastIndexOf('m') - 1) + name.subName(0, name.lastIndexOf('m') - 1) else name ) @@ -394,39 +411,23 @@ trait StdNames { } else (name, "", "") - def getterName(name: TermName): TermName = if (isLocalName(name)) localToGetter(name) else name - def getterToLocal(name: TermName): TermName = name append LOCAL_SUFFIX_STRING - def getterToSetter(name: TermName): TermName = name append SETTER_SUFFIX - def localToGetter(name: TermName): TermName = name dropRight LOCAL_SUFFIX_STRING.length - - def dropLocalSuffix(name: Name): Name = if (name endsWith ' ') name dropRight 1 else name - - def setterToGetter(name: TermName): TermName = { - val p = name.pos(TRAIT_SETTER_SEPARATOR_STRING) - if (p < name.length) - setterToGetter(name drop (p + TRAIT_SETTER_SEPARATOR_STRING.length)) - else - name.subName(0, name.length - SETTER_SUFFIX.length) - } - // Nominally, name$default$N, encoded for <init> - def defaultGetterName(name: Name, pos: Int): TermName = { - val prefix = if (isConstructorName(name)) DEFAULT_GETTER_INIT_STRING else name - newTermName(prefix + DEFAULT_GETTER_STRING + pos) - } + def defaultGetterName(name: Name, pos: Int): TermName = ( + if (isConstructorName(name)) + DEFAULT_GETTER_INIT_STRING + pos + else + name + DEFAULT_GETTER_STRING + pos + ) // Nominally, name from name$default$N, CONSTRUCTOR for <init> - def defaultGetterToMethod(name: Name): TermName = { - val p = name.pos(DEFAULT_GETTER_STRING) - if (p < name.length) { - val q = name.toTermName.subName(0, p) - // i.e., if (q.decoded == CONSTRUCTOR.toString) CONSTRUCTOR else q - if (q.toString == DEFAULT_GETTER_INIT_STRING) CONSTRUCTOR else q - } else name.toTermName - } - - def stripModuleSuffix(name: Name): Name = ( - if (isModuleName(name)) name dropRight MODULE_SUFFIX_STRING.length else name + def defaultGetterToMethod(name: Name): TermName = ( + if (name startsWith DEFAULT_GETTER_INIT_STRING) + nme.CONSTRUCTOR + else name indexOf DEFAULT_GETTER_STRING match { + case -1 => name.toTermName + case idx => name.toTermName take idx + } ) + def localDummyName(clazz: Symbol): TermName = newTermName(LOCALDUMMY_PREFIX + clazz.name + ">") def superName(name: Name): TermName = newTermName(SUPER_PREFIX_STRING + name) diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index 03ec59f0fe..336c2748c6 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/src/reflect/scala/reflect/internal/SymbolTable.scala @@ -60,8 +60,8 @@ abstract class SymbolTable extends macros.Universe def debugwarn(msg: => String): Unit = devWarning(msg) /** Override with final implementation for inlining. */ - def debuglog(msg: => String): Unit = if (settings.debug.value) log(msg) - def devWarning(msg: => String): Unit = if (settings.debug.value) Console.err.println(msg) + def debuglog(msg: => String): Unit = if (settings.debug) log(msg) + def devWarning(msg: => String): Unit = if (settings.debug) Console.err.println(msg) def throwableAsString(t: Throwable): String = "" + t /** Prints a stack trace if -Ydebug or equivalent was given, otherwise does nothing. */ diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index c881de7830..8ef2805529 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -190,7 +190,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def varianceString: String = variance.symbolicString override def flagMask = - if (settings.debug.value && !isAbstractType) AllFlags + if (settings.debug && !isAbstractType) AllFlags else if (owner.isRefinementClass) ExplicitFlags & ~OVERRIDE else ExplicitFlags @@ -202,7 +202,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def shortSymbolClass = shortClassOfInstance(this) def symbolCreationString: String = ( "%s%25s | %-40s | %s".format( - if (settings.uniqid.value) "%06d | ".format(id) else "", + if (settings.uniqid) "%06d | ".format(id) else "", shortSymbolClass, name.decode + " in " + owner, rawFlagString @@ -761,16 +761,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => def compileTimeOnlyMessage = getAnnotation(CompileTimeOnlyAttr) flatMap (_ stringArg 0) /** Is this symbol an accessor method for outer? */ - final def isOuterAccessor = { - hasFlag(STABLE | ARTIFACT) && - originalName == nme.OUTER - } + final def isOuterAccessor = hasFlag(STABLE | ARTIFACT) && (unexpandedName == nme.OUTER) /** Is this symbol an accessor method for outer? */ - final def isOuterField = { - hasFlag(ARTIFACT) && - originalName == nme.OUTER_LOCAL - } + final def isOuterField = isArtifact && (unexpandedName == nme.OUTER_LOCAL) /** Does this symbol denote a stable value? */ def isStable = false @@ -834,7 +828,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Is this symbol effectively final? I.e, it cannot be overridden */ final def isEffectivelyFinal: Boolean = ( (this hasFlag FINAL | PACKAGE) - || isModuleOrModuleClass && (isTopLevel || !settings.overrideObjects.value) + || isModuleOrModuleClass && (isTopLevel || !settings.overrideObjects) || isTerm && ( isPrivate || isLocal @@ -995,10 +989,12 @@ trait Symbols extends api.Symbols { self: SymbolTable => // ------ name attribute -------------------------------------------------------------- - /** If this symbol has an expanded name, its original name, otherwise its name itself. - * @see expandName + @deprecated("Use unexpandedName", "2.11.0") def originalName: Name = unexpandedName + + /** If this symbol has an expanded name, its original (unexpanded) name, + * otherwise the name itself. */ - def originalName: Name = nme.originalName(nme.dropLocalSuffix(name)) + def unexpandedName: Name = nme.unexpandedName(name) /** The name of the symbol before decoding, e.g. `\$eq\$eq` instead of `==`. */ @@ -1006,7 +1002,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The decoded name of the symbol, e.g. `==` instead of `\$eq\$eq`. */ - def decodedName: String = nme.dropLocalSuffix(name).decode + def decodedName: String = name.decode private def addModuleSuffix(n: Name): Name = if (needsModuleSuffix) n append nme.MODULE_SUFFIX_STRING else n @@ -1025,7 +1021,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => ) /** These should be moved somewhere like JavaPlatform. */ - def javaSimpleName: Name = addModuleSuffix(nme.dropLocalSuffix(simpleName)) + def javaSimpleName: Name = addModuleSuffix(simpleName.dropLocal) def javaBinaryName: Name = addModuleSuffix(fullNameInternal('/')) def javaClassName: String = addModuleSuffix(fullNameInternal('.')).toString @@ -1046,7 +1042,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => else ((effectiveOwner.enclClass.fullNameAsName(separator) append separator): Name) append name ) - def fullNameAsName(separator: Char): Name = nme.dropLocalSuffix(fullNameInternal(separator)) + def fullNameAsName(separator: Char): Name = fullNameInternal(separator).dropLocal /** The encoded full path name of this symbol, where outer names and inner names * are separated by periods. @@ -1823,7 +1819,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => // // The slightly more principled approach of using the paramss of the // primary constructor leads to cycles in, for example, pos/t5084.scala. - val primaryNames = constrParamAccessors.map(acc => nme.dropLocalSuffix(acc.name)) + val primaryNames = constrParamAccessors map (_.name.dropLocal) caseFieldAccessorsUnsorted.sortBy { acc => primaryNames indexWhere { orig => (acc.name == orig) || (acc.name startsWith (orig append "$")) @@ -1842,7 +1838,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The symbol accessed by this accessor function, but with given owner type. */ final def accessed(ownerTp: Type): Symbol = { assert(hasAccessorFlag, this) - ownerTp decl nme.getterToLocal(getterName.toTermName) + ownerTp decl localName } /** The module corresponding to this module class (note that this @@ -2199,22 +2195,23 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** The getter of this value or setter definition in class `base`, or NoSymbol if * none exists. */ - final def getter(base: Symbol): Symbol = base.info.decl(getterName) filter (_.hasAccessorFlag) + final def getter(base: Symbol): Symbol = + base.info decl getterName filter (_.hasAccessorFlag) - def getterName: TermName = ( - if (isSetter) nme.setterToGetter(name.toTermName) - else if (nme.isLocalName(name)) nme.localToGetter(name.toTermName) - else name.toTermName - ) + def getterName: TermName = name.getterName + def setterName: TermName = name.setterName + def localName: TermName = name.localName /** The setter of this value or getter definition, or NoSymbol if none exists */ - final def setter(base: Symbol): Symbol = setter(base, hasExpandedName = false) + final def setter(base: Symbol, hasExpandedName: Boolean = needsExpandedSetterName): Symbol = + base.info decl setterNameInBase(base, hasExpandedName) filter (_.hasAccessorFlag) - final def setter(base: Symbol, hasExpandedName: Boolean): Symbol = { - var sname = nme.getterToSetter(nme.getterName(name.toTermName)) - if (hasExpandedName) sname = nme.expandedSetterName(sname, base) - base.info.decl(sname) filter (_.hasAccessorFlag) - } + def needsExpandedSetterName = ( + if (isMethod) hasStableFlag && !isLazy + else hasNoFlags(LAZY | MUTABLE) + ) + def setterNameInBase(base: Symbol, expanded: Boolean): TermName = + if (expanded) nme.expandedSetterName(setterName, base) else setterName /** If this is a derived value class, return its unbox method * or NoSymbol if it does not exist. @@ -2391,12 +2388,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => * If settings.uniqid, adds id. * If settings.Yshowsymkinds, adds abbreviated symbol kind. */ - def nameString: String = ( - if (!settings.uniqid.value && !settings.Yshowsymkinds.value) "" + originalName.decode - else if (settings.uniqid.value && !settings.Yshowsymkinds.value) originalName.decode + "#" + id - else if (!settings.uniqid.value && settings.Yshowsymkinds.value) originalName.decode + "#" + abbreviatedKindString - else originalName.decode + "#" + id + "#" + abbreviatedKindString - ) + def nameString: String = { + val name_s = if (settings.debug.value) "" + unexpandedName else unexpandedName.dropLocal.decode + val id_s = if (settings.uniqid.value) "#" + id else "" + val kind_s = if (settings.Yshowsymkinds.value) "#" + abbreviatedKindString else "" + + name_s + id_s + kind_s + } def fullNameString: String = { def recur(sym: Symbol): String = { @@ -2623,20 +2621,32 @@ trait Symbols extends api.Symbols { self: SymbolTable => } /** change name by appending $$<fully-qualified-name-of-class `base`> - * Do the same for any accessed symbols or setters/getters + * Do the same for any accessed symbols or setters/getters. + * If the accessor to be renamed is overriding a base symbol, enter + * a cloned symbol with the original name but without ACCESSOR flag. */ override def expandName(base: Symbol) { - if (!hasFlag(EXPANDEDNAME)) { - setFlag(EXPANDEDNAME) - if (hasAccessorFlag && !isDeferred) { - accessed.expandName(base) - } - else if (hasGetter) { - getter(owner).expandName(base) - setter(owner).expandName(base) - } - name = nme.expandedName(name.toTermName, base) + def expand(sym: Symbol) { + if ((sym eq NoSymbol) || (sym hasFlag EXPANDEDNAME)) () // skip + else sym setFlag EXPANDEDNAME setName nme.expandedName(sym.name.toTermName, base) + } + def cloneAndExpand(accessor: Symbol) { + val clone = accessor.cloneSymbol(accessor.owner, (accessor.flags | ARTIFACT) & ~ACCESSOR) + expand(accessor) + log(s"Expanded overriding accessor to $accessor, but cloned $clone to preserve override") + accessor.owner.info.decls enter clone + } + def expandAccessor(accessor: Symbol) { + if (accessor.isOverridingSymbol) cloneAndExpand(accessor) else expand(accessor) + } + if (hasAccessorFlag && !isDeferred) { + expand(accessed) + } + else if (hasGetter) { + expandAccessor(getter(owner)) + expandAccessor(setter(owner)) } + expand(this) } } implicit val TermSymbolTag = ClassTag[TermSymbol](classOf[TermSymbol]) @@ -3065,7 +3075,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => if (Statistics.canEnable) Statistics.incCounter(nameCount) if (needsFlatClasses) { if (flatname eq null) - flatname = nme.flattenedName(rawowner.name, rawname).toTypeName + flatname = tpnme.flattenedName(rawowner.name, rawname) flatname } diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index e96fcc90df..b1f58814c7 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/src/reflect/scala/reflect/internal/TreeInfo.scala @@ -188,7 +188,7 @@ abstract class TreeInfo { def isVariableOrGetter(tree: Tree) = { def sym = tree.symbol def isVar = sym.isVariable - def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name.toTermName)) != NoSymbol + def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(sym.setterName) != NoSymbol tree match { case Ident(_) => isVar diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala index c00337e578..7467ccc6b9 100644 --- a/src/reflect/scala/reflect/internal/Trees.scala +++ b/src/reflect/scala/reflect/internal/Trees.scala @@ -242,6 +242,9 @@ trait Trees extends api.Trees { self: SymbolTable => trait NameTree extends Tree with NameTreeApi { def name: Name + def getterName: TermName = name.getterName + def setterName: TermName = name.setterName + def localName: TermName = name.localName } trait RefTree extends SymTree with NameTree with RefTreeApi { @@ -1488,6 +1491,11 @@ trait Trees extends api.Trees { self: SymbolTable => /** Substitute symbols in `from` with symbols in `to`. Returns a new * tree using the new symbols and whose Ident and Select nodes are * name-consistent with the new symbols. + * + * Note: This is currently a destructive operation on the original Tree. + * Trees currently assigned a symbol in `from` will be assigned the new symbols + * without copying, and trees that define symbols with an `info` that refer + * a symbol in `from` will have a new type assigned. */ class TreeSymSubstituter(from: List[Symbol], to: List[Symbol]) extends Transformer { val symSubst = new SubstSymMap(from, to) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index d7ff4faa5d..25b05ae6b3 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -871,7 +871,7 @@ trait Types def baseTypeSeqDepth: Int = 1 /** The list of all baseclasses of this type (including its own typeSymbol) - * in reverse linearization order, starting with the class itself and ending + * in linearization order, starting with the class itself and ending * in class Any. */ def baseClasses: List[Symbol] = List() @@ -1306,7 +1306,7 @@ trait Types override def isVolatile = false override def isHigherKinded = sym.isRefinementClass && underlying.isHigherKinded override def prefixString = - if (settings.debug.value) sym.nameString + ".this." + if (settings.debug) sym.nameString + ".this." else if (sym.isAnonOrRefinementClass) "this." else if (sym.isOmittablePrefix) "" else if (sym.isModuleClass) sym.fullNameString + "." @@ -1522,7 +1522,7 @@ trait Types typeSymbol.isAnonOrRefinementClass && (decls exists symbolIsPossibleInRefinement) override def safeToString: String = parentsString(parents) + ( - (if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + (if (settings.debug || parents.isEmpty || (decls.elems ne null)) fullyInitializeScope(decls).mkString("{", "; ", "}") else "") ) } @@ -1622,7 +1622,7 @@ trait Types object baseClassesCycleMonitor { private var open: List[Symbol] = Nil @inline private def cycleLog(msg: => String) { - if (settings.debug.value) + if (settings.debug) Console.err.println(msg) } def size = open.size @@ -1868,7 +1868,7 @@ trait Types tp match { case tr @ TypeRef(_, sym, args) if args.nonEmpty => val tparams = tr.initializedTypeParams - if (settings.debug.value && !sameLength(tparams, args)) + if (settings.debug && !sameLength(tparams, args)) devWarning(s"Mismatched zip in computeRefs(): ${sym.info.typeParams}, $args") foreach2(tparams, args) { (tparam1, arg) => @@ -1934,7 +1934,7 @@ trait Types override def kind = "ClassInfoType" override def safeToString = - if (settings.debug.value || decls.size > 1) + if (settings.debug || decls.size > 1) formattedToString else super.safeToString @@ -1943,7 +1943,7 @@ trait Types */ def formattedToString: String = parents.mkString("\n with ") + ( - if (settings.debug.value || parents.isEmpty || (decls.elems ne null)) + if (settings.debug || parents.isEmpty || (decls.elems ne null)) fullyInitializeScope(decls).mkString(" {\n ", "\n ", "\n}") else "" ) @@ -2352,7 +2352,7 @@ trait Types // ensure that symbol is not a local copy with a name coincidence private def needsPreString = ( - settings.debug.value + settings.debug || !shorthands(sym.fullName) || (sym.ownersIterator exists (s => !s.isClass)) ) @@ -2403,12 +2403,12 @@ trait Types "" } override def safeToString = { - val custom = if (settings.debug.value) "" else customToString + val custom = if (settings.debug) "" else customToString if (custom != "") custom else finishPrefix(preString + sym.nameString + argsString) } override def prefixString = "" + ( - if (settings.debug.value) + if (settings.debug) super.prefixString else if (sym.isOmittablePrefix) "" @@ -2422,20 +2422,28 @@ trait Types override def kind = "TypeRef" } + // No longer defined as anonymous classes in `object TypeRef` to avoid an unnecessary outer pointer. + private final class AliasArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AliasTypeRef + private final class AbstractArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AbstractTypeRef + private final class ClassArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with ClassTypeRef + private final class AliasNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AliasTypeRef + private final class AbstractNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AbstractTypeRef + private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with ClassTypeRef + object TypeRef extends TypeRefExtractor { def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({ if (args.nonEmpty) { - if (sym.isAliasType) new ArgsTypeRef(pre, sym, args) with AliasTypeRef - else if (sym.isAbstractType) new ArgsTypeRef(pre, sym, args) with AbstractTypeRef - else new ArgsTypeRef(pre, sym, args) with ClassTypeRef + if (sym.isAliasType) new AliasArgsTypeRef(pre, sym, args) + else if (sym.isAbstractType) new AbstractArgsTypeRef(pre, sym, args) + else new ClassArgsTypeRef(pre, sym, args) } else { - if (sym.isAliasType) new NoArgsTypeRef(pre, sym) with AliasTypeRef - else if (sym.isAbstractType) new NoArgsTypeRef(pre, sym) with AbstractTypeRef + if (sym.isAliasType) new AliasNoArgsTypeRef(pre, sym) + else if (sym.isAbstractType) new AbstractNoArgsTypeRef(pre, sym) else if (sym.isRefinementClass) new RefinementTypeRef(pre, sym) else if (sym.isPackageClass) new PackageTypeRef(pre, sym) else if (sym.isModuleClass) new ModuleTypeRef(pre, sym) - else new NoArgsTypeRef(pre, sym) with ClassTypeRef + else new ClassNoArgsTypeRef(pre, sym) } }) } @@ -2714,10 +2722,10 @@ trait Types override def safeToString: String = { def clauses = { val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") - if (settings.explaintypes.value) "(" + str + ")" else str + if (settings.explaintypes) "(" + str + ")" else str } underlying match { - case TypeRef(pre, sym, args) if !settings.debug.value && isRepresentableWithWildcards => + case TypeRef(pre, sym, args) if !settings.debug && isRepresentableWithWildcards => "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => "(" + underlying + ")" + clauses @@ -3220,7 +3228,7 @@ trait Types if (sym.owner.isTerm && (sym.owner != encl)) Some(sym.owner) else None ).flatten map (s => s.decodedName + tparamsOfSym(s)) mkString "#" } - private def levelString = if (settings.explaintypes.value) level else "" + private def levelString = if (settings.explaintypes) level else "" override def safeToString = ( if ((constr eq null) || (constr.inst eq null)) "TVar<" + originName + "=null>" else if (constr.inst ne NoType) "=?" + constr.inst @@ -3752,10 +3760,11 @@ trait Types if (tp.isTrivial) tp else if (tp.prefix.typeSymbol isNonBottomSubClass owner) { val widened = tp match { - case _: ConstantType => tp // Java enum constants: don't widen to the enum type! - case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect. + case _: ConstantType => tp // Java enum constants: don't widen to the enum type! + case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect. } - widened asSeenFrom (pre, tp.typeSymbol.owner) + val memType = widened asSeenFrom (pre, tp.typeSymbol.owner) + if (tp eq widened) memType else memType.narrow } else loop(tp.prefix) memberType tp.typeSymbol @@ -3783,7 +3792,7 @@ trait Types * the maximum depth `bd` of all types in the base type sequences of these types. */ private def lubDepthAdjust(td: Int, bd: Int): Int = - if (settings.XfullLubs.value) bd + if (settings.XfullLubs) bd else if (bd <= 3) bd else if (bd <= 5) td max (bd - 1) else if (bd <= 7) td max (bd - 2) @@ -3875,9 +3884,14 @@ trait Types } } - def normalizePlus(tp: Type) = + def normalizePlus(tp: Type) = ( if (isRawType(tp)) rawToExistential(tp) - else tp.normalize + else tp.normalize match { + // Unify the two representations of module classes + case st @ SingleType(_, sym) if sym.isModule => st.underlying.normalize + case _ => tp.normalize + } + ) /* todo: change to: @@ -4399,7 +4413,7 @@ trait Types /** An exception for cyclic references from which we can recover */ case class RecoverableCyclicReference(sym: Symbol) extends TypeError("illegal cyclic reference involving " + sym) { - if (settings.debug.value) printStackTrace() + if (settings.debug) printStackTrace() } class NoCommonType(tps: List[Type]) extends Throwable( @@ -4425,12 +4439,12 @@ trait Types /** If option `explaintypes` is set, print a subtype trace for `found <:< required`. */ def explainTypes(found: Type, required: Type) { - if (settings.explaintypes.value) withTypesExplained(found <:< required) + if (settings.explaintypes) withTypesExplained(found <:< required) } /** If option `explaintypes` is set, print a subtype trace for `op(found, required)`. */ def explainTypes(op: (Type, Type) => Any, found: Type, required: Type) { - if (settings.explaintypes.value) withTypesExplained(op(found, required)) + if (settings.explaintypes) withTypesExplained(op(found, required)) } /** Execute `op` while printing a trace of the operations on types executed. */ diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index e7a1ea9311..68f9fc8e83 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -49,3 +49,8 @@ abstract class MutableSettings extends AbsSettings { def XfullLubs: BooleanSetting def breakCycles: BooleanSetting } +object MutableSettings { + import scala.language.implicitConversions + /** Support the common use case, `if (settings.debug) println("Hello, martin.")` */ + @inline implicit def reflectSettingToBoolean(s: MutableSettings#BooleanSetting): Boolean = s.value +} diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala index 5bdc5f8a73..921d2e3d66 100644 --- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -366,7 +366,7 @@ private[internal] trait GlbLubs { // parameters are not handled correctly. val ok = ts forall { t => isSubType(t, lubRefined, depth) || { - if (settings.debug.value || printLubs) { + if (settings.debug || printLubs) { Console.println( "Malformed lub: " + lubRefined + "\n" + "Argument " + t + " does not conform. Falling back to " + lubBase diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index e9d3ffbf56..da8e64ea16 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -52,14 +52,17 @@ trait TypeComparers { private def isSubPre(pre1: Type, pre2: Type, sym: Symbol) = if ((pre1 ne pre2) && (pre1 ne NoPrefix) && (pre2 ne NoPrefix) && pre1 <:< pre2) { - if (settings.debug.value) println(s"new isSubPre $sym: $pre1 <:< $pre2") + if (settings.debug) println(s"new isSubPre $sym: $pre1 <:< $pre2") true } else false - private def equalSymsAndPrefixes(sym1: Symbol, pre1: Type, sym2: Symbol, pre2: Type): Boolean = - if (sym1 == sym2) sym1.hasPackageFlag || sym1.owner.hasPackageFlag || phase.erasedTypes || pre1 =:= pre2 - else (sym1.name == sym2.name) && isUnifiable(pre1, pre2) + private def equalSymsAndPrefixes(sym1: Symbol, pre1: Type, sym2: Symbol, pre2: Type): Boolean = ( + if (sym1 == sym2) + sym1.hasPackageFlag || sym1.owner.hasPackageFlag || phase.erasedTypes || pre1 =:= pre2 + else + (sym1.name == sym2.name) && isUnifiable(pre1, pre2) + ) def isDifferentType(tp1: Type, tp2: Type): Boolean = try { @@ -126,7 +129,13 @@ trait TypeComparers { tp2.typeSymbol.isPackageClass else if (tp2 eq NoPrefix) // !! I do not see how this would be warranted by the spec tp1.typeSymbol.isPackageClass + else if (tp1.isInstanceOf[AnnotatedType] || tp2.isInstanceOf[AnnotatedType]) + annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && (tp1.withoutAnnotations =:= tp2.withoutAnnotations) else { + // We flush out any AnnotatedTypes before calling isSameType2 because + // unlike most other subclasses of Type, we have to allow for equivalence of any + // combination of { tp1, tp2 } { is, is not } an AnnotatedType - this because the + // logic of "annotationsConform" is arbitrary and unknown. isSameType2(tp1, tp2) || { val tp1n = normalizePlus(tp1) val tp2n = normalizePlus(tp2) @@ -135,165 +144,99 @@ trait TypeComparers { } } - def isSameType2(tp1: Type, tp2: Type): Boolean = { - tp1 match { - case tr1: TypeRef => - tp2 match { - case tr2: TypeRef => - return (equalSymsAndPrefixes(tr1.sym, tr1.pre, tr2.sym, tr2.pre) && - ((tp1.isHigherKinded && tp2.isHigherKinded && tp1.normalize =:= tp2.normalize) || - isSameTypes(tr1.args, tr2.args))) || - ((tr1.pre, tr2.pre) match { - case (tv @ TypeVar(_,_), _) => tv.registerTypeSelection(tr1.sym, tr2) - case (_, tv @ TypeVar(_,_)) => tv.registerTypeSelection(tr2.sym, tr1) - case _ => false - }) - case _: SingleType => - return isSameType2(tp2, tp1) // put singleton type on the left, caught below - case _ => - } - case tt1: ThisType => - tp2 match { - case tt2: ThisType => - if (tt1.sym == tt2.sym) return true - case _ => - } - case st1: SingleType => - tp2 match { - case st2: SingleType => - if (equalSymsAndPrefixes(st1.sym, st1.pre, st2.sym, st2.pre)) return true - case TypeRef(pre2, sym2, Nil) => - if (sym2.isModuleClass && equalSymsAndPrefixes(st1.sym, st1.pre, sym2.sourceModule, pre2)) return true - case _ => - } - case ct1: ConstantType => - tp2 match { - case ct2: ConstantType => - return (ct1.value == ct2.value) - case _ => - } - case rt1: RefinedType => - tp2 match { - case rt2: RefinedType => // - def isSubScope(s1: Scope, s2: Scope): Boolean = s2.toList.forall { - sym2 => - var e1 = s1.lookupEntry(sym2.name) - (e1 ne null) && { - val substSym = sym2.info.substThis(sym2.owner, e1.sym.owner) - var isEqual = false - while (!isEqual && (e1 ne null)) { - isEqual = e1.sym.info =:= substSym - e1 = s1.lookupNextEntry(e1) - } - isEqual - } - } - //Console.println("is same? " + tp1 + " " + tp2 + " " + tp1.typeSymbol.owner + " " + tp2.typeSymbol.owner)//DEBUG - return isSameTypes(rt1.parents, rt2.parents) && { - val decls1 = rt1.decls - val decls2 = rt2.decls - isSubScope(decls1, decls2) && isSubScope(decls2, decls1) - } - case _ => - } - case mt1: MethodType => - tp2 match { - case mt2: MethodType => - return isSameTypes(mt1.paramTypes, mt2.paramTypes) && - mt1.resultType =:= mt2.resultType.substSym(mt2.params, mt1.params) && - mt1.isImplicit == mt2.isImplicit - // note: no case NullaryMethodType(restpe) => return mt1.params.isEmpty && mt1.resultType =:= restpe - case _ => - } - case NullaryMethodType(restpe1) => - tp2 match { - // note: no case mt2: MethodType => return mt2.params.isEmpty && restpe =:= mt2.resultType - case NullaryMethodType(restpe2) => - return restpe1 =:= restpe2 - case _ => - } - case PolyType(tparams1, res1) => - tp2 match { - case PolyType(tparams2, res2) => - // assert((tparams1 map (_.typeParams.length)) == (tparams2 map (_.typeParams.length))) - // @M looks like it might suffer from same problem as #2210 - return ( - (sameLength(tparams1, tparams2)) && // corresponds does not check length of two sequences before checking the predicate - (tparams1 corresponds tparams2)(_.info =:= _.info.substSym(tparams2, tparams1)) && - res1 =:= res2.substSym(tparams2, tparams1) - ) - case _ => - } - case ExistentialType(tparams1, res1) => - tp2 match { - case ExistentialType(tparams2, res2) => - // @M looks like it might suffer from same problem as #2210 - return ( - // corresponds does not check length of two sequences before checking the predicate -- faster & needed to avoid crasher in #2956 - sameLength(tparams1, tparams2) && - (tparams1 corresponds tparams2)(_.info =:= _.info.substSym(tparams2, tparams1)) && - res1 =:= res2.substSym(tparams2, tparams1) - ) - case _ => - } - case TypeBounds(lo1, hi1) => - tp2 match { - case TypeBounds(lo2, hi2) => - return lo1 =:= lo2 && hi1 =:= hi2 - case _ => - } - case BoundedWildcardType(bounds) => - return bounds containsType tp2 - case _ => - } - tp2 match { - case BoundedWildcardType(bounds) => - return bounds containsType tp1 - case _ => - } - tp1 match { - case tv @ TypeVar(_,_) => - return tv.registerTypeEquality(tp2, typeVarLHS = true) - case _ => - } - tp2 match { - case tv @ TypeVar(_,_) => - return tv.registerTypeEquality(tp1, typeVarLHS = false) - case _ => + private def isSameHKTypes(tp1: Type, tp2: Type) = ( + tp1.isHigherKinded + && tp2.isHigherKinded + && (tp1.normalize =:= tp2.normalize) + ) + private def isSameTypeRef(tr1: TypeRef, tr2: TypeRef) = ( + equalSymsAndPrefixes(tr1.sym, tr1.pre, tr2.sym, tr2.pre) + && (isSameHKTypes(tr1, tr2) || isSameTypes(tr1.args, tr2.args)) + ) + + private def isSameSingletonType(tp1: SingletonType, tp2: SingletonType): Boolean = { + // We don't use dealiasWiden here because we are looking for the SAME type, + // and widening leads to a less specific type. The logic is along the lines of + // dealiasAndFollowUnderlyingAsLongAsTheTypeIsEquivalent. This method is only + // called after a surface comparison has failed, so if chaseDealiasedUnderlying + // does not produce a type other than tp1 and tp2, return false. + @tailrec def chaseDealiasedUnderlying(tp: Type): Type = tp.underlying.dealias match { + case next: SingletonType if tp ne next => chaseDealiasedUnderlying(next) + case _ => tp } - tp1 match { - case _: AnnotatedType => - return annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations - case _ => + val origin1 = chaseDealiasedUnderlying(tp1) + val origin2 = chaseDealiasedUnderlying(tp2) + ((origin1 ne tp1) || (origin2 ne tp2)) && (origin1 =:= origin2) + } + + private def isSameMethodType(mt1: MethodType, mt2: MethodType) = ( + isSameTypes(mt1.paramTypes, mt2.paramTypes) + && (mt1.resultType =:= mt2.resultType.substSym(mt2.params, mt1.params)) + && (mt1.isImplicit == mt2.isImplicit) + ) + + private def equalTypeParamsAndResult(tparams1: List[Symbol], res1: Type, tparams2: List[Symbol], res2: Type) = { + def subst(info: Type) = info.substSym(tparams2, tparams1) + // corresponds does not check length of two sequences before checking the predicate, + // but SubstMap assumes it has been checked (SI-2956) + ( sameLength(tparams1, tparams2) + && (tparams1 corresponds tparams2)((p1, p2) => p1.info =:= subst(p2.info)) + && (res1 =:= subst(res2)) + ) + } + + def isSameType2(tp1: Type, tp2: Type): Boolean = { + /** Here we highlight those unfortunate type-like constructs which + * are hidden bundles of mutable state, cruising the type system picking + * up any type constraints naive enough to get into their hot rods. + */ + def mutateNonTypeConstructs(lhs: Type, rhs: Type) = lhs match { + case BoundedWildcardType(bounds) => bounds containsType rhs + case tv @ TypeVar(_, _) => tv.registerTypeEquality(rhs, typeVarLHS = lhs eq tp1) + case TypeRef(tv @ TypeVar(_, _), sym, _) => tv.registerTypeSelection(sym, rhs) + case _ => false } - tp2 match { - case _: AnnotatedType => - return annotationsConform(tp1, tp2) && annotationsConform(tp2, tp1) && tp1.withoutAnnotations =:= tp2.withoutAnnotations - case _ => + /* SingletonType receives this additional scrutiny because there are + * a variety of Types which must be treated as equivalent even if they + * arrive in different guises. For instance, object Foo in the following + * might appear in (at least) the four given below. + * + * package pkg { object Foo ; type Bar = Foo.type } + * + * ModuleClassTypeRef(pkg.type, Foo: ModuleClassSymbol, Nil) + * ThisType(Foo: ModuleClassSymbol) + * SingleType(pkg.type, Foo: ModuleSymbol) + * AliasTypeRef(NoPrefix, sym: AliasSymbol, Nil) where sym.info is one of the above + */ + def sameSingletonType = tp1 match { + case tp1: SingletonType => tp2 match { + case tp2: SingletonType => isSameSingletonType(tp1, tp2) + case _ => false + } + case _ => false } - tp1 match { - case _: SingletonType => - tp2 match { - case _: SingletonType => - def chaseDealiasedUnderlying(tp: Type): Type = { - var origin = tp - var next = origin.underlying.dealias - while (next.isInstanceOf[SingletonType]) { - assert(origin ne next, origin) - origin = next - next = origin.underlying.dealias - } - origin - } - val origin1 = chaseDealiasedUnderlying(tp1) - val origin2 = chaseDealiasedUnderlying(tp2) - ((origin1 ne tp1) || (origin2 ne tp2)) && (origin1 =:= origin2) - case _ => - false - } - case _ => - false + /** Those false cases certainly are ugly. There's a proposed SIP to deuglify it. + * https://docs.google.com/a/improving.org/document/d/1onPrzSqyDpHScc9PS_hpxJwa3FlPtthxw-bAuuEe8uA + */ + def sameTypeAndSameCaseClass = tp1 match { + case tp1: TypeRef => tp2 match { case tp2: TypeRef => isSameTypeRef(tp1, tp2) ; case _ => false } + case tp1: MethodType => tp2 match { case tp2: MethodType => isSameMethodType(tp1, tp2) ; case _ => false } + case RefinedType(ps1, decls1) => tp2 match { case RefinedType(ps2, decls2) => isSameTypes(ps1, ps2) && (decls1 isSameScope decls2) ; case _ => false } + case SingleType(pre1, sym1) => tp2 match { case SingleType(pre2, sym2) => equalSymsAndPrefixes(sym1, pre1, sym2, pre2) ; case _ => false } + case PolyType(ps1, res1) => tp2 match { case PolyType(ps2, res2) => equalTypeParamsAndResult(ps1, res1, ps2, res2) ; case _ => false } + case ExistentialType(qs1, res1) => tp2 match { case ExistentialType(qs2, res2) => equalTypeParamsAndResult(qs1, res1, qs2, res2) ; case _ => false } + case ThisType(sym1) => tp2 match { case ThisType(sym2) => sym1 == sym2 ; case _ => false } + case ConstantType(c1) => tp2 match { case ConstantType(c2) => c1 == c2 ; case _ => false } + case NullaryMethodType(res1) => tp2 match { case NullaryMethodType(res2) => res1 =:= res2 ; case _ => false } + case TypeBounds(lo1, hi1) => tp2 match { case TypeBounds(lo2, hi2) => lo1 =:= lo2 && hi1 =:= hi2 ; case _ => false } + case _ => false } + + ( sameTypeAndSameCaseClass + || sameSingletonType + || mutateNonTypeConstructs(tp1, tp2) + || mutateNonTypeConstructs(tp2, tp1) + ) } def isSubType(tp1: Type, tp2: Type): Boolean = isSubType(tp1, tp2, AnyDepth) diff --git a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala index a002b01f70..b0feb0a7fb 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeConstraints.scala @@ -66,7 +66,7 @@ private[internal] trait TypeConstraints { def clear() { lock() try { - if (settings.debug.value) + if (settings.debug) self.log("Clearing " + log.size + " entries from the undoLog.") log = Nil } finally unlock() diff --git a/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala index 263b0f5a3e..c86383e9e3 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeToStrings.scala @@ -14,7 +14,7 @@ private[internal] trait TypeToStrings { protected def typeToString(tpe: Type): String = if (tostringRecursions >= maxTostringRecursions) { devWarning("Exceeded recursion depth attempting to print " + util.shortClassOfInstance(tpe)) - if (settings.debug.value) + if (settings.debug) (new Throwable).printStackTrace "..." diff --git a/src/reflect/scala/reflect/internal/transform/UnCurry.scala b/src/reflect/scala/reflect/internal/transform/UnCurry.scala index 32d3171b26..1f7638a621 100644 --- a/src/reflect/scala/reflect/internal/transform/UnCurry.scala +++ b/src/reflect/scala/reflect/internal/transform/UnCurry.scala @@ -25,7 +25,14 @@ trait UnCurry { val tp = expandAlias(tp0) tp match { case MethodType(params, MethodType(params1, restpe)) => - apply(MethodType(params ::: params1, restpe)) + // This transformation is described in UnCurryTransformer.dependentParamTypeErasure + val packSymbolsMap = new TypeMap { + // Wrapping in a TypeMap to reuse the code that opts for a fast path if the function is an identity. + def apply(tp: Type): Type = packSymbols(params, tp) + } + val existentiallyAbstractedParam1s = packSymbolsMap.mapOver(params1) + val substitutedResult = restpe.substSym(params1, existentiallyAbstractedParam1s) + apply(MethodType(params ::: existentiallyAbstractedParam1s, substitutedResult)) case MethodType(params, ExistentialType(tparams, restpe @ MethodType(_, _))) => abort("unexpected curried method types with intervening existential") case MethodType(h :: t, restpe) if h.isImplicit => diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index 63b7f73386..d6fca9d186 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -76,6 +76,19 @@ trait Collections { lb.toList } + final def distinctBy[A, B](xs: List[A])(f: A => B): List[A] = { + val buf = new ListBuffer[A] + val seen = mutable.Set[B]() + xs foreach { x => + val y = f(x) + if (!seen(y)) { + buf += x + seen += y + } + } + buf.toList + } + @tailrec final def flattensToEmpty(xss: Seq[Seq[_]]): Boolean = { xss.isEmpty || xss.head.isEmpty && flattensToEmpty(xss.tail) } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index 2e38caaf5d..3211bb7919 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -6,12 +6,14 @@ import scala.collection.mutable.WeakHashMap import java.lang.{Class => jClass, Package => jPackage} import java.lang.reflect.{ - Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField, + Method => jMethod, Constructor => jConstructor, Field => jField, Member => jMember, Type => jType, TypeVariable => jTypeVariable, Array => jArray, + AccessibleObject => jAccessibleObject, GenericDeclaration, GenericArrayType, ParameterizedType, WildcardType, AnnotatedElement } import java.lang.annotation.{Annotation => jAnnotation} import java.io.IOException -import internal.MissingRequirementError +import scala.reflect.internal.{ MissingRequirementError, JavaAccFlags, JMethodOrConstructor } +import JavaAccFlags._ import internal.pickling.ByteCodecs import internal.ClassfileConstants._ import internal.pickling.UnPickler @@ -88,12 +90,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // ----------- Caching ------------------------------------------------------------------ - private val classCache = new TwoWayCache[jClass[_], ClassSymbol] - private val packageCache = new TwoWayCache[Package, ModuleSymbol] - private val methodCache = new TwoWayCache[jMethod, MethodSymbol] + private val classCache = new TwoWayCache[jClass[_], ClassSymbol] + private val packageCache = new TwoWayCache[Package, ModuleSymbol] + private val methodCache = new TwoWayCache[jMethod, MethodSymbol] private val constructorCache = new TwoWayCache[jConstructor[_], MethodSymbol] - private val fieldCache = new TwoWayCache[jField, TermSymbol] - private val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol] + private val fieldCache = new TwoWayCache[jField, TermSymbol] + private val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol] private[runtime] def toScala[J: HasJavaClass, S](cache: TwoWayCache[J, S], key: J)(body: (JavaMirror, J) => S): S = cache.toScala(key){ @@ -101,38 +103,36 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni body(mirrorDefining(jclazz), key) } - private implicit val classHasJavaClass: HasJavaClass[jClass[_]] = - new HasJavaClass(identity) - private implicit val methHasJavaClass: HasJavaClass[jMethod] - = new HasJavaClass(_.getDeclaringClass) - private implicit val fieldHasJavaClass: HasJavaClass[jField] = - new HasJavaClass(_.getDeclaringClass) - private implicit val constrHasJavaClass: HasJavaClass[jConstructor[_]] = - new HasJavaClass(_.getDeclaringClass) + private implicit val classHasJavaClass: HasJavaClass[jClass[_]] = new HasJavaClass(identity) + private implicit val methHasJavaClass: HasJavaClass[jMethod] = new HasJavaClass(_.getDeclaringClass) + private implicit val fieldHasJavaClass: HasJavaClass[jField] = new HasJavaClass(_.getDeclaringClass) + private implicit val constrHasJavaClass: HasJavaClass[jConstructor[_]] = new HasJavaClass(_.getDeclaringClass) private implicit val tparamHasJavaClass: HasJavaClass[jTypeVariable[_ <: GenericDeclaration]] = new HasJavaClass ( (tparam: jTypeVariable[_ <: GenericDeclaration]) => { tparam.getGenericDeclaration match { - case jclazz: jClass[_] => jclazz - case jmeth: jMethod => jmeth.getDeclaringClass + case jclazz: jClass[_] => jclazz + case jmeth: jMethod => jmeth.getDeclaringClass case jconstr: jConstructor[_] => jconstr.getDeclaringClass } }) // ----------- Implementations of mirror operations and classes ------------------- - private def ErrorInnerClass(sym: Symbol) = throw new ScalaReflectionException(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") - private def ErrorInnerModule(sym: Symbol) = throw new ScalaReflectionException(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") - private def ErrorStaticClass(sym: Symbol) = throw new ScalaReflectionException(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") - private def ErrorStaticModule(sym: Symbol) = throw new ScalaReflectionException(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") - private def ErrorNotMember(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") - private def ErrorNotField(sym: Symbol) = throw new ScalaReflectionException(s"expected a field or an accessor method symbol, you provided $sym") - private def ErrorNonExistentField(sym: Symbol) = throw new ScalaReflectionException( + private def abort(msg: String) = throw new ScalaReflectionException(msg) + + private def ErrorInnerClass(sym: Symbol) = abort(s"$sym is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror") + private def ErrorInnerModule(sym: Symbol) = abort(s"$sym is an inner module, use reflectModule on an InstanceMirror to obtain its ModuleMirror") + private def ErrorStaticClass(sym: Symbol) = abort(s"$sym is a static class, use reflectClass on a RuntimeMirror to obtain its ClassMirror") + private def ErrorStaticModule(sym: Symbol) = abort(s"$sym is a static module, use reflectModule on a RuntimeMirror to obtain its ModuleMirror") + private def ErrorNotMember(sym: Symbol, owner: Symbol) = abort(s"expected a member of $owner, you provided ${sym.kindString} ${sym.fullName}") + private def ErrorNotField(sym: Symbol) = abort(s"expected a field or an accessor method symbol, you provided $sym") + private def ErrorSetImmutableField(sym: Symbol) = abort(s"cannot set an immutable field ${sym.name}") + private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = abort(s"expected a constructor of $owner, you provided $sym") + private def ErrorFree(member: Symbol, freeType: Symbol) = abort(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") + private def ErrorNonExistentField(sym: Symbol) = abort( sm"""Scala field ${sym.name} isn't represented as a Java field, neither it has a Java accessor method |note that private parameters of class constructors don't get mapped onto fields and/or accessors, |unless they are used outside of their declaring constructors.""") - private def ErrorSetImmutableField(sym: Symbol) = throw new ScalaReflectionException(s"cannot set an immutable field ${sym.name}") - private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a constructor of $owner, you provided $sym") - private def ErrorFree(member: Symbol, freeType: Symbol) = throw new ScalaReflectionException(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") /** Helper functions for extracting typed values from a (Class[_], Any) * representing an annotation argument. @@ -210,7 +210,6 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case _ => body } } - private def checkMemberOf(sym: Symbol, owner: ClassSymbol) { if (sym.owner == AnyClass || sym.owner == AnyRefClass || sym.owner == ObjectClass) { // do nothing @@ -236,16 +235,12 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (staticClazz.isPrimitive) staticClazz else dynamicClazz } - private class JavaInstanceMirror[T: ClassTag](val instance: T) - extends InstanceMirror { + private class JavaInstanceMirror[T: ClassTag](val instance: T) extends InstanceMirror { def symbol = thisMirror.classSymbol(preciseClass(instance)) def reflectField(field: TermSymbol): FieldMirror = { checkMemberOf(field, symbol) if ((field.isMethod && !field.isAccessor) || field.isModule) ErrorNotField(field) - val name = - if (field.isGetter) nme.getterToLocal(field.name) - else if (field.isSetter) nme.getterToLocal(nme.setterToGetter(field.name)) - else field.name + val name = if (field.isAccessor) field.localName else field.name val field1 = (field.owner.info decl name).asTerm try fieldToJava(field1) catch { @@ -272,12 +267,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private class JavaFieldMirror(val receiver: Any, val symbol: TermSymbol) extends FieldMirror { - lazy val jfield = { - val jfield = fieldToJava(symbol) - if (!jfield.isAccessible) jfield.setAccessible(true) - jfield - } - def get = jfield.get(receiver) + lazy val jfield = ensureAccessible(fieldToJava(symbol)) + def get = jfield get receiver def set(value: Any) = { if (!symbol.isMutable) ErrorSetImmutableField(symbol) jfield.set(receiver, value) @@ -313,7 +304,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni // the "symbol == Any_getClass || symbol == Object_getClass" test doesn't cut it // because both AnyVal and its primitive descendants define their own getClass methods - private def isGetClass(meth: MethodSymbol) = meth.name.toString == "getClass" && meth.paramss.flatten.isEmpty + private def isGetClass(meth: MethodSymbol) = (meth.name string_== "getClass") && meth.paramss.flatten.isEmpty private def isStringConcat(meth: MethodSymbol) = meth == String_+ || (meth.owner.isPrimitiveValueClass && meth.returnType =:= StringClass.toType) lazy val bytecodelessMethodOwners = Set[Symbol](AnyClass, AnyValClass, AnyRefClass, ObjectClass, ArrayClass) ++ ScalaPrimitiveValueClasses lazy val bytecodefulObjectMethods = Set[Symbol](Object_clone, Object_equals, Object_finalize, Object_hashCode, Object_toString, @@ -341,15 +332,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } } } - - private abstract class JavaMethodMirror(val symbol: MethodSymbol) - extends MethodMirror { - lazy val jmeth = { - val jmeth = methodToJava(symbol) - if (!jmeth.isAccessible) jmeth.setAccessible(true) - jmeth - } - + private abstract class JavaMethodMirror(val symbol: MethodSymbol) extends MethodMirror { + lazy val jmeth = ensureAccessible(methodToJava(symbol)) def jinvokeraw(jmeth: jMethod, receiver: Any, args: Seq[Any]) = jmeth.invoke(receiver, args.asInstanceOf[Seq[AnyRef]]: _*) def jinvoke(jmeth: jMethod, receiver: Any, args: Seq[Any]): Any = { @@ -421,13 +405,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (!perfectMatch && !varargMatch) { val n_arguments = if (isVarArgsList(params)) s"${params.length - 1} or more" else s"${params.length}" val s_arguments = if (params.length == 1 && !isVarArgsList(params)) "argument" else "arguments" - throw new ScalaReflectionException(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments") + abort(s"${showMethodSig(symbol)} takes $n_arguments $s_arguments") } def objReceiver = receiver.asInstanceOf[AnyRef] def objArg0 = args(0).asInstanceOf[AnyRef] def objArgs = args.asInstanceOf[Seq[AnyRef]] - def fail(msg: String) = throw new ScalaReflectionException(msg + ", it cannot be invoked with mirrors") + def fail(msg: String) = abort(msg + ", it cannot be invoked with mirrors") def invokePrimitiveMethod = { val jmeths = classOf[BoxesRunTime].getDeclaredMethods.filter(_.getName == nme.primitiveMethodName(symbol.name).toString) @@ -467,14 +451,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni extends MethodMirror { def bind(newReceiver: Any) = new JavaConstructorMirror(newReceiver.asInstanceOf[AnyRef], symbol) override val receiver = outer - lazy val jconstr = { - val jconstr = constructorToJava(symbol) - if (!jconstr.isAccessible) jconstr.setAccessible(true) - jconstr - } + lazy val jconstr = ensureAccessible(constructorToJava(symbol)) def apply(args: Any*): Any = { if (symbol.owner == ArrayClass) - throw new ScalaReflectionException("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead") + abort("Cannot instantiate arrays with mirrors. Consider using `scala.reflect.ClassTag(<class of element>).newArray(<length>)` instead") val effectiveArgs = if (outer == null) args.asInstanceOf[Seq[AnyRef]] @@ -535,7 +515,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni } def javaClass(path: String): jClass[_] = - Class.forName(path, true, classLoader) + jClass.forName(path, true, classLoader) /** Does `path` correspond to a Java class with that fully qualified name in the current class loader? */ def tryJavaClass(path: String): Option[jClass[_]] = ( @@ -565,7 +545,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def markAbsent(tpe: Type) = setAllInfos(clazz, module, tpe) def handleError(ex: Exception) = { markAbsent(ErrorType) - if (settings.debug.value) ex.printStackTrace() + if (settings.debug) ex.printStackTrace() val msg = ex.getMessage() MissingRequirementError.signal( (if (msg eq null) "reflection error while loading " + clazz.name @@ -658,13 +638,22 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni sym setAnnotations (jann.getAnnotations map JavaAnnotationProxy).toList // SI-7065: we're not using getGenericExceptionTypes here to be consistent with ClassfileParser val jexTpes = jann match { - case jm: jMethod => jm.getExceptionTypes.toList + case jm: jMethod => jm.getExceptionTypes.toList case jconstr: jConstructor[_] => jconstr.getExceptionTypes.toList - case _ => Nil + case _ => Nil } jexTpes foreach (jexTpe => sym.addThrowsAnnotation(classSymbol(jexTpe))) } + private implicit class jClassOps(val clazz: jClass[_]) { + def javaFlags: JavaAccFlags = JavaAccFlags(clazz) + def scalaFlags: Long = javaFlags.toScalaFlags + } + private implicit class jMemberOps(val member: jMember) { + def javaFlags: JavaAccFlags = JavaAccFlags(member) + def scalaFlags: Long = javaFlags.toScalaFlags + } + /** * A completer that fills in the types of a Scala class and its companion object * by copying corresponding type info from a Java class. This completer is used @@ -684,14 +673,14 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni override def load(sym: Symbol): Unit = { debugInfo("completing from Java " + sym + "/" + clazz.fullName)//debug assert(sym == clazz || (module != NoSymbol && (sym == module || sym == module.moduleClass)), sym) - val flags = toScalaClassFlags(jclazz.getModifiers) + val flags = jclazz.scalaFlags clazz setFlag (flags | JAVA) if (module != NoSymbol) { module setFlag (flags & PRIVATE | JAVA) module.moduleClass setFlag (flags & PRIVATE | JAVA) } - relatedSymbols foreach (importPrivateWithinFromJavaFlags(_, jclazz.getModifiers)) + propagatePackageBoundary(jclazz, relatedSymbols: _*) copyAnnotations(clazz, jclazz) // to do: annotations to set also for module? @@ -723,28 +712,21 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni module.moduleClass setInfo new ClassInfoType(List(), newScope, module.moduleClass) } - def enter(sym: Symbol, mods: Int) = - (if (jModifier.isStatic(mods)) module.moduleClass else clazz).info.decls enter sym + def enter(sym: Symbol, mods: JavaAccFlags) = + ( if (mods.isStatic) module.moduleClass else clazz ).info.decls enter sym for (jinner <- jclazz.getDeclaredClasses) jclassAsScala(jinner) // inner class is entered as a side-effect // no need to call enter explicitly - pendingLoadActions = { () => - - for (jfield <- jclazz.getDeclaredFields) - enter(jfieldAsScala(jfield), jfield.getModifiers) - - for (jmeth <- jclazz.getDeclaredMethods) - enter(jmethodAsScala(jmeth), jmeth.getModifiers) - - for (jconstr <- jclazz.getConstructors) - enter(jconstrAsScala(jconstr), jconstr.getModifiers) - - } :: pendingLoadActions + pendingLoadActions ::= { () => + jclazz.getDeclaredFields foreach (f => enter(jfieldAsScala(f), f.javaFlags)) + jclazz.getDeclaredMethods foreach (m => enter(jmethodAsScala(m), m.javaFlags)) + jclazz.getConstructors foreach (c => enter(jconstrAsScala(c), c.javaFlags)) + } if (parentsLevel == 0) { - while (!pendingLoadActions.isEmpty) { + while (pendingLoadActions.nonEmpty) { val item = pendingLoadActions.head pendingLoadActions = pendingLoadActions.tail item() @@ -763,8 +745,8 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * If Java modifiers `mods` contain STATIC, return the module class * of the companion module of `clazz`, otherwise the class `clazz` itself. */ - private def followStatic(clazz: Symbol, mods: Int) = - if (jModifier.isStatic(mods)) clazz.companionModule.moduleClass else clazz + private def followStatic(clazz: Symbol, mods: JavaAccFlags) = + if (mods.isStatic) clazz.companionModule.moduleClass else clazz /** Methods which need to be treated with care * because they either are getSimpleName or call getSimpleName: @@ -800,7 +782,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni if (jclazz.isMemberClass) { val jEnclosingClass = jclazz.getEnclosingClass val sEnclosingClass = classToScala(jEnclosingClass) - followStatic(sEnclosingClass, jclazz.getModifiers) + followStatic(sEnclosingClass, jclazz.javaFlags) } else if (jclazz.isLocalClass0) { val jEnclosingMethod = jclazz.getEnclosingMethod if (jEnclosingMethod != null) { @@ -828,7 +810,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * The Scala owner of the Scala symbol corresponding to the Java member `jmember` */ private def sOwner(jmember: jMember): Symbol = { - followStatic(classToScala(jmember.getDeclaringClass), jmember.getModifiers) + followStatic(classToScala(jmember.getDeclaringClass), jmember.javaFlags) } /** @@ -844,9 +826,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * that start with the given name are searched instead. */ private def lookup(clazz: Symbol, jname: String): Symbol = { - def approximateMatch(sym: Symbol, jstr: String): Boolean = - (sym.name.toString == jstr) || - sym.isPrivate && nme.expandedName(sym.name.toTermName, sym.owner).toString == jstr + def approximateMatch(sym: Symbol, jstr: String): Boolean = ( + (sym.name string_== jstr) + || sym.isPrivate && (nme.expandedName(sym.name.toTermName, sym.owner) string_== jstr) + ) clazz.info.decl(newTermName(jname)) orElse { (clazz.info.decls.iterator filter (approximateMatch(_, jname))).toList match { @@ -868,7 +851,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def methodToScala1(jmeth: jMethod): MethodSymbol = { val jOwner = jmeth.getDeclaringClass val preOwner = classToScala(jOwner) - val owner = followStatic(preOwner, jmeth.getModifiers) + val owner = followStatic(preOwner, jmeth.javaFlags) (lookup(owner, jmeth.getName) suchThat (erasesTo(_, jmeth)) orElse jmethodAsScala(jmeth)) .asMethod } @@ -882,7 +865,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni toScala(constructorCache, jconstr)(_ constructorToScala1 _) private def constructorToScala1(jconstr: jConstructor[_]): MethodSymbol = { - val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.getModifiers) + val owner = followStatic(classToScala(jconstr.getDeclaringClass), jconstr.javaFlags) (lookup(owner, jconstr.getName) suchThat (erasesTo(_, jconstr)) orElse jconstrAsScala(jconstr)) .asMethod } @@ -1008,7 +991,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def typeParamToScala1(jparam: jTypeVariable[_ <: GenericDeclaration]): TypeSymbol = { val owner = genericDeclarationToScala(jparam.getGenericDeclaration) owner.info match { - case PolyType(tparams, _) => tparams.find(_.name.toString == jparam.getName).get.asType + case PolyType(tparams, _) => tparams.find(_.name string_== jparam.getName).get.asType } } @@ -1020,6 +1003,10 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni case jmeth: jMethod => methodToScala(jmeth) case jconstr: jConstructor[_] => constructorToScala(jconstr) } + def reflectMemberToScala(m: jMember): Symbol = m match { + case x: GenericDeclaration => genericDeclarationToScala(x) + case x: jField => jfieldAsScala(x) + } /** * Given some Java type arguments, a corresponding list of Scala types, plus potentially @@ -1094,10 +1081,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jfieldAsScala1(jfield: jField): TermSymbol = { val field = sOwner(jfield) - .newValue(newTermName(jfield.getName), NoPosition, toScalaFieldFlags(jfield.getModifiers)) + .newValue(newTermName(jfield.getName), NoPosition, jfield.scalaFlags) .setInfo(typeToScala(jfield.getGenericType)) - fieldCache enter (jfield, field) - importPrivateWithinFromJavaFlags(field, jfield.getModifiers) + + fieldCache.enter(jfield, field) + propagatePackageBoundary(jfield, field) copyAnnotations(field, jfield) field } @@ -1117,16 +1105,19 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jmethodAsScala1(jmeth: jMethod): MethodSymbol = { val clazz = sOwner(jmeth) - val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, toScalaMethodFlags(jmeth.getModifiers)) + val meth = clazz.newMethod(newTermName(jmeth.getName), NoPosition, jmeth.scalaFlags) methodCache enter (jmeth, meth) val tparams = jmeth.getTypeParameters.toList map createTypeParameter val paramtpes = jmeth.getGenericParameterTypes.toList map typeToScala val resulttpe = typeToScala(jmeth.getGenericReturnType) setMethType(meth, tparams, paramtpes, resulttpe) - importPrivateWithinFromJavaFlags(meth, jmeth.getModifiers) + propagatePackageBoundary(jmeth.javaFlags, meth) copyAnnotations(meth, jmeth) - if ((jmeth.getModifiers & JAVA_ACC_VARARGS) != 0) meth.setInfo(arrayToRepeated(meth.info)) - meth + + if (jmeth.javaFlags.isVarargs) + meth modifyInfo arrayToRepeated + else + meth } /** @@ -1141,13 +1132,13 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni private def jconstrAsScala1(jconstr: jConstructor[_]): MethodSymbol = { // [Martin] Note: I know there's a lot of duplication wrt jmethodAsScala, but don't think it's worth it to factor this out. val clazz = sOwner(jconstr) - val constr = clazz.newConstructor(NoPosition, toScalaMethodFlags(jconstr.getModifiers)) + val constr = clazz.newConstructor(NoPosition, jconstr.scalaFlags) constructorCache enter (jconstr, constr) val tparams = jconstr.getTypeParameters.toList map createTypeParameter val paramtpes = jconstr.getGenericParameterTypes.toList map typeToScala setMethType(constr, tparams, paramtpes, clazz.tpe_*) constr setInfo GenPolyType(tparams, MethodType(clazz.newSyntheticValueParams(paramtpes), clazz.tpe)) - importPrivateWithinFromJavaFlags(constr, jconstr.getModifiers) + propagatePackageBoundary(jconstr.javaFlags, constr) copyAnnotations(constr, jconstr) constr } @@ -1172,13 +1163,15 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni else if (clazz.isTopLevel) javaClass(clazz.javaClassName) else if (clazz.owner.isClass) { - val childOfClass = !clazz.owner.isModuleClass - val childOfTopLevel = clazz.owner.isTopLevel + val childOfClass = !clazz.owner.isModuleClass + val childOfTopLevel = clazz.owner.isTopLevel val childOfTopLevelObject = clazz.owner.isModuleClass && childOfTopLevel // suggested in https://issues.scala-lang.org/browse/SI-4023?focusedCommentId=54759#comment-54759 var ownerClazz = classToJava(clazz.owner.asClass) - if (childOfTopLevelObject) ownerClazz = Class.forName(ownerClazz.getName stripSuffix "$", true, ownerClazz.getClassLoader) + if (childOfTopLevelObject) + ownerClazz = jClass.forName(ownerClazz.getName stripSuffix "$", true, ownerClazz.getClassLoader) + val ownerChildren = ownerClazz.getDeclaredClasses var fullNameOfJavaClass = ownerClazz.getName @@ -1202,7 +1195,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni */ def fieldToJava(fld: TermSymbol): jField = fieldCache.toJava(fld) { val jclazz = classToJava(fld.owner.asClass) - val jname = nme.dropLocalSuffix(fld.name).toString + val jname = fld.name.dropLocal.toString try jclazz getDeclaredField jname catch { case ex: NoSuchFieldException => jclazz getDeclaredField expandedName(fld) @@ -1215,7 +1208,7 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni def methodToJava(meth: MethodSymbol): jMethod = methodCache.toJava(meth) { val jclazz = classToJava(meth.owner.asClass) val paramClasses = transformedType(meth).paramTypes map typeToJavaClass - val jname = nme.dropLocalSuffix(meth.name).toString + val jname = meth.name.dropLocal.toString try jclazz getDeclaredMethod (jname, paramClasses: _*) catch { case ex: NoSuchMethodException => @@ -1243,11 +1236,11 @@ private[reflect] trait JavaMirrors extends internal.SymbolTable with api.JavaUni * Pre: Scala type is already transformed to Java level. */ def typeToJavaClass(tpe: Type): jClass[_] = tpe match { - case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) - case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) - case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass) + case ExistentialType(_, rtpe) => typeToJavaClass(rtpe) + case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe)) + case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass) case tpe @ TypeRef(_, sym: AliasTypeSymbol, _) => typeToJavaClass(tpe.dealias) - case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") + case _ => throw new NoClassDefFoundError("no Java class corresponding to "+tpe+" found") } } diff --git a/src/reflect/scala/reflect/runtime/JavaUniverse.scala b/src/reflect/scala/reflect/runtime/JavaUniverse.scala index a130013398..4d90afcdc3 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverse.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverse.scala @@ -14,7 +14,7 @@ class JavaUniverse extends internal.SymbolTable with ReflectSetup with runtime.S lazy val settings = new Settings private val isLogging = sys.props contains "scala.debug.reflect" - def log(msg: => AnyRef): Unit = if (isLogging) Console.err.println("[reflect] " + msg) + def log(msg: => AnyRef): Unit = if (isLogging) Console.err.println("[reflect] " + msg) type TreeCopier = InternalTreeCopierOps def newStrictTreeCopier: TreeCopier = new StrictTreeCopier diff --git a/src/reflect/scala/reflect/runtime/SymbolTable.scala b/src/reflect/scala/reflect/runtime/SymbolTable.scala index 5c08e9a508..ade7a4a21a 100644 --- a/src/reflect/scala/reflect/runtime/SymbolTable.scala +++ b/src/reflect/scala/reflect/runtime/SymbolTable.scala @@ -11,10 +11,10 @@ import scala.reflect.internal.Flags._ private[scala] trait SymbolTable extends internal.SymbolTable with JavaMirrors with SymbolLoaders with SynchronizedOps { def info(msg: => String) = - if (settings.verbose.value) println("[reflect-compiler] "+msg) + if (settings.verbose) println("[reflect-compiler] "+msg) def debugInfo(msg: => String) = - if (settings.debug.value) info(msg) + if (settings.debug) info(msg) /** Declares that this is a runtime reflection universe. * diff --git a/src/repl/scala/tools/nsc/MainGenericRunner.scala b/src/repl/scala/tools/nsc/MainGenericRunner.scala index 7195424cf9..9e87b6ba55 100644 --- a/src/repl/scala/tools/nsc/MainGenericRunner.scala +++ b/src/repl/scala/tools/nsc/MainGenericRunner.scala @@ -17,7 +17,7 @@ object JarRunner extends CommonRunner { val jarURLs = ClassPath expandManifestPath jarPath val urls = if (jarURLs.isEmpty) File(jarPath).toURL +: settings.classpathURLs else jarURLs - if (settings.Ylogcp.value) { + if (settings.Ylogcp) { Console.err.println("Running jar with these URLs as the classpath:") urls foreach println } @@ -46,7 +46,7 @@ class MainGenericRunner { def sampleCompiler = new Global(settings) // def so its not created unless needed if (!command.ok) return errorFn("\n" + command.shortUsageMsg) - else if (settings.version.value) return errorFn("Scala code runner %s -- %s".format(versionString, copyrightString)) + 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 diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 599a061984..df28e428ce 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -628,10 +628,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) * with SimpleReader. */ def chooseReader(settings: Settings): InteractiveReader = { - if (settings.Xnojline.value || Properties.isEmacsShell) + if (settings.Xnojline || Properties.isEmacsShell) SimpleReader() else try new JLineReader( - if (settings.noCompletion.value) NoCompletion + if (settings.noCompletion) NoCompletion else new JLineCompletion(intp) ) catch { diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index d2b6cdd7f0..4ba81b634a 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -397,7 +397,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set } private[nsc] def replwarn(msg: => String) { - if (!settings.nowarnings.value) + if (!settings.nowarnings) printMessage(msg) } diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index e517a16b32..da6d271a68 100644 --- a/src/repl/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala @@ -70,7 +70,7 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re pass += 1 val (repeats, unseen) = todo partition seen unseenHistory += unseen.size - if (settings.verbose.value) { + if (settings.verbose) { println("%3d %s accumulated, %s discarded. This pass: %s unseen, %s repeats".format( pass, keep.size, discarded, unseen.size, repeats.size)) } diff --git a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala index b4d2adaad4..4607684c0d 100644 --- a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala @@ -126,7 +126,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor } private[doc] def docdbg(msg: String) { - if (settings.Ydocdebug.value) + if (settings.Ydocdebug) println(msg) } } diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala index 5ad50445a8..bf6d6ffed7 100644 --- a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala +++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala @@ -91,7 +91,7 @@ trait ScaladocAnalyzer extends Analyzer { typedStats(trees, NoSymbol) useCase.defined = context.scope.toList filterNot (useCase.aliases contains _) - if (settings.debug.value) + if (settings.debug) useCase.defined foreach (sym => println("defined use cases: %s:%s".format(sym, sym.tpe))) useCase.defined diff --git a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala index 9447e36610..ea45ca1a56 100644 --- a/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala +++ b/src/scaladoc/scala/tools/nsc/doc/Uncompilable.scala @@ -28,7 +28,7 @@ trait Uncompilable { lazy val pairs = files flatMap { f => val comments = docPairs(f.slurp()) - if (settings.verbose.value) + if (settings.verbose) inform("Found %d doc comments in parse-only file %s: %s".format(comments.size, f, comments.map(_._1).mkString(", "))) comments @@ -37,7 +37,7 @@ trait Uncompilable { def symbols = pairs map (_._1) def templates = symbols filter (x => x.isClass || x.isTrait || x == AnyRefClass/* which is now a type alias */) toSet def comments = { - if (settings.debug.value || settings.verbose.value) + if (settings.debug || settings.verbose) inform("Found %d uncompilable files: %s".format(files.size, files mkString ", ")) if (pairs.isEmpty) diff --git a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala index 2a07547de2..ac5fec80b3 100755 --- a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala @@ -73,16 +73,20 @@ object EntityLink { def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link)) } final case class HtmlTag(data: String) extends Inline { - def canClose(open: HtmlTag) = { - open.data.stripPrefix("<") == data.stripPrefix("</") + private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r + private val (isEnd, tagName) = data match { + case Pattern(s1, s2) => + (! s1.isEmpty, Some(s2.toLowerCase)) + case _ => + (false, None) } - def close = { - if (data.indexOf("</") == -1) - Some(HtmlTag("</" + data.stripPrefix("<"))) - else - None + def canClose(open: HtmlTag) = { + isEnd && tagName == open.tagName } + + private val TagsNotToClose = Set("br", "img") + def close = tagName collect { case name if !TagsNotToClose(name) => HtmlTag(s"</$name>") } } /** The summary of a comment, usually its first sentence. There must be exactly one summary per body. */ diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala index 229e26d699..9edd5afa13 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala @@ -60,7 +60,7 @@ abstract class HtmlPage extends Page { thisPage => w.write(xml.Xhtml.toXhtml(html)) } - if (site.universe.settings.docRawOutput.value) + if (site.universe.settings.docRawOutput) writeFile(site, ".raw") { // we're only interested in the body, as this will go into the diff _.write(body.text) diff --git a/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala b/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala index 5781e680dd..348ea97c5b 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/SyntaxHigh.scala @@ -6,6 +6,7 @@ package scala.tools.nsc.doc.html import scala.xml.NodeSeq +import scala.annotation.tailrec /** Highlight the syntax of Scala code appearing in a `{{{` wiki block * (see method `HtmlPage.blockToHtml`). @@ -209,9 +210,9 @@ private[html] object SyntaxHigh { out.toString } - def parse(pre: String, i: Int): Int = { + @tailrec def parse(pre: String, i: Int): Unit = { out append pre - if (i == buf.length) return i + if (i == buf.length) return buf(i) match { case '\n' => parse("\n", i+1) @@ -277,7 +278,6 @@ private[html] object SyntaxHigh { } else parse(buf(i).toChar.toString, i+1) } - i } parse("", 0) diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala index ec00cace75..ab8e9e2756 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DiagramStats.scala @@ -42,7 +42,7 @@ object DiagramStats { private[this] var fixedImages = 0 def printStats(settings: Settings) = { - if (settings.docDiagramsDebug.value) { + if (settings.docDiagramsDebug) { settings.printMsg("\nDiagram generation running time breakdown:\n") filterTrack.printStats(settings.printMsg) modelTrack.printStats(settings.printMsg) @@ -63,4 +63,4 @@ object DiagramStats { def addBrokenImage(): Unit = brokenImages += 1 def addFixedImage(): Unit = fixedImages += 1 -}
\ No newline at end of file +} diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala index 837d9e6f21..7d146b4a5f 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/diagram/DotDiagramGenerator.scala @@ -329,7 +329,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator { NodeSeq.Empty } catch { case exc: Exception => - if (settings.docDiagramsDebug.value) { + if (settings.docDiagramsDebug) { settings.printMsg("\n\n**********************************************************************") settings.printMsg("Encountered an error while generating page for " + template.qualifiedName) settings.printMsg(dotInput.toString.split("\n").mkString("\nDot input:\n\t","\n\t","")) diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala index 895cc84f39..cc228082c1 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactory.scala @@ -272,7 +272,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { assert(!(docTemplatesCache isDefinedAt sym), sym) docTemplatesCache += (sym -> this) - if (settings.verbose.value) + if (settings.verbose) inform("Creating doc template for " + sym) override def toRoot: List[DocTemplateImpl] = this :: inTpl.toRoot @@ -350,7 +350,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // the implicit conversions are generated eagerly, but the members generated by implicit conversions are added // lazily, on completeModel val conversions: List[ImplicitConversionImpl] = - if (settings.docImplicits.value) makeImplicitConversions(sym, this) else Nil + if (settings.docImplicits) makeImplicitConversions(sym, this) else Nil // members as given by the compiler lazy val memberSyms = sym.info.members.filter(s => membersShouldDocument(s, this)).toList @@ -924,7 +924,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { def ownerTpl(sym: Symbol): Symbol = if (sym.isClass || sym.isModule || sym == NoSymbol) sym else ownerTpl(sym.owner) val tpe = - if (thisFactory.settings.useStupidTypes.value) aType else { + if (thisFactory.settings.useStupidTypes) aType else { def ownerTpl(sym: Symbol): Symbol = if (sym.isClass || sym.isModule || sym == NoSymbol) sym else ownerTpl(sym.owner) val fixedSym = if (inTpl.sym.isModule) inTpl.sym.moduleClass else inTpl.sym @@ -1036,7 +1036,7 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { // whether or not to create a page for an {abstract,alias} type def typeShouldDocument(bSym: Symbol, inTpl: DocTemplateImpl) = - (settings.docExpandAllTypes.value && (bSym.sourceFile != null)) || + (settings.docExpandAllTypes && (bSym.sourceFile != null)) || (bSym.isAliasType || bSym.isAbstractType) && { val rawComment = global.expandedDocComment(bSym, inTpl.sym) rawComment.contains("@template") || rawComment.contains("@documentable") } diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala index 71b0a00e0a..1f87f935f2 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala @@ -241,7 +241,7 @@ trait ModelFactoryImplicitSupport { available match { case Some(true) => Nil - case Some(false) if (!settings.docImplicitsShowAll.value) => + case Some(false) if !settings.docImplicitsShowAll => // if -implicits-show-all is not set, we get rid of impossible conversions (such as Numeric[String]) throw new ImplicitNotFound(implType) case _ => diff --git a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala index 99e9059d79..d5048dcfa3 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/ModelFactoryTypeSupport.scala @@ -140,7 +140,7 @@ trait ModelFactoryTypeSupport { } val prefix = - if (!settings.docNoPrefixes.value && needsPrefix && (bSym != AnyRefClass /* which we normalize */)) { + if (!settings.docNoPrefixes && needsPrefix && (bSym != AnyRefClass /* which we normalize */)) { if (!owner.isRefinementClass) { val qName = makeQualifiedName(owner, Some(inTpl.sym)) if (qName != "") qName + "." else "" @@ -308,7 +308,7 @@ trait ModelFactoryTypeSupport { // SI-4360: Entity caching depends on both the type AND the template it's in, as the prefixes might change for the // same type based on the template the type is shown in. - if (settings.docNoPrefixes.value) + if (settings.docNoPrefixes) typeCache.getOrElseUpdate(aType, createTypeEntity) else createTypeEntity } diff --git a/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala b/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala index 6395446d3b..44d8886e4e 100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/diagram/DiagramDirectiveParser.scala @@ -248,7 +248,7 @@ trait DiagramDirectiveParser { hideNodesFilter = hideNodesFilter0, hideEdgesFilter = hideEdgesFilter0) - if (settings.docDiagramsDebug.value && result != NoDiagramAtAll && result != FullDiagram) + if (settings.docDiagramsDebug && result != NoDiagramAtAll && result != FullDiagram) settings.printMsg(template.kind + " " + template.qualifiedName + " filter: " + result) tFilter += System.currentTimeMillis diff --git a/starr.number b/starr.number new file mode 100644 index 0000000000..89659fcbf3 --- /dev/null +++ b/starr.number @@ -0,0 +1 @@ +starr.version=2.11.0-M2
\ No newline at end of file diff --git a/test/build-partest.xml b/test/build-partest.xml new file mode 100755 index 0000000000..44502ffa61 --- /dev/null +++ b/test/build-partest.xml @@ -0,0 +1,24 @@ +<project name="partest" basedir="."> + <dirname property="partest.basedir" file="${ant.file.partest}"/> + <property file="${partest.basedir}/included.properties"/> + + <macrodef name="testSuite"> + <attribute name="dir" default="${partest.basedir}/test"/> + <attribute name="srcdir" default="files"/> <!-- TODO: make targets for `pending` and other subdirs --> + <attribute name="colors" default="${partest.colors}"/> + <attribute name="scalacOpts" default="${scalac.args.optimise}"/> + <attribute name="kinds" default="pos neg run jvm res scalap scalacheck specialized instrumented presentation"/> + <sequential> + <property name="partest.dir" value="@{dir}" /> + <partest srcdir="@{srcdir}" + kinds="@{kinds}" + colors="@{colors}" + scalacOpts="@{scalacOpts}" + compilationpathref="partest.classpath"> + <compilationpath> + <fileset dir="${partest.dir}/files/lib" includes="*.jar" /> + </compilationpath> + </partest> + </sequential> + </macrodef> +</project> diff --git a/test/files/jvm/t7253.check b/test/files/jvm/t7253.check new file mode 100644 index 0000000000..43f53aba12 --- /dev/null +++ b/test/files/jvm/t7253.check @@ -0,0 +1 @@ +bytecode identical diff --git a/test/files/jvm/t7253/Base_1.scala b/test/files/jvm/t7253/Base_1.scala new file mode 100644 index 0000000000..a531ebb69d --- /dev/null +++ b/test/files/jvm/t7253/Base_1.scala @@ -0,0 +1,5 @@ +trait A { def f(): Int } +trait B1 extends A +abstract class B2 extends A +class B3 extends A { def f(): Int = 1 } +class B4 extends B3 diff --git a/test/files/jvm/t7253/JavaClient_1.java b/test/files/jvm/t7253/JavaClient_1.java new file mode 100644 index 0000000000..43475de2f5 --- /dev/null +++ b/test/files/jvm/t7253/JavaClient_1.java @@ -0,0 +1,9 @@ +public class JavaClient_1 { + int foo() { + ((A) null).f(); + ((B1) null).f(); + ((B2) null).f(); + ((B3) null).f(); + return ((B4) null).f(); + } +} diff --git a/test/files/jvm/t7253/ScalaClient_1.scala b/test/files/jvm/t7253/ScalaClient_1.scala new file mode 100644 index 0000000000..d244b326a8 --- /dev/null +++ b/test/files/jvm/t7253/ScalaClient_1.scala @@ -0,0 +1,9 @@ +class ScalaClient_1 { + def foo() = { + (null: A).f() + (null: B1).f() + (null: B2).f() + (null: B3).f() + (null: B4).f() + } +} diff --git a/test/files/jvm/t7253/test.scala b/test/files/jvm/t7253/test.scala new file mode 100644 index 0000000000..7fe08e8813 --- /dev/null +++ b/test/files/jvm/t7253/test.scala @@ -0,0 +1,28 @@ +import scala.tools.partest.BytecodeTest + +import scala.tools.nsc.util.JavaClassPath +import java.io.InputStream +import scala.tools.asm +import asm.ClassReader +import asm.tree.{ClassNode, InsnList} +import scala.collection.JavaConverters._ + +object Test extends BytecodeTest { + import instructions._ + + def show: Unit = { + val instrBaseSeqs = Seq("ScalaClient_1", "JavaClient_1") map (name => instructions.fromMethod(getMethod(loadClassNode(name), "foo"))) + val instrSeqs = instrBaseSeqs map (_ filter isInvoke) + cmpInstructions(instrSeqs(0), instrSeqs(1)) + } + + def cmpInstructions(isa: List[Instruction], isb: List[Instruction]) = { + if (isa == isb) println("bytecode identical") + else diffInstructions(isa, isb) + } + + def isInvoke(node: Instruction): Boolean = { + val opcode = node.opcode + (opcode == "INVOKEVIRTUAL") || (opcode == "INVOKEINTERFACE") + } +} diff --git a/test/files/neg/choices.check b/test/files/neg/choices.check index 3e63f9999d..b114394e96 100644 --- a/test/files/neg/choices.check +++ b/test/files/neg/choices.check @@ -1,2 +1,2 @@ -partest error: bad flags: -Ylinearizer +error: bad options: -Yresolve-term-conflict one error found diff --git a/test/files/neg/choices.flags b/test/files/neg/choices.flags index 5464a18c5d..9718467d4c 100644 --- a/test/files/neg/choices.flags +++ b/test/files/neg/choices.flags @@ -1 +1 @@ --Ylinearizer
\ No newline at end of file +-Yresolve-term-conflict diff --git a/test/files/neg/javac-error.check b/test/files/neg/javac-error.check new file mode 100644 index 0000000000..e7d1ccc1a1 --- /dev/null +++ b/test/files/neg/javac-error.check @@ -0,0 +1,10 @@ +#partest java6 +javac-error/J.java:2: method does not override or implement a method from a supertype + @Override public void foo() { } + ^ +1 error +#partest java7 +javac-error/J.java:2: error: method does not override or implement a method from a supertype + @Override public void foo() { } + ^ +1 error diff --git a/test/files/neg/javac-error.flags b/test/files/neg/javac-error.flags new file mode 100644 index 0000000000..85d8eb2ba2 --- /dev/null +++ b/test/files/neg/javac-error.flags @@ -0,0 +1 @@ +-Xfatal-warnings diff --git a/test/files/neg/javac-error/J.java b/test/files/neg/javac-error/J.java new file mode 100644 index 0000000000..83f50c9ae2 --- /dev/null +++ b/test/files/neg/javac-error/J.java @@ -0,0 +1,5 @@ +public class J { + @Override public void foo() { } + + public void bar() { foo(); } +} diff --git a/test/files/neg/javac-error/SUT_5.scala b/test/files/neg/javac-error/SUT_5.scala new file mode 100644 index 0000000000..0a996352c0 --- /dev/null +++ b/test/files/neg/javac-error/SUT_5.scala @@ -0,0 +1,5 @@ + +/** The System Under Test. + * We bail on the earlier round that generates the first error. + */ +class SUT extends J diff --git a/test/files/neg/t3631.check b/test/files/neg/t3631.check deleted file mode 100644 index 6d8feca1ed..0000000000 --- a/test/files/neg/t3631.check +++ /dev/null @@ -1,4 +0,0 @@ -t3631.scala:3: error: Implementation restriction: case classes cannot have more than 22 parameters. -case class X23(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int, x11: Int, x12: Int, x13: Int, x14: Int, x15: Int, x16: Int, x17: Int, x18: Int, x19: Int, x20: Int, x21: Int, x22: Int, x23: Int) { } - ^ -one error found diff --git a/test/files/neg/t5580b.check b/test/files/neg/t5580b.check new file mode 100644 index 0000000000..45fde46ff9 --- /dev/null +++ b/test/files/neg/t5580b.check @@ -0,0 +1,6 @@ +t5580b.scala:11: error: polymorphic expression cannot be instantiated to expected type; + found : [A]scala.collection.mutable.Set[A] + required: scala.collection.mutable.Map[bar,scala.collection.mutable.Set[bar]] + if (map.get(tmp).isEmpty) map.put(tmp,collection.mutable.Set()) + ^ +one error found diff --git a/test/files/pos/t5580b.scala b/test/files/neg/t5580b.scala index d5a4a0a2b2..2161da4584 100644 --- a/test/files/pos/t5580b.scala +++ b/test/files/neg/t5580b.scala @@ -1,9 +1,3 @@ -/** It's a pos test because it does indeed compile, - * not so much because I'm glad it does. Testing - * that error messages created and discarded during - * implicit search don't blow it up. - */ - import scala.collection.mutable.WeakHashMap import scala.collection.JavaConversions._ diff --git a/test/files/neg/t7110.check b/test/files/neg/t7110.check new file mode 100644 index 0000000000..e484dc4325 --- /dev/null +++ b/test/files/neg/t7110.check @@ -0,0 +1,6 @@ +t7110.scala:2: warning: A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled. + try { ??? } // warn + ^ +error: No warnings can be incurred under -Xfatal-warnings. +one warning found +one error found diff --git a/test/files/neg/t7110.flags b/test/files/neg/t7110.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t7110.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t7110.scala b/test/files/neg/t7110.scala new file mode 100644 index 0000000000..79ac325216 --- /dev/null +++ b/test/files/neg/t7110.scala @@ -0,0 +1,6 @@ +object Test { + try { ??? } // warn + + try { ??? } finally ??? // no warn + try { ??? } catch { case _: Throwable => } // no warn +} diff --git a/test/files/neg/t7251.check b/test/files/neg/t7251.check new file mode 100644 index 0000000000..8df8984d63 --- /dev/null +++ b/test/files/neg/t7251.check @@ -0,0 +1,4 @@ +B_2.scala:5: error: object s.Outer$Triple$ is not a value + println( s.Outer$Triple$ ) + ^ +one error found diff --git a/test/files/neg/t7251/A_1.scala b/test/files/neg/t7251/A_1.scala new file mode 100644 index 0000000000..d05373ed28 --- /dev/null +++ b/test/files/neg/t7251/A_1.scala @@ -0,0 +1,10 @@ +package s + +object Outer { + type Triple[+A, +B, +C] = Tuple3[A, B, C] + object Triple { + def apply[A, B, C](x: A, y: B, z: C) = Tuple3(x, y, z) + def unapply[A, B, C](x: Tuple3[A, B, C]): Option[Tuple3[A, B, C]] = Some(x) + } +} + diff --git a/test/files/neg/t7251/B_2.scala b/test/files/neg/t7251/B_2.scala new file mode 100644 index 0000000000..eb59b30902 --- /dev/null +++ b/test/files/neg/t7251/B_2.scala @@ -0,0 +1,7 @@ +package s + +object Test { + def main(args: Array[String]): Unit = { + println( s.Outer$Triple$ ) + } +} diff --git a/test/files/neg/t7259.check b/test/files/neg/t7259.check new file mode 100644 index 0000000000..0ad627fc3b --- /dev/null +++ b/test/files/neg/t7259.check @@ -0,0 +1,7 @@ +t7259.scala:1: error: not found: type xxxxx +@xxxxx // error: not found: type xxxx + ^ +t7259.scala:8: error: type xxxxx is not a member of package annotation +@annotation.xxxxx // error: not found: type scala + ^ +two errors found diff --git a/test/files/neg/t7259.scala b/test/files/neg/t7259.scala new file mode 100644 index 0000000000..0fdfe18822 --- /dev/null +++ b/test/files/neg/t7259.scala @@ -0,0 +1,9 @@ +@xxxxx // error: not found: type xxxx +class Ok + +// +// This had the wrong error message in 2.9 and 2.10. +// + +@annotation.xxxxx // error: not found: type scala +class WrongErrorMessage diff --git a/test/files/neg/t7285.check b/test/files/neg/t7285.check new file mode 100644 index 0000000000..a38772bead --- /dev/null +++ b/test/files/neg/t7285.check @@ -0,0 +1,15 @@ +t7285.scala:15: warning: match may not be exhaustive. +It would fail on the following input: (Up, Down) + (d1, d2) match { + ^ +t7285.scala:33: warning: match may not be exhaustive. +It would fail on the following input: Down + (d1) match { + ^ +t7285.scala:51: warning: match may not be exhaustive. +It would fail on the following input: (Up, Down) + (d1, d2) match { + ^ +error: No warnings can be incurred under -Xfatal-warnings. +three warnings found +one error found diff --git a/test/files/neg/t7285.flags b/test/files/neg/t7285.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t7285.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t7285.scala b/test/files/neg/t7285.scala new file mode 100644 index 0000000000..14121d92b1 --- /dev/null +++ b/test/files/neg/t7285.scala @@ -0,0 +1,55 @@ +sealed abstract class Base + + +object Test1 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + (d1: Base, d2: Base) => + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Up) => true + } + } +} + +object Test2 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + (d1: Base, d2: Base) => + (d1) match { + case Test2.Base.Up => false + } + } +} + + +object Test4 { + sealed abstract class Base + + object Base { + case object Down extends Base + + case object Up extends Base + } + + import Test4.Base._ + (d1: Base, d2: Base) => + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Test4.Base.Up) => true + } +} diff --git a/test/files/neg/t7290.check b/test/files/neg/t7290.check new file mode 100644 index 0000000000..ad2d0e25b0 --- /dev/null +++ b/test/files/neg/t7290.check @@ -0,0 +1,12 @@ +t7290.scala:4: warning: Pattern contains duplicate alternatives: 0 + case 0 | 0 => 0 + ^ +t7290.scala:5: warning: Pattern contains duplicate alternatives: 2, 3 + case 2 | 2 | 2 | 3 | 2 | 3 => 0 + ^ +t7290.scala:6: warning: Pattern contains duplicate alternatives: 4 + case 4 | (_ @ 4) => 0 + ^ +error: No warnings can be incurred under -Xfatal-warnings. +three warnings found +one error found diff --git a/test/files/neg/t7290.flags b/test/files/neg/t7290.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/neg/t7290.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t7290.scala b/test/files/neg/t7290.scala new file mode 100644 index 0000000000..b9db7f7e8a --- /dev/null +++ b/test/files/neg/t7290.scala @@ -0,0 +1,10 @@ +object Test extends App { + val y = (0: Int) match { + case 1 => 1 + case 0 | 0 => 0 + case 2 | 2 | 2 | 3 | 2 | 3 => 0 + case 4 | (_ @ 4) => 0 + case _ => -1 + } + assert(y == 0, y) +} diff --git a/test/files/neg/t7299.check b/test/files/neg/t7299.check new file mode 100644 index 0000000000..74340c4841 --- /dev/null +++ b/test/files/neg/t7299.check @@ -0,0 +1,7 @@ +t7299.scala:4: error: implementation restricts functions to 22 parameters + val eta1 = f _ + ^ +t7299.scala:5: error: implementation restricts functions to 22 parameters + val eta2 = g[Any] _ + ^ +two errors found diff --git a/test/files/neg/t7299.scala b/test/files/neg/t7299.scala new file mode 100644 index 0000000000..f3aae5ce5d --- /dev/null +++ b/test/files/neg/t7299.scala @@ -0,0 +1,6 @@ +object Test { + def f(a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int, a7: Int, a8: Int, a9: Int, a10: Int, a11: Int, a12: Int, a13: Int, a14: Int, a15: Int, a16: Int, a17: Int, a18: Int, a19: Int, a20: Int, a21: Int, a22: Int, a23: Int) = 0 + def g[A](a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int, a7: Int, a8: Int, a9: Int, a10: Int, a11: Int, a12: Int, a13: Int, a14: Int, a15: Int, a16: Int, a17: Int, a18: Int, a19: Int, a20: Int, a21: Int, a22: Int, a23: Int) = 0 + val eta1 = f _ + val eta2 = g[Any] _ +} diff --git a/test/files/pos/t3120/J1.java b/test/files/pos/t3120/J1.java new file mode 100644 index 0000000000..12b23c1c98 --- /dev/null +++ b/test/files/pos/t3120/J1.java @@ -0,0 +1,4 @@ +class J1 { + public class Inner1 { } + public static class Inner2 { } +} diff --git a/test/files/pos/t3120/J2.java b/test/files/pos/t3120/J2.java new file mode 100644 index 0000000000..db6e859020 --- /dev/null +++ b/test/files/pos/t3120/J2.java @@ -0,0 +1,4 @@ +public class J2 { + public void f1(J1.Inner1 p) { } + public void f2(J1.Inner2 p) { } +} diff --git a/test/files/pos/t3120/Q.java b/test/files/pos/t3120/Q.java new file mode 100644 index 0000000000..fe2269308a --- /dev/null +++ b/test/files/pos/t3120/Q.java @@ -0,0 +1,3 @@ +public class Q { + public static void passInner(J1.Inner1 myInner) {} +} diff --git a/test/files/pos/t3120/Test.scala b/test/files/pos/t3120/Test.scala new file mode 100644 index 0000000000..c02146fba1 --- /dev/null +++ b/test/files/pos/t3120/Test.scala @@ -0,0 +1,3 @@ +object Test { + Q.passInner(null) +} diff --git a/test/files/neg/t3631.scala b/test/files/pos/t3631.scala index bcf91619ee..bcf91619ee 100644 --- a/test/files/neg/t3631.scala +++ b/test/files/pos/t3631.scala diff --git a/test/files/pos/t6210.flags b/test/files/pos/t6210.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t6210.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/t6210.scala b/test/files/pos/t6210.scala new file mode 100644 index 0000000000..1ce8493872 --- /dev/null +++ b/test/files/pos/t6210.scala @@ -0,0 +1,21 @@ +abstract sealed trait AST +abstract sealed trait AExpr extends AST +case class AAssign(name: String, v: AExpr) extends AExpr +case class AConstBool(v: Boolean) extends AExpr + +trait Ty {} +case class TInt() extends Ty +case class TBool() extends Ty + +object Foo { + def checkExpr(ast: AExpr): Ty = { + var astTy:Ty = ast match { + case AAssign(nm: String, v:AExpr) => TBool() + + case AConstBool(v: Boolean) => TBool() + + case _ => throw new Exception(s"Unhandled case check(ast: ${ast.getClass})") + } + astTy + } +} diff --git a/test/files/pos/t6921.scala b/test/files/pos/t6921.scala new file mode 100644 index 0000000000..36e70e5d2c --- /dev/null +++ b/test/files/pos/t6921.scala @@ -0,0 +1,11 @@ +class Message(messageType: String, reason: Option[String]) + +class ReproForSI6921 { + + private[this] var reason = "" + + def decideElection = { + val explanation = None + new Message("", reason = explanation) + } +} diff --git a/test/files/pos/t7232.flags b/test/files/pos/t7232.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t7232.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/t7232/Foo.java b/test/files/pos/t7232/Foo.java new file mode 100644 index 0000000000..3478301b30 --- /dev/null +++ b/test/files/pos/t7232/Foo.java @@ -0,0 +1,9 @@ +package pack; + +import java.util.List; + +public class Foo { + public static java.util.List okay() { throw new Error(); } + + public static List wrong() { throw new Error(); } +} diff --git a/test/files/pos/t7232/List.java b/test/files/pos/t7232/List.java new file mode 100644 index 0000000000..e42c63aa67 --- /dev/null +++ b/test/files/pos/t7232/List.java @@ -0,0 +1,4 @@ +package pack; + +public class List { +} diff --git a/test/files/pos/t7232/Test.scala b/test/files/pos/t7232/Test.scala new file mode 100644 index 0000000000..49c3c12aed --- /dev/null +++ b/test/files/pos/t7232/Test.scala @@ -0,0 +1,5 @@ +object Test { + import pack._ + Foo.okay().size() + Foo.wrong().size() +} diff --git a/test/files/pos/t7232b.flags b/test/files/pos/t7232b.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t7232b.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/t7232b/Foo.java b/test/files/pos/t7232b/Foo.java new file mode 100644 index 0000000000..94f08d545e --- /dev/null +++ b/test/files/pos/t7232b/Foo.java @@ -0,0 +1,8 @@ +package pack; + +import java.util.*; + +public class Foo { + // should be pack.List. + public static List list() { throw new Error(); } +} diff --git a/test/files/pos/t7232b/List.java b/test/files/pos/t7232b/List.java new file mode 100644 index 0000000000..ce977152b9 --- /dev/null +++ b/test/files/pos/t7232b/List.java @@ -0,0 +1,5 @@ +package pack; + +public class List { + public void packList() {} +} diff --git a/test/files/pos/t7232b/Test.scala b/test/files/pos/t7232b/Test.scala new file mode 100644 index 0000000000..6377e26bec --- /dev/null +++ b/test/files/pos/t7232b/Test.scala @@ -0,0 +1,5 @@ +object Test { + import pack._ + + Foo.list().packList() +} diff --git a/test/files/pos/t7232c.flags b/test/files/pos/t7232c.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t7232c.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/t7232c/Foo.java b/test/files/pos/t7232c/Foo.java new file mode 100644 index 0000000000..bbda09a2da --- /dev/null +++ b/test/files/pos/t7232c/Foo.java @@ -0,0 +1,10 @@ +package pack; + +import java.util.List; + +public class Foo { + public static class List { + public void isInnerList() {} + } + public static List innerList() { throw new Error(); } +} diff --git a/test/files/pos/t7232c/Test.scala b/test/files/pos/t7232c/Test.scala new file mode 100644 index 0000000000..aa7c710948 --- /dev/null +++ b/test/files/pos/t7232c/Test.scala @@ -0,0 +1,4 @@ +object Test { + import pack._ + Foo.innerList().isInnerList() +} diff --git a/test/files/pos/t7232d.flags b/test/files/pos/t7232d.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t7232d.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/t7232d/Entry.java b/test/files/pos/t7232d/Entry.java new file mode 100644 index 0000000000..0cfb6fb25b --- /dev/null +++ b/test/files/pos/t7232d/Entry.java @@ -0,0 +1,4 @@ +package pack; + +public class Entry { +} diff --git a/test/files/pos/t7232d/Foo.java b/test/files/pos/t7232d/Foo.java new file mode 100644 index 0000000000..df7114a0f0 --- /dev/null +++ b/test/files/pos/t7232d/Foo.java @@ -0,0 +1,8 @@ +package pack; + +import java.util.Map.Entry; + +public class Foo { + public static Entry mapEntry() { throw new Error(); } + public static void javaTest() { mapEntry().getKey(); } +} diff --git a/test/files/pos/t7232d/Test.scala b/test/files/pos/t7232d/Test.scala new file mode 100644 index 0000000000..89a8063b3c --- /dev/null +++ b/test/files/pos/t7232d/Test.scala @@ -0,0 +1,4 @@ +object Test { + import pack._ + Foo.mapEntry().getKey() +} diff --git a/test/files/pos/t7233.scala b/test/files/pos/t7233.scala new file mode 100644 index 0000000000..ae15c08c35 --- /dev/null +++ b/test/files/pos/t7233.scala @@ -0,0 +1,14 @@ +object Foo { + def bar(i: Int) = i + + def ol(i: Int) = i + def ol(i: String) = i +} +object Test { + import Foo.{ bar => quux, toString => bar, ol => olRenamed} + + val f1 = quux _ + val f1Typed: (Int => Int) = f1 + + val f2: String => String = olRenamed _ +} diff --git a/test/files/pos/t7233b.scala b/test/files/pos/t7233b.scala new file mode 100644 index 0000000000..927c7fcfd1 --- /dev/null +++ b/test/files/pos/t7233b.scala @@ -0,0 +1,8 @@ +object Test { + // crash + def foo(a: Any) = { import a.{toString => toS}; toS } + + // okay + def ok1(a: String) = { import a.{isInstanceOf => iio}; iio[String] } + def ok2(a: Int) = { import a.{toInt => ti}; ti } +} diff --git a/test/files/pos/t7239.scala b/test/files/pos/t7239.scala new file mode 100644 index 0000000000..16e9d00f17 --- /dev/null +++ b/test/files/pos/t7239.scala @@ -0,0 +1,38 @@ +object Test { + def BrokenMethod(): HasFilter[(Int, String)] = ??? + + trait HasFilter[B] { + def filter(p: B => Boolean) = ??? + } + + trait HasWithFilter { + def withFilter = ??? + } + + object addWithFilter { + trait NoImplicit + implicit def enrich(v: Any) + (implicit F0: NoImplicit): HasWithFilter = ??? + } + + BrokenMethod().withFilter(_ => true) // okay + BrokenMethod().filter(_ => true) // okay + + locally { + import addWithFilter._ + BrokenMethod().withFilter((_: (Int, String)) => true) // okay + } + + locally { + import addWithFilter._ + // adaptToMemberWithArgs sets the type of the tree `x` + // to ErrorType (while in silent mode, so the error is not + // reported. Later, when the fallback from `withFilter` + // to `filter` is attempted, the closure is taken to have + // have the type `<error> => Boolean`, which conforms to + // `(B => Boolean)`. Only later during pickling does the + // defensive check for erroneous types in the tree pick up + // the problem. + BrokenMethod().withFilter(x => true) // erroneous or inaccessible type. + } +} diff --git a/test/files/pos/t7285a.flags b/test/files/pos/t7285a.flags new file mode 100644 index 0000000000..e8fb65d50c --- /dev/null +++ b/test/files/pos/t7285a.flags @@ -0,0 +1 @@ +-Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/t7285a.scala b/test/files/pos/t7285a.scala new file mode 100644 index 0000000000..34e79c741b --- /dev/null +++ b/test/files/pos/t7285a.scala @@ -0,0 +1,83 @@ +sealed abstract class Base + +object Test { + case object Up extends Base + + def foo(d1: Base) = + d1 match { + case Up => + } + + // Sealed subtype: ModuleTypeRef <empty>.this.Test.Up.type + // Pattern: UniqueThisType Test.this.type +} + + +object Test1 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + (d1: Base, d2: Base) => + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Up) => true + case (Up, Down) => false + } + } +} + +object Test2 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + (d1: Base, d2: Base) => + (d1) match { + case Up | Down => false + } + } +} + +object Test3 { + sealed abstract class Base + + object Base { + case object Down extends Base + + (d1: Base, d2: Base) => + (d1, d2) match { + case (Down, Down) => false + } + } +} + +object Test4 { + sealed abstract class Base + + object Base { + case object Down extends Base { + } + + case object Up extends Base { + } + + } + import Test4.Base._ + (d1: Base, d2: Base) => + (d1, d2) match { + case (Up, Up) | (Down, Down) => false + case (Down, Test4.Base.Up) => true + case (Up, Down) => false + } +} diff --git a/test/files/pos/t7296.scala b/test/files/pos/t7296.scala new file mode 100644 index 0000000000..0c078d3657 --- /dev/null +++ b/test/files/pos/t7296.scala @@ -0,0 +1,6 @@ +object Test { + type A = Int + // Emits the implementation restriction but then proceeds to crash + // when creating the Foo.unapply. + case class Foo(a: A, b: A, c: A, d: A, e: A, f: A, g: A, h: A, i: A, j: A, k: A, l: A, m: A, n: A, o: A, p: A, q: A, r: A, s: A, t: A, u: A, v: A, w: A, x: A, y: A, Z: A) +} diff --git a/test/files/presentation/doc/doc.scala b/test/files/presentation/doc/doc.scala index 7a2eb9a588..916b7832f4 100755 --- a/test/files/presentation/doc/doc.scala +++ b/test/files/presentation/doc/doc.scala @@ -127,16 +127,17 @@ object Test extends InteractiveTest { val baseSource = findSource("Base.scala") val derivedSource = findSource("Derived.scala") def existsText(where: Any, text: String): Boolean = where match { - case `text` => true + case s: String => s contains text case s: Seq[_] => s exists (existsText(_, text)) case p: Product => p.productIterator exists (existsText(_, text)) + case c: Comment => existsText(c.body, text) } val (derived, base) = compiler.ask { () => val derived = definitions.RootPackage.info.decl(newTermName("p")).info.decl(newTypeName("Derived")) (derived, derived.ancestors(0)) } val cmt1 = getComment(derived, derivedSource, (base, baseSource)::(derived, derivedSource)::Nil) - if (!existsText(cmt1, "Derived comment.")) + if (!existsText(cmt1, "This is Derived comment")) println("Unexpected Derived class comment:"+cmt1) val (fooDerived, fooBase) = compiler.ask { () => @@ -145,7 +146,7 @@ object Test extends InteractiveTest { } val cmt2 = getComment(fooDerived, derivedSource, (fooBase, baseSource)::(fooDerived, derivedSource)::Nil) - if (!existsText(cmt2, "Base method has documentation.")) + if (!existsText(cmt2, "Base method has documentation")) println("Unexpected foo method comment:"+cmt2) } } diff --git a/test/files/presentation/doc/src/p/Base.scala b/test/files/presentation/doc/src/p/Base.scala index 9031de3e3e..d91632b6f6 100755 --- a/test/files/presentation/doc/src/p/Base.scala +++ b/test/files/presentation/doc/src/p/Base.scala @@ -1,7 +1,7 @@ package p /** - * @define BaseComment $BaseVar comment. + * @define BaseComment This is $BaseVar comment. */ trait Base { /** diff --git a/test/files/run/blame_eye_triple_eee.check b/test/files/run/blame_eye_triple_eee-double.check index 5e46d91a8f..5e46d91a8f 100644 --- a/test/files/run/blame_eye_triple_eee.check +++ b/test/files/run/blame_eye_triple_eee-double.check diff --git a/test/files/run/blame_eye_triple_eee.flags b/test/files/run/blame_eye_triple_eee-double.flags index c9b68d70dc..c9b68d70dc 100644 --- a/test/files/run/blame_eye_triple_eee.flags +++ b/test/files/run/blame_eye_triple_eee-double.flags diff --git a/test/files/run/blame_eye_triple_eee.scala b/test/files/run/blame_eye_triple_eee-double.scala index 1640aead40..1640aead40 100644 --- a/test/files/run/blame_eye_triple_eee.scala +++ b/test/files/run/blame_eye_triple_eee-double.scala diff --git a/test/files/run/blame_eye_triple_eee-float.check b/test/files/run/blame_eye_triple_eee-float.check new file mode 100644 index 0000000000..5e46d91a8f --- /dev/null +++ b/test/files/run/blame_eye_triple_eee-float.check @@ -0,0 +1,9 @@ +if (NaN == NaN) is good +if (x == x) is good +if (x == NaN) is good +if (NaN != NaN) is good +if (x != x) is good +if (NaN != x) is good +x matching was good +NaN matching was good +loop with NaN was goood diff --git a/test/files/run/blame_eye_triple_eee-float.flags b/test/files/run/blame_eye_triple_eee-float.flags new file mode 100644 index 0000000000..c9b68d70dc --- /dev/null +++ b/test/files/run/blame_eye_triple_eee-float.flags @@ -0,0 +1 @@ +-optimise diff --git a/test/files/run/blame_eye_triple_eee-float.scala b/test/files/run/blame_eye_triple_eee-float.scala new file mode 100644 index 0000000000..4deb9f3d60 --- /dev/null +++ b/test/files/run/blame_eye_triple_eee-float.scala @@ -0,0 +1,61 @@ +object Test extends App { + import Float.NaN + + // NaN must not equal NaN no matter what optimizations are applied + // All the following will seem redundant, but to an optimizer + // they can appear different + + val x = NaN + + if (NaN == NaN) + println("if (NaN == NaN) is broken") + else + println("if (NaN == NaN) is good") + + if (x == x) + println("if (x == x) is broken") + else + println("if (x == x) is good") + + if (x == NaN) + println("if (x == NaN) is broken") + else + println("if (x == NaN) is good") + + if (NaN != NaN) + println("if (NaN != NaN) is good") + else + println("if (NaN != NaN) broken") + + if (x != x) + println("if (x != x) is good") + else + println("if (x != x) broken") + + if (NaN != x) + println("if (NaN != x) is good") + else + println("if (NaN != x) is broken") + + x match { + case 0.0f => println("x matched 0!") + case NaN => println("x matched NaN!") + case _ => println("x matching was good") + } + + NaN match { + case 0.0f => println("NaN matched 0!") + case NaN => println("NaN matched NaN!") + case _ => println("NaN matching was good") + } + + var z = 0.0f + var i = 0 + while (i < 10) { + if (i % 2 == 0) z = NaN + else z = NaN + i += 1 + } + if (z.isNaN && i == 10) println("loop with NaN was goood") + else println("loop with NaN was broken") +} diff --git a/test/files/run/case-class-23.check b/test/files/run/case-class-23.check new file mode 100644 index 0000000000..888ed2c9eb --- /dev/null +++ b/test/files/run/case-class-23.check @@ -0,0 +1,2 @@ +23 +(1,23) diff --git a/test/files/run/case-class-23.scala b/test/files/run/case-class-23.scala new file mode 100644 index 0000000000..92b719574a --- /dev/null +++ b/test/files/run/case-class-23.scala @@ -0,0 +1,33 @@ +case class TwentyThree( + _1: Int, + _2: Int, + _3: Int, + _4: Int, + _5: Int, + _6: Int, + _7: Int, + _8: Int, + _9: Int, + _10: Int, + _11: Int, + _12: Int, + _13: Int, + _14: Int, + _15: Int, + _16: Int, + _17: Int, + _18: Int, + _19: Int, + _20: Int, + _21: Int, + _22: Int, + _23: Int +) + +object Test extends App { + val x = new TwentyThree(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23) + println(x._23) + assert(x.copy(_1 = 1) == x) + val TwentyThree(a, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, b) = x + println((a, b)) +} diff --git a/test/files/run/classfile-format-51.scala b/test/files/run/classfile-format-51.scala new file mode 100644 index 0000000000..9b1e612f4f --- /dev/null +++ b/test/files/run/classfile-format-51.scala @@ -0,0 +1,126 @@ +import java.io.{File, FileOutputStream} + +import scala.tools.nsc.settings.ScalaVersion +import scala.tools.partest._ +import scala.tools.asm +import asm.{AnnotationVisitor, ClassWriter, FieldVisitor, Handle, MethodVisitor, Opcodes} +import Opcodes._ + +// This test ensures that we can read JDK 7 (classfile format 51) files, including those +// with invokeDynamic instructions and associated constant pool entries +// to do that it first uses ASM to generate a class called DynamicInvoker. Then +// it runs a normal compile on the source in the 'code' field that refers to +// DynamicInvoker. Any failure will be dumped to std out. +// +// By it's nature the test can only work on JDK 7+ because under JDK 6 some of the +// classes referred to by DynamicInvoker won't be available and DynamicInvoker won't +// verify. So the test includes a version check that short-circuites the whole test +// on JDK 6 +object Test extends DirectTest { + override def extraSettings: String = "-optimise -usejavacp -d " + testOutput.path + " -cp " + testOutput.path + + def generateClass() { + val invokerClassName = "DynamicInvoker" + val bootstrapMethodName = "bootstrap" + val bootStrapMethodType = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;" + val targetMethodName = "target" + val targetMethodType = "()Ljava/lang/String;" + + val cw = new ClassWriter(0) + cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, invokerClassName, null, "java/lang/Object", null) + + 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.visitInsn(RETURN) + constructor.visitMaxs(1, 1) + constructor.visitEnd() + + val target = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, targetMethodName, targetMethodType, null, null) + target.visitCode() + target.visitLdcInsn("hello") + target.visitInsn(ARETURN) + target.visitMaxs(1, 1) + target.visitEnd() + + 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.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.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.visitVarInsn(ASTORE, 5) // methodType + +// val methodHandle = lookup.findStatic(thisClass, "target", methodType) + bootstrap.visitVarInsn(ALOAD, 3) // lookup + 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.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.visitInsn(ARETURN) + bootstrap.visitMaxs(4,7) + bootstrap.visitEnd() + + val test = cw.visitMethod(ACC_PUBLIC + ACC_FINAL, "test", s"()Ljava/lang/String;", null, null) + test.visitCode() + val bootstrapHandle = new Handle(H_INVOKESTATIC, invokerClassName, bootstrapMethodName, bootStrapMethodType) + test.visitInvokeDynamicInsn("invoke", targetMethodType, bootstrapHandle) + test.visitInsn(ARETURN) + test.visitMaxs(1, 1) + test.visitEnd() + + cw.visitEnd() + val bytes = cw.toByteArray() + + val fos = new FileOutputStream(new File(s"${testOutput.path}/$invokerClassName.class")) + try + fos write bytes + finally + fos.close() + + } + + def code = +""" +object Driver { + val invoker = new DynamicInvoker() + println(invoker.test()) +} +""" + + override def show(): Unit = { + // redirect err to out, for logging + val prevErr = System.err + System.setErr(System.out) + try { + // this test is only valid under JDK 1.7+ + // cheat a little by using 'ScalaVersion' because it can parse java versions just as well + val requiredJavaVersion = ScalaVersion("1.7") + val executingJavaVersion = ScalaVersion(System.getProperty("java.specification.version")) + if (executingJavaVersion >= requiredJavaVersion) { + generateClass() + compile() + } + } + finally + System.setErr(prevErr) + } +} diff --git a/test/files/run/existentials3-new.check b/test/files/run/existentials3-new.check index 8f7dd701ac..c0233d2267 100644 --- a/test/files/run/existentials3-new.check +++ b/test/files/run/existentials3-new.check @@ -12,9 +12,9 @@ List[Seq[Int]], t=TypeRef, s=class List List[Seq[U forSome { type U <: Int }]], t=TypeRef, s=class List Bar.type, t=TypeRef, s=type Bar.type Bar, t=TypeRef, s=type Bar -Test.ToS, t=RefinedType, s=g3 -Test.ToS, t=RefinedType, s=g4 -Test.ToS, t=RefinedType, s=g5 +Test.ToS, t=RefinedType, s=g3 +Test.ToS, t=RefinedType, s=g4 +Test.ToS, t=RefinedType, s=g5 () => Test.ToS, t=TypeRef, s=trait Function0 () => Test.ToS, t=TypeRef, s=trait Function0 $anon, t=TypeRef, s=type $anon diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check index abcc8bf42d..7c885d2cc9 100644 --- a/test/files/run/inline-ex-handlers.check +++ b/test/files/run/inline-ex-handlers.check @@ -1,427 +1,492 @@ -172c172 -< locals: value x$1, value x1 ---- -> locals: value x$1, value x1, variable boxed1 -174c174 -< blocks: [1,2,3,4] ---- -> blocks: [1,3,4] -186a187,188 -> 92 STORE_LOCAL(variable boxed1) -> 92 LOAD_LOCAL(variable boxed1) -195,197d196 -< 92 JUMP 2 -< -< 2: -247c246 -< blocks: [1,2,3,4,5,6,7,8,11,12,13,14,15,16,17,18] ---- -> blocks: [1,2,3,4,5,6,8,11,12,13,14,15,16,17,18] -258,260d256 -< 92 JUMP 7 -< -< 7: -391c387 -< locals: value args, variable result, value ex6, value x4, value x5, value message, value x ---- -> locals: value args, variable result, value ex6, value x4, value x5, value x -393c389 -< blocks: [1,2,3,4,5,8,10,11,13] ---- -> blocks: [1,2,3,5,8,10,11,13,14] -417c413,422 -< 103 THROW(MyException) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 14 -> -> 14: -> 101 LOAD_LOCAL(value ex6) -> 101 STORE_LOCAL(value x4) -> 101 SCOPE_ENTER value x4 -> 106 LOAD_LOCAL(value x4) -> 106 IS_INSTANCE REF(class MyException) -> 106 CZJUMP (BOOL)NE ? 5 : 8 -430,432d434 -< 101 JUMP 4 -< -< 4: -442,445d443 -< 106 LOAD_LOCAL(value x5) -< 106 CALL_METHOD MyException.message (dynamic) -< 106 STORE_LOCAL(value message) -< 106 SCOPE_ENTER value message -447c445,446 -< 106 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 106 CALL_METHOD MyException.message (dynamic) -519c518 -< blocks: [1,2,3,4,6,7,9,10] ---- -> blocks: [1,3,4,6,7,9,10,11,12,13] -548c547,552 -< 306 THROW(MyException) ---- -> ? JUMP 11 -> -> 11: -> ? LOAD_LOCAL(variable monitor4) -> 305 MONITOR_EXIT -> ? JUMP 12 -554c558 -< ? THROW(Throwable) ---- -> ? JUMP 12 -560c564,571 -< ? THROW(Throwable) ---- -> ? STORE_LOCAL(value t) -> ? JUMP 13 -> -> 12: -> ? LOAD_LOCAL(variable monitor3) -> 304 MONITOR_EXIT -> ? STORE_LOCAL(value t) -> ? JUMP 13 -574c585 -< 310 JUMP 2 ---- -> 300 RETURN(UNIT) -576c587,596 -< 2: ---- -> 13: -> 310 LOAD_MODULE object Predef -> 310 CALL_PRIMITIVE(StartConcat) -> 310 CONSTANT("Caught crash: ") -> 310 CALL_PRIMITIVE(StringConcat(REF(class String))) -> 310 LOAD_LOCAL(value t) -> 310 CALL_METHOD java.lang.Throwable.toString (dynamic) -> 310 CALL_PRIMITIVE(StringConcat(REF(class String))) -> 310 CALL_PRIMITIVE(EndConcat) -> 310 CALL_METHOD scala.Predef.println (dynamic) -584c604 -< catch (Throwable) in ArrayBuffer(7, 9, 10) starting at: 6 ---- -> catch (Throwable) in ArrayBuffer(7, 9, 10, 11) starting at: 6 -587c607 -< catch (Throwable) in ArrayBuffer(4, 6, 7, 9, 10) starting at: 3 ---- -> catch (Throwable) in ArrayBuffer(4, 6, 7, 9, 10, 11, 12) starting at: 3 -619c639 -< blocks: [1,3,4,5,6,8,9] ---- -> blocks: [1,3,4,5,6,8,9,10,11] -643c663,669 -< 78 THROW(IllegalArgumentException) ---- -> ? STORE_LOCAL(value e) -> ? JUMP 10 -> -> 10: -> 81 LOAD_LOCAL(value e) -> ? STORE_LOCAL(variable exc1) -> ? JUMP 11 -669c695,696 -< 81 THROW(Exception) ---- -> ? STORE_LOCAL(variable exc1) -> ? JUMP 11 -685a713,725 -> 11: -> 83 LOAD_MODULE object Predef -> 83 CONSTANT("finally") -> 83 CALL_METHOD scala.Predef.println (dynamic) -> 84 LOAD_LOCAL(variable result) -> 84 CONSTANT(1) -> 84 CALL_PRIMITIVE(Arithmetic(SUB,INT)) -> 84 CONSTANT(2) -> 84 CALL_PRIMITIVE(Arithmetic(DIV,INT)) -> 84 STORE_LOCAL(variable result) -> 84 LOAD_LOCAL(variable exc1) -> 84 THROW(Throwable) -> -691c731 -< catch (<none>) in ArrayBuffer(4, 5, 6, 8) starting at: 3 ---- -> catch (<none>) in ArrayBuffer(4, 5, 6, 8, 10) starting at: 3 -715c755 -< locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x ---- -> locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x -717c757 -< blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24] ---- -> blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24,25,26,27] -741c781,788 -< 172 THROW(MyException) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 25 -> -> 25: -> 170 LOAD_LOCAL(value ex6) -> 170 STORE_LOCAL(value x4) -> 170 SCOPE_ENTER value x4 -> 170 JUMP 14 -781,784d827 -< 175 LOAD_LOCAL(value x5) -< 175 CALL_METHOD MyException.message (dynamic) -< 175 STORE_LOCAL(value message) -< 175 SCOPE_ENTER value message -786c829,830 -< 176 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 176 CALL_METHOD MyException.message (dynamic) -790c834,835 -< 177 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 177 CALL_METHOD MyException.message (dynamic) -792c837,838 -< 177 THROW(MyException) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 26 -796c842,843 -< 170 THROW(Throwable) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 26 -805a853,858 -> 26: -> 169 LOAD_LOCAL(value ex6) -> 169 STORE_LOCAL(value x4) -> 169 SCOPE_ENTER value x4 -> 169 JUMP 5 -> -816,819d868 -< 180 LOAD_LOCAL(value x5) -< 180 CALL_METHOD MyException.message (dynamic) -< 180 STORE_LOCAL(value message) -< 180 SCOPE_ENTER value message -821c870,871 -< 181 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 181 CALL_METHOD MyException.message (dynamic) -825c875,876 -< 182 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 182 CALL_METHOD MyException.message (dynamic) -827c878,879 -< 182 THROW(MyException) ---- -> ? STORE_LOCAL(variable exc2) -> ? JUMP 27 -831c883,884 -< 169 THROW(Throwable) ---- -> ? STORE_LOCAL(variable exc2) -> ? JUMP 27 -847a901,913 -> 27: -> 184 LOAD_MODULE object Predef -> 184 CONSTANT("finally") -> 184 CALL_METHOD scala.Predef.println (dynamic) -> 185 LOAD_LOCAL(variable result) -> 185 CONSTANT(1) -> 185 CALL_PRIMITIVE(Arithmetic(SUB,INT)) -> 185 CONSTANT(2) -> 185 CALL_PRIMITIVE(Arithmetic(DIV,INT)) -> 185 STORE_LOCAL(variable result) -> 185 LOAD_LOCAL(variable exc2) -> 185 THROW(Throwable) -> -853c919 -< catch (Throwable) in ArrayBuffer(13, 14, 15, 18, 20, 21, 23) starting at: 4 ---- -> catch (Throwable) in ArrayBuffer(13, 14, 15, 18, 20, 21, 23, 25) starting at: 4 -856c922 -< catch (<none>) in ArrayBuffer(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23) starting at: 3 ---- -> catch (<none>) in ArrayBuffer(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23, 25, 26) starting at: 3 -880c946 -< locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x ---- -> locals: value args, variable result, value e, value ex6, value x4, value x5, value x -882c948 -< blocks: [1,2,3,6,7,8,11,13,14,16] ---- -> blocks: [1,2,3,6,7,8,11,13,14,16,17] -906c972,979 -< 124 THROW(MyException) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 17 -> -> 17: -> 122 LOAD_LOCAL(value ex6) -> 122 STORE_LOCAL(value x4) -> 122 SCOPE_ENTER value x4 -> 122 JUMP 7 -931,934d1003 -< 127 LOAD_LOCAL(value x5) -< 127 CALL_METHOD MyException.message (dynamic) -< 127 STORE_LOCAL(value message) -< 127 SCOPE_ENTER value message -936c1005,1006 -< 127 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 127 CALL_METHOD MyException.message (dynamic) -965c1035 -< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16) starting at: 3 ---- -> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3 -989c1059 -< locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e ---- -> locals: value args, variable result, value ex6, value x4, value x5, value x, value e -991c1061 -< blocks: [1,2,3,4,5,8,12,13,14,16] ---- -> blocks: [1,2,3,5,8,12,13,14,16,17] -1015c1085,1094 -< 148 THROW(MyException) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 17 -> -> 17: -> 145 LOAD_LOCAL(value ex6) -> 145 STORE_LOCAL(value x4) -> 145 SCOPE_ENTER value x4 -> 154 LOAD_LOCAL(value x4) -> 154 IS_INSTANCE REF(class MyException) -> 154 CZJUMP (BOOL)NE ? 5 : 8 -1036,1038d1114 -< 145 JUMP 4 -< -< 4: -1048,1051d1123 -< 154 LOAD_LOCAL(value x5) -< 154 CALL_METHOD MyException.message (dynamic) -< 154 STORE_LOCAL(value message) -< 154 SCOPE_ENTER value message -1053c1125,1126 -< 154 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 154 CALL_METHOD MyException.message (dynamic) -1270c1343 -< blocks: [1,2,3,4,5,7] ---- -> blocks: [1,2,3,4,5,7,8] -1294c1367,1374 -< 38 THROW(IllegalArgumentException) ---- -> ? STORE_LOCAL(value e) -> ? JUMP 8 -> -> 8: -> 42 LOAD_MODULE object Predef -> 42 CONSTANT("IllegalArgumentException") -> 42 CALL_METHOD scala.Predef.println (dynamic) -> 42 JUMP 2 -1341c1421 -< locals: value args, variable result, value ex6, value x4, value x5, value message, value x ---- -> locals: value args, variable result, value ex6, value x4, value x5, value x -1343c1423 -< blocks: [1,2,3,4,5,8,10,11,13,14,16] ---- -> blocks: [1,2,3,5,8,10,11,13,14,16,17] -1367c1447,1448 -< 203 THROW(MyException) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 17 -1387c1468,1477 -< 209 THROW(MyException) ---- -> ? STORE_LOCAL(value ex6) -> ? JUMP 17 -> -> 17: -> 200 LOAD_LOCAL(value ex6) -> 200 STORE_LOCAL(value x4) -> 200 SCOPE_ENTER value x4 -> 212 LOAD_LOCAL(value x4) -> 212 IS_INSTANCE REF(class MyException) -> 212 CZJUMP (BOOL)NE ? 5 : 8 -1400,1402d1489 -< 200 JUMP 4 -< -< 4: -1412,1415d1498 -< 212 LOAD_LOCAL(value x5) -< 212 CALL_METHOD MyException.message (dynamic) -< 212 STORE_LOCAL(value message) -< 212 SCOPE_ENTER value message -1417c1500,1501 -< 213 LOAD_LOCAL(value message) ---- -> ? LOAD_LOCAL(value x5) -> 213 CALL_METHOD MyException.message (dynamic) -1461c1545 -< blocks: [1,2,3,4,5,7] ---- -> blocks: [1,2,3,4,5,7,8] -1485c1569,1570 -< 58 THROW(IllegalArgumentException) ---- -> ? STORE_LOCAL(value e) -> ? JUMP 8 -1486a1572,1577 -> 8: -> 62 LOAD_MODULE object Predef -> 62 CONSTANT("RuntimeException") -> 62 CALL_METHOD scala.Predef.println (dynamic) -> 62 JUMP 2 -> -1534c1625 -< blocks: [1,3,4] ---- -> blocks: [1,3,4,5] -1554c1645,1650 -< 229 THROW(MyException) ---- -> ? JUMP 5 -> -> 5: -> ? LOAD_LOCAL(variable monitor1) -> 228 MONITOR_EXIT -> 228 THROW(Throwable) -1560c1656 -< ? THROW(Throwable) ---- -> 228 THROW(Throwable) -1588c1684 -< locals: value args, variable result, variable monitor2, variable monitorResult1 ---- -> locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 -1590c1686 -< blocks: [1,3,4] ---- -> blocks: [1,3,4,5] -1613c1709,1717 -< 245 THROW(MyException) ---- -> ? STORE_LOCAL(value exception$1) -> ? DROP ConcatClass -> ? LOAD_LOCAL(value exception$1) -> ? JUMP 5 -> -> 5: -> ? LOAD_LOCAL(variable monitor2) -> 244 MONITOR_EXIT -> 244 THROW(Throwable) -1619c1723 -< ? THROW(Throwable) ---- -> 244 THROW(Throwable) - +--- a ++++ b +@@ -171,5 +171,5 @@ + def productElement(x$1: Int (INT)): Object { +- locals: value x$1, value x1 ++ locals: value x$1, value x1, variable boxed1 + startBlock: 1 +- blocks: [1,2,3,4] ++ blocks: [1,3,4] + +@@ -186,2 +186,4 @@ + 92 LOAD_LOCAL(value x$1) ++ 92 STORE_LOCAL(variable boxed1) ++ 92 LOAD_LOCAL(variable boxed1) + 92 BOX INT +@@ -194,5 +196,2 @@ + 92 CALL_METHOD MyException.message (dynamic) +- 92 JUMP 2 +- +- 2: + 92 RETURN(REF(class Object)) +@@ -246,3 +245,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,5,6,7,8,11,12,13,14,15,16,17,18] ++ blocks: [1,2,3,4,5,6,8,11,12,13,14,15,16,17,18] + +@@ -257,5 +256,2 @@ + 92 SCOPE_ENTER value x1 +- 92 JUMP 7 +- +- 7: + 92 LOAD_LOCAL(value x1) +@@ -390,5 +386,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,2,3,4,5,8,10,11,13] ++ blocks: [1,2,3,5,8,10,11,13,14] + +@@ -416,4 +412,13 @@ + 103 CALL_METHOD MyException.<init> (static-instance) +- 103 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 14 + ++ 14: ++ 101 LOAD_LOCAL(value ex6) ++ 101 STORE_LOCAL(value x4) ++ 101 SCOPE_ENTER value x4 ++ 106 LOAD_LOCAL(value x4) ++ 106 IS_INSTANCE REF(class MyException) ++ 106 CZJUMP (BOOL)NE ? 5 : 8 ++ + 13: +@@ -429,5 +434,2 @@ + 101 SCOPE_ENTER value x4 +- 101 JUMP 4 +- +- 4: + 106 LOAD_LOCAL(value x4) +@@ -441,8 +443,5 @@ + 106 SCOPE_ENTER value x5 +- 106 LOAD_LOCAL(value x5) +- 106 CALL_METHOD MyException.message (dynamic) +- 106 STORE_LOCAL(value message) +- 106 SCOPE_ENTER value message + 106 LOAD_MODULE object Predef +- 106 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 106 CALL_METHOD MyException.message (dynamic) + 106 CALL_METHOD scala.Predef.println (dynamic) +@@ -518,3 +517,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,6,7,9,10] ++ blocks: [1,3,4,6,7,9,10,11,12,13] + +@@ -547,4 +546,9 @@ + 306 CALL_METHOD MyException.<init> (static-instance) +- 306 THROW(MyException) ++ ? JUMP 11 + ++ 11: ++ ? LOAD_LOCAL(variable monitor4) ++ 305 MONITOR_EXIT ++ ? JUMP 12 ++ + 9: +@@ -553,3 +557,3 @@ + 305 MONITOR_EXIT +- ? THROW(Throwable) ++ ? JUMP 12 + +@@ -559,4 +563,11 @@ + 304 MONITOR_EXIT +- ? THROW(Throwable) ++ ? STORE_LOCAL(value t) ++ ? JUMP 13 + ++ 12: ++ ? LOAD_LOCAL(variable monitor3) ++ 304 MONITOR_EXIT ++ ? STORE_LOCAL(value t) ++ ? JUMP 13 ++ + 3: +@@ -573,5 +584,14 @@ + 310 CALL_METHOD scala.Predef.println (dynamic) +- 310 JUMP 2 ++ 300 RETURN(UNIT) + +- 2: ++ 13: ++ 310 LOAD_MODULE object Predef ++ 310 CALL_PRIMITIVE(StartConcat) ++ 310 CONSTANT("Caught crash: ") ++ 310 CALL_PRIMITIVE(StringConcat(REF(class String))) ++ 310 LOAD_LOCAL(value t) ++ 310 CALL_METHOD java.lang.Throwable.toString (dynamic) ++ 310 CALL_PRIMITIVE(StringConcat(REF(class String))) ++ 310 CALL_PRIMITIVE(EndConcat) ++ 310 CALL_METHOD scala.Predef.println (dynamic) + 300 RETURN(UNIT) +@@ -583,6 +603,6 @@ + with finalizer: null +- catch (Throwable) in ArrayBuffer(7, 9, 10) starting at: 6 ++ catch (Throwable) in ArrayBuffer(7, 9, 10, 11) starting at: 6 + consisting of blocks: List(6) + with finalizer: null +- catch (Throwable) in ArrayBuffer(4, 6, 7, 9, 10) starting at: 3 ++ catch (Throwable) in ArrayBuffer(4, 6, 7, 9, 10, 11, 12) starting at: 3 + consisting of blocks: List(3) +@@ -618,3 +638,3 @@ + startBlock: 1 +- blocks: [1,3,4,5,6,8,9] ++ blocks: [1,3,4,5,6,8,9,10,11] + +@@ -642,4 +662,10 @@ + 78 CALL_METHOD java.lang.IllegalArgumentException.<init> (static-instance) +- 78 THROW(IllegalArgumentException) ++ ? STORE_LOCAL(value e) ++ ? JUMP 10 + ++ 10: ++ 81 LOAD_LOCAL(value e) ++ ? STORE_LOCAL(variable exc1) ++ ? JUMP 11 ++ + 8: +@@ -668,3 +694,4 @@ + 81 LOAD_LOCAL(value e) +- 81 THROW(Exception) ++ ? STORE_LOCAL(variable exc1) ++ ? JUMP 11 + +@@ -685,2 +712,15 @@ + ++ 11: ++ 83 LOAD_MODULE object Predef ++ 83 CONSTANT("finally") ++ 83 CALL_METHOD scala.Predef.println (dynamic) ++ 84 LOAD_LOCAL(variable result) ++ 84 CONSTANT(1) ++ 84 CALL_PRIMITIVE(Arithmetic(SUB,INT)) ++ 84 CONSTANT(2) ++ 84 CALL_PRIMITIVE(Arithmetic(DIV,INT)) ++ 84 STORE_LOCAL(variable result) ++ 84 LOAD_LOCAL(variable exc1) ++ 84 THROW(Throwable) ++ + } +@@ -690,3 +730,3 @@ + with finalizer: null +- catch (<none>) in ArrayBuffer(4, 5, 6, 8) starting at: 3 ++ catch (<none>) in ArrayBuffer(4, 5, 6, 8, 10) starting at: 3 + consisting of blocks: List(3) +@@ -714,5 +754,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value message, value x, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value ex6, variable exc2, value x4, value x5, value x, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24] ++ blocks: [1,3,4,5,6,9,13,14,15,18,20,21,23,24,25,26,27] + +@@ -740,4 +780,11 @@ + 172 CALL_METHOD MyException.<init> (static-instance) +- 172 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 25 + ++ 25: ++ 170 LOAD_LOCAL(value ex6) ++ 170 STORE_LOCAL(value x4) ++ 170 SCOPE_ENTER value x4 ++ 170 JUMP 14 ++ + 23: +@@ -780,8 +827,5 @@ + 175 SCOPE_ENTER value x5 +- 175 LOAD_LOCAL(value x5) +- 175 CALL_METHOD MyException.message (dynamic) +- 175 STORE_LOCAL(value message) +- 175 SCOPE_ENTER value message + 176 LOAD_MODULE object Predef +- 176 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 176 CALL_METHOD MyException.message (dynamic) + 176 CALL_METHOD scala.Predef.println (dynamic) +@@ -789,5 +833,7 @@ + 177 DUP(REF(class MyException)) +- 177 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 177 CALL_METHOD MyException.message (dynamic) + 177 CALL_METHOD MyException.<init> (static-instance) +- 177 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 26 + +@@ -795,3 +841,4 @@ + 170 LOAD_LOCAL(value ex6) +- 170 THROW(Throwable) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 26 + +@@ -805,2 +852,8 @@ + ++ 26: ++ 169 LOAD_LOCAL(value ex6) ++ 169 STORE_LOCAL(value x4) ++ 169 SCOPE_ENTER value x4 ++ 169 JUMP 5 ++ + 5: +@@ -815,8 +868,5 @@ + 180 SCOPE_ENTER value x5 +- 180 LOAD_LOCAL(value x5) +- 180 CALL_METHOD MyException.message (dynamic) +- 180 STORE_LOCAL(value message) +- 180 SCOPE_ENTER value message + 181 LOAD_MODULE object Predef +- 181 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 181 CALL_METHOD MyException.message (dynamic) + 181 CALL_METHOD scala.Predef.println (dynamic) +@@ -824,5 +874,7 @@ + 182 DUP(REF(class MyException)) +- 182 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 182 CALL_METHOD MyException.message (dynamic) + 182 CALL_METHOD MyException.<init> (static-instance) +- 182 THROW(MyException) ++ ? STORE_LOCAL(variable exc2) ++ ? JUMP 27 + +@@ -830,3 +882,4 @@ + 169 LOAD_LOCAL(value ex6) +- 169 THROW(Throwable) ++ ? STORE_LOCAL(variable exc2) ++ ? JUMP 27 + +@@ -847,2 +900,15 @@ + ++ 27: ++ 184 LOAD_MODULE object Predef ++ 184 CONSTANT("finally") ++ 184 CALL_METHOD scala.Predef.println (dynamic) ++ 185 LOAD_LOCAL(variable result) ++ 185 CONSTANT(1) ++ 185 CALL_PRIMITIVE(Arithmetic(SUB,INT)) ++ 185 CONSTANT(2) ++ 185 CALL_PRIMITIVE(Arithmetic(DIV,INT)) ++ 185 STORE_LOCAL(variable result) ++ 185 LOAD_LOCAL(variable exc2) ++ 185 THROW(Throwable) ++ + } +@@ -852,6 +918,6 @@ + with finalizer: null +- catch (Throwable) in ArrayBuffer(13, 14, 15, 18, 20, 21, 23) starting at: 4 ++ catch (Throwable) in ArrayBuffer(13, 14, 15, 18, 20, 21, 23, 25) starting at: 4 + consisting of blocks: List(9, 8, 6, 5, 4) + with finalizer: null +- catch (<none>) in ArrayBuffer(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23) starting at: 3 ++ catch (<none>) in ArrayBuffer(4, 5, 6, 9, 13, 14, 15, 18, 20, 21, 23, 25, 26) starting at: 3 + consisting of blocks: List(3) +@@ -879,5 +945,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value e, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value e, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,2,3,6,7,8,11,13,14,16] ++ blocks: [1,2,3,6,7,8,11,13,14,16,17] + +@@ -905,4 +971,11 @@ + 124 CALL_METHOD MyException.<init> (static-instance) +- 124 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + ++ 17: ++ 122 LOAD_LOCAL(value ex6) ++ 122 STORE_LOCAL(value x4) ++ 122 SCOPE_ENTER value x4 ++ 122 JUMP 7 ++ + 16: +@@ -930,8 +1003,5 @@ + 127 SCOPE_ENTER value x5 +- 127 LOAD_LOCAL(value x5) +- 127 CALL_METHOD MyException.message (dynamic) +- 127 STORE_LOCAL(value message) +- 127 SCOPE_ENTER value message + 127 LOAD_MODULE object Predef +- 127 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 127 CALL_METHOD MyException.message (dynamic) + 127 CALL_METHOD scala.Predef.println (dynamic) +@@ -964,3 +1034,3 @@ + with finalizer: null +- catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16) starting at: 3 ++ catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 11, 13, 14, 16, 17) starting at: 3 + consisting of blocks: List(3) +@@ -988,5 +1058,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, value x4, value x5, value message, value x, value e ++ locals: value args, variable result, value ex6, value x4, value x5, value x, value e + startBlock: 1 +- blocks: [1,2,3,4,5,8,12,13,14,16] ++ blocks: [1,2,3,5,8,12,13,14,16,17] + +@@ -1014,4 +1084,13 @@ + 148 CALL_METHOD MyException.<init> (static-instance) +- 148 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + ++ 17: ++ 145 LOAD_LOCAL(value ex6) ++ 145 STORE_LOCAL(value x4) ++ 145 SCOPE_ENTER value x4 ++ 154 LOAD_LOCAL(value x4) ++ 154 IS_INSTANCE REF(class MyException) ++ 154 CZJUMP (BOOL)NE ? 5 : 8 ++ + 16: +@@ -1035,5 +1114,2 @@ + 145 SCOPE_ENTER value x4 +- 145 JUMP 4 +- +- 4: + 154 LOAD_LOCAL(value x4) +@@ -1047,8 +1123,5 @@ + 154 SCOPE_ENTER value x5 +- 154 LOAD_LOCAL(value x5) +- 154 CALL_METHOD MyException.message (dynamic) +- 154 STORE_LOCAL(value message) +- 154 SCOPE_ENTER value message + 154 LOAD_MODULE object Predef +- 154 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 154 CALL_METHOD MyException.message (dynamic) + 154 CALL_METHOD scala.Predef.println (dynamic) +@@ -1269,3 +1342,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,5,7] ++ blocks: [1,2,3,4,5,7,8] + +@@ -1293,4 +1366,11 @@ + 38 CALL_METHOD java.lang.IllegalArgumentException.<init> (static-instance) +- 38 THROW(IllegalArgumentException) ++ ? STORE_LOCAL(value e) ++ ? JUMP 8 + ++ 8: ++ 42 LOAD_MODULE object Predef ++ 42 CONSTANT("IllegalArgumentException") ++ 42 CALL_METHOD scala.Predef.println (dynamic) ++ 42 JUMP 2 ++ + 7: +@@ -1340,5 +1420,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, value ex6, value x4, value x5, value message, value x ++ locals: value args, variable result, value ex6, value x4, value x5, value x + startBlock: 1 +- blocks: [1,2,3,4,5,8,10,11,13,14,16] ++ blocks: [1,2,3,5,8,10,11,13,14,16,17] + +@@ -1366,3 +1446,4 @@ + 203 CALL_METHOD MyException.<init> (static-instance) +- 203 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + +@@ -1386,4 +1467,13 @@ + 209 CALL_METHOD MyException.<init> (static-instance) +- 209 THROW(MyException) ++ ? STORE_LOCAL(value ex6) ++ ? JUMP 17 + ++ 17: ++ 200 LOAD_LOCAL(value ex6) ++ 200 STORE_LOCAL(value x4) ++ 200 SCOPE_ENTER value x4 ++ 212 LOAD_LOCAL(value x4) ++ 212 IS_INSTANCE REF(class MyException) ++ 212 CZJUMP (BOOL)NE ? 5 : 8 ++ + 16: +@@ -1399,5 +1489,2 @@ + 200 SCOPE_ENTER value x4 +- 200 JUMP 4 +- +- 4: + 212 LOAD_LOCAL(value x4) +@@ -1411,8 +1498,5 @@ + 212 SCOPE_ENTER value x5 +- 212 LOAD_LOCAL(value x5) +- 212 CALL_METHOD MyException.message (dynamic) +- 212 STORE_LOCAL(value message) +- 212 SCOPE_ENTER value message + 213 LOAD_MODULE object Predef +- 213 LOAD_LOCAL(value message) ++ ? LOAD_LOCAL(value x5) ++ 213 CALL_METHOD MyException.message (dynamic) + 213 CALL_METHOD scala.Predef.println (dynamic) +@@ -1460,3 +1544,3 @@ + startBlock: 1 +- blocks: [1,2,3,4,5,7] ++ blocks: [1,2,3,4,5,7,8] + +@@ -1484,4 +1568,11 @@ + 58 CALL_METHOD java.lang.IllegalArgumentException.<init> (static-instance) +- 58 THROW(IllegalArgumentException) ++ ? STORE_LOCAL(value e) ++ ? JUMP 8 + ++ 8: ++ 62 LOAD_MODULE object Predef ++ 62 CONSTANT("RuntimeException") ++ 62 CALL_METHOD scala.Predef.println (dynamic) ++ 62 JUMP 2 ++ + 7: +@@ -1533,3 +1624,3 @@ + startBlock: 1 +- blocks: [1,3,4] ++ blocks: [1,3,4,5] + +@@ -1553,4 +1644,9 @@ + 229 CALL_METHOD MyException.<init> (static-instance) +- 229 THROW(MyException) ++ ? JUMP 5 + ++ 5: ++ ? LOAD_LOCAL(variable monitor1) ++ 228 MONITOR_EXIT ++ 228 THROW(Throwable) ++ + 3: +@@ -1559,3 +1655,3 @@ + 228 MONITOR_EXIT +- ? THROW(Throwable) ++ 228 THROW(Throwable) + +@@ -1587,5 +1683,5 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, variable result, variable monitor2, variable monitorResult1 ++ locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1 + startBlock: 1 +- blocks: [1,3,4] ++ blocks: [1,3,4,5] + +@@ -1612,4 +1708,12 @@ + 245 CALL_METHOD MyException.<init> (static-instance) +- 245 THROW(MyException) ++ ? STORE_LOCAL(value exception$1) ++ ? DROP ConcatClass ++ ? LOAD_LOCAL(value exception$1) ++ ? JUMP 5 + ++ 5: ++ ? LOAD_LOCAL(variable monitor2) ++ 244 MONITOR_EXIT ++ 244 THROW(Throwable) ++ + 3: +@@ -1618,3 +1722,3 @@ + 244 MONITOR_EXIT +- ? THROW(Throwable) ++ 244 THROW(Throwable) + diff --git a/test/files/run/iterator-concat.check b/test/files/run/iterator-concat.check new file mode 100644 index 0000000000..23835b07ae --- /dev/null +++ b/test/files/run/iterator-concat.check @@ -0,0 +1,4 @@ +100 +1000 +10000 +100000 diff --git a/test/files/run/iterator-concat.scala b/test/files/run/iterator-concat.scala new file mode 100644 index 0000000000..f11363410f --- /dev/null +++ b/test/files/run/iterator-concat.scala @@ -0,0 +1,15 @@ +object Test { + // Create `size` Function0s, each of which evaluates to an Iterator + // which produces 1. Then fold them over ++ to get a single iterator, + // which should sum to "size". + def mk(size: Int): Iterator[Int] = { + val closures = (1 to size).toList.map(x => (() => Iterator(1))) + closures.foldLeft(Iterator.empty: Iterator[Int])((res, f) => res ++ f()) + } + def main(args: Array[String]): Unit = { + println(mk(100).sum) + println(mk(1000).sum) + println(mk(10000).sum) + println(mk(100000).sum) + } +} diff --git a/test/files/run/outertest.scala b/test/files/run/outertest.scala index 3cc96afa5b..fa0443f669 100644 --- a/test/files/run/outertest.scala +++ b/test/files/run/outertest.scala @@ -1,26 +1,57 @@ // A test for the case where the outer field of class B#J should be eliminated. -// You can verify this by running a javap on B.J + +import reflect.ClassTag + abstract class A { + abstract class I - abstract class I { + val foo = this +} +class B extends A { + class J extends I { + val bar = foo } - val foo = "foo" + type II = I + class K extends II { + val bar = foo + } + class L extends (I @annotation.tailrec) { + val bar = foo + } } -class B extends A { - class J extends I { +class C extends A { + val c: C = this + + class M extends c.I { val bar = foo } - } -object Test extends App { +object Test extends App { val b = new B - assert((new b.J).bar == b.foo) + val c0 = new C + val c = new { override val c = c0 } with C + + assert((new b.J).bar eq b) + assert((new b.K).bar eq b) + assert((new b.L).bar eq b) + assert((new c.M).bar eq c) + + def checkOuterFields[C: ClassTag](expected: Int) { + val cls = implicitly[ClassTag[C]].runtimeClass + val outerFields = cls.getDeclaredFields().filter(_.getName.contains("$outer")) + assert(outerFields.size == expected, outerFields.map(_.getName)) + } + checkOuterFields[A#I](1) // the base class must have the $outer pointer + checkOuterFields[B#J](0) // reuse parent class' $outer pointer + checkOuterFields[B#K](0) // ... through an alias + checkOuterFields[B#L](0) // ... through the annotated type + checkOuterFields[C#M](1) // different prefix, can't share. } diff --git a/test/files/run/reify_this.scala b/test/files/run/reify_this.scala index ecbf394bba..c385da6360 100644 --- a/test/files/run/reify_this.scala +++ b/test/files/run/reify_this.scala @@ -1,11 +1,11 @@ import scala.reflect.runtime.universe._ import scala.tools.reflect.Eval -trait Eval { +trait Transvaal { def eval(tree: Expr[_]) = tree.eval } -object Test extends App with Eval { +object Test extends App with Transvaal { // select a value from package eval(reify{println("foo")}) eval(reify{println((new Object).toString == (new Object).toString)}) @@ -17,4 +17,4 @@ object Test extends App with Eval { // select a value from module val x = 2 eval(reify{println(x)}) -}
\ No newline at end of file +} diff --git a/test/files/run/t3994.scala b/test/files/run/t3994.scala new file mode 100644 index 0000000000..0ee1d9d966 --- /dev/null +++ b/test/files/run/t3994.scala @@ -0,0 +1,20 @@ +trait T { + trait Default { def foo = this } + object Default extends Default +} + +class Crash { // if you change this to a `trait` it keeps failing, though if it is an `object` it compiles just fine! + class Element + + /* declare this as a class, and the crash goes away */ + trait ElementOrdering extends Ordering[Element] { + def compare(a: Element, b: Element): Int = 0 + } + + implicit object ElementOrdering extends ElementOrdering +} + +object Test extends App { + (new T {}).Default + (new Crash).ElementOrdering +} diff --git a/test/files/run/t5699.check b/test/files/run/t5699.check new file mode 100755 index 0000000000..df19644ae6 --- /dev/null +++ b/test/files/run/t5699.check @@ -0,0 +1,11 @@ +[[syntax trees at end of parser]] // annodef.java +package <empty> { + object MyAnnotation extends { + def <init>() = _ + }; + class MyAnnotation extends scala.annotation.Annotation with _root_.java.lang.annotation.Annotation with scala.annotation.ClassfileAnnotation { + def <init>() = _; + def value(): String + } +} + diff --git a/test/files/run/t5699.scala b/test/files/run/t5699.scala new file mode 100755 index 0000000000..5cef67e3b1 --- /dev/null +++ b/test/files/run/t5699.scala @@ -0,0 +1,24 @@ +import scala.tools.partest.DirectTest +import scala.tools.nsc.util.BatchSourceFile + +object Test extends DirectTest { + // Java code + override def code = """ + |public @interface MyAnnotation { String value(); } + """.stripMargin + + override def extraSettings: String = "-usejavacp -Ystop-after:typer -Xprint:parser" + + override def show(): Unit = { + // redirect err to out, for logging + val prevErr = System.err + System.setErr(System.out) + compile() + System.setErr(prevErr) + } + + override def newSources(sourceCodes: String*) = { + assert(sourceCodes.size == 1) + List(new BatchSourceFile("annodef.java", sourceCodes(0))) + } +} diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check index 67c30e35c6..2ec639fce2 100644 --- a/test/files/run/t6028.check +++ b/test/files/run/t6028.check @@ -1,7 +1,7 @@ [[syntax trees at end of lambdalift]] // newSource1 package <empty> { class T extends Object { - <paramaccessor> val T$$classParam: Int = _; + <paramaccessor> val classParam: Int = _; def <init>(classParam: Int): T = { T.super.<init>(); () @@ -30,15 +30,15 @@ package <empty> { () }; final def apply(): Int = $anonfun$foo$1.this.apply$mcI$sp(); - <specialized> def apply$mcI$sp(): Int = $anonfun$foo$1.this.$outer.T$$classParam.+($anonfun$foo$1.this.$outer.field()).+($anonfun$foo$1.this.methodParam$1).+($anonfun$foo$1.this.methodLocal$1); + <specialized> def apply$mcI$sp(): Int = $anonfun$foo$1.this.$outer.classParam.+($anonfun$foo$1.this.$outer.field()).+($anonfun$foo$1.this.methodParam$1).+($anonfun$foo$1.this.methodLocal$1); <synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _; - <synthetic> <stable> <artifact> def T$$anonfun$$$outer(): T = $anonfun$foo$1.this.$outer; + <synthetic> <stable> <artifact> def $outer(): T = $anonfun$foo$1.this.$outer; final <bridge> <artifact> def apply(): Object = scala.Int.box($anonfun$foo$1.this.apply()); <synthetic> <paramaccessor> private[this] val methodParam$1: Int = _; <synthetic> <paramaccessor> private[this] val methodLocal$1: Int = _ }; abstract trait MethodLocalTrait$1 extends Object { - <synthetic> <stable> <artifact> def T$MethodLocalTrait$$$outer(): T + <synthetic> <stable> <artifact> def $outer(): T }; object MethodLocalObject$2 extends Object with T#MethodLocalTrait$1 { def <init>($outer: T, barParam$1: Int): T#MethodLocalObject$2.type = { @@ -47,8 +47,8 @@ package <empty> { () }; <synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _; - <synthetic> <stable> <artifact> def T$MethodLocalObject$$$outer(): T = MethodLocalObject$2.this.$outer; - <synthetic> <stable> <artifact> def T$MethodLocalTrait$$$outer(): T = MethodLocalObject$2.this.$outer + <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer; + <synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer }; final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = { MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1); @@ -70,7 +70,7 @@ package <empty> { $anonfun$tryy$1.this.tryyLocal$1.elem = $anonfun$tryy$1.this.tryyParam$1 } finally (); <synthetic> <paramaccessor> <artifact> private[this] val $outer: T = _; - <synthetic> <stable> <artifact> def T$$anonfun$$$outer(): T = $anonfun$tryy$1.this.$outer; + <synthetic> <stable> <artifact> def $outer(): T = $anonfun$tryy$1.this.$outer; final <bridge> <artifact> def apply(): Object = { $anonfun$tryy$1.this.apply(); scala.runtime.BoxedUnit.UNIT diff --git a/test/files/run/t6146b.check b/test/files/run/t6146b.check index b664d1152a..49ff70697e 100644 --- a/test/files/run/t6146b.check +++ b/test/files/run/t6146b.check @@ -37,8 +37,15 @@ memType: (sub: u.Type, scrut: u.Type)u.Type scala> -scala> memType(S1, fTpe) -res0: u.Type = O.X.S1.type +scala> val mt1 = memType(S1, fTpe) +mt1: u.Type = O.X.S1.type + +scala> global.typeDeconstruct.show(mt1) +res0: String = +TypeRef( + pre = SingleType(pre = ThisType(object O), object X) + TypeSymbol(class S1 extends C.this.F[T]) +) scala> memType(S2, fTpe) res1: u.Type = O.S2 diff --git a/test/files/run/t6146b.scala b/test/files/run/t6146b.scala index adcd40d2ee..e63709aa9d 100644 --- a/test/files/run/t6146b.scala +++ b/test/files/run/t6146b.scala @@ -31,7 +31,8 @@ val fTpe = typeOf[O.type].decl(newTermName("foo")).paramss.head.head.tpe def memType(sub: Type, scrut: Type): Type = nestedMemberType(sub.typeSymbol, scrut.prefix, scrut.typeSymbol.owner) -memType(S1, fTpe) +val mt1 = memType(S1, fTpe) +global.typeDeconstruct.show(mt1) memType(S2, fTpe) memType(S3, fTpe) memType(S4, fTpe) diff --git a/test/files/run/t6168/Context.java b/test/files/run/t6168/Context.java new file mode 100644 index 0000000000..e527844563 --- /dev/null +++ b/test/files/run/t6168/Context.java @@ -0,0 +1,34 @@ +public class Context<ParentType> { + private ParentType parent; + + public Context() {} + + public ParentType getParent() { + return parent; + } + + public void setParent(ParentType parent) { + this.parent = parent; + } + + public Field<Integer> intField() { + return new Field<Integer>() { + @Override + public Integer get() { + return 0; + } + + @Override + public ParentType set(Integer t) { + return parent; + } + }; + } + + public abstract class Field<T> { //Note this is a path dependent type + + public abstract T get(); + + public abstract ParentType set(T t); + } +}
\ No newline at end of file diff --git a/test/files/run/t6168/JavaTest.java b/test/files/run/t6168/JavaTest.java new file mode 100644 index 0000000000..94ae91661d --- /dev/null +++ b/test/files/run/t6168/JavaTest.java @@ -0,0 +1,8 @@ +public class JavaTest { + public static void main(String[] args) { + SomeClass a = new SomeClass(); + SomeClass2 a2 = new SomeClass2(); + SomeClass b = a.f.set(23).f.set(23); + SomeClass2 b2 = a2.f.set(23).f.set(23); + } +}
\ No newline at end of file diff --git a/test/files/run/t6168/SomeClass.java b/test/files/run/t6168/SomeClass.java new file mode 100644 index 0000000000..6f76b829bb --- /dev/null +++ b/test/files/run/t6168/SomeClass.java @@ -0,0 +1,14 @@ +public class SomeClass { + private final Context<SomeClass> context = new Context<SomeClass>(); + { + context.setParent(this); + } + + public final Context<SomeClass>.Field<Integer> f = context.intField(); + + public SomeClass() { + f.set(23).f.set(23); + } +} + + diff --git a/test/files/run/t6168/SomeClass2.java b/test/files/run/t6168/SomeClass2.java new file mode 100644 index 0000000000..b2c7a7540b --- /dev/null +++ b/test/files/run/t6168/SomeClass2.java @@ -0,0 +1,12 @@ +public class SomeClass2 { + private final Context<SomeClass2> context = new Context<SomeClass2>(); + { + context.setParent(this); + } + + public final Context<SomeClass2>.Field<Integer> f = context.intField(); + + public SomeClass2() { + f.set(23).f.set(23); + } +}
\ No newline at end of file diff --git a/test/files/run/t6168/main.scala b/test/files/run/t6168/main.scala new file mode 100644 index 0000000000..c7ad37830b --- /dev/null +++ b/test/files/run/t6168/main.scala @@ -0,0 +1,15 @@ + + +object Test extends App { + JavaTest.main(null) + + var a1 : SomeClass = new SomeClass + var a2 : SomeClass2 = new SomeClass2 + //import language.implicitConversions + //implicit def setParentType2SomeClass(x:Any) = x.asInstanceOf[SomeClass] + //implicit def setParentType2SomeClass2(x:Any) = x.asInstanceOf[SomeClass2] + //var b : SomeClass = a.f.set(23).asInstanceOf[SomeClass].f.set(23).asInstanceOf[SomeClass] + //var b2 : SomeClass2 = a2.f.set(23).asInstanceOf[SomeClass2].f.set(23).asInstanceOf[SomeClass2] + var b1 : SomeClass = a1.f.set(23).f.set(23) + var b2 : SomeClass2 = a2.f.set(23).f.set(23) +} diff --git a/test/files/run/t6168b/Context.java b/test/files/run/t6168b/Context.java new file mode 100644 index 0000000000..b3ea22126f --- /dev/null +++ b/test/files/run/t6168b/Context.java @@ -0,0 +1,34 @@ +public class Context<ParentType> { + private ParentType parent; + + public Context() {} + + public ParentType getParent() { + return parent; + } + + public void setParent(ParentType parent) { + this.parent = parent; + } + + public Field<Integer> intField() { + return new Field<Integer>() { + @Override + public Integer get() { + return 0; + } + + @Override + public ParentType set(Integer t) { + return parent; + } + }; + } + + public static abstract class Field<T> { + + public abstract T get(); + + public abstract Object set(T t); + } +}
\ No newline at end of file diff --git a/test/files/run/t6168b/JavaTest.java b/test/files/run/t6168b/JavaTest.java new file mode 100644 index 0000000000..a09fa0382d --- /dev/null +++ b/test/files/run/t6168b/JavaTest.java @@ -0,0 +1,6 @@ +public class JavaTest { + public static void main(String[] args) { + SomeClass a = new SomeClass(); + Object b = a.f.set(23); + } +}
\ No newline at end of file diff --git a/test/files/run/t6168b/SomeClass.java b/test/files/run/t6168b/SomeClass.java new file mode 100644 index 0000000000..566c55e1c5 --- /dev/null +++ b/test/files/run/t6168b/SomeClass.java @@ -0,0 +1,11 @@ +public class SomeClass { + private final Context<SomeClass> context = new Context<SomeClass>(); + { + context.setParent(this); + } + + public final Context.Field<Integer> f = context.intField(); + +} + + diff --git a/test/files/run/t6168b/main.scala b/test/files/run/t6168b/main.scala new file mode 100644 index 0000000000..187e9fe85e --- /dev/null +++ b/test/files/run/t6168b/main.scala @@ -0,0 +1,8 @@ + + +object Test extends App { + JavaTest.main(null) + + var a1 : SomeClass = new SomeClass + var b1 : Object = a1.f.set(23) +} diff --git a/test/files/run/t6223.scala b/test/files/run/t6223.scala index fb176e32e6..0996ea1c45 100644 --- a/test/files/run/t6223.scala +++ b/test/files/run/t6223.scala @@ -5,7 +5,7 @@ class Foo[@specialized(Int) A](a:A) { object Test { def main(args:Array[String]) { val f = new Foo(333) - val ms = f.getClass().getDeclaredMethods().sortBy(_.getName) - ms.foreach(m => println(m.getName)) + val ms = f.getClass().getDeclaredMethods().map(_.getName).sorted + ms.foreach(println) } } diff --git a/test/files/run/t6387.check b/test/files/run/t6387.check new file mode 100644 index 0000000000..83b33d238d --- /dev/null +++ b/test/files/run/t6387.check @@ -0,0 +1 @@ +1000 diff --git a/test/files/run/t6387.scala b/test/files/run/t6387.scala new file mode 100644 index 0000000000..bbebb5f511 --- /dev/null +++ b/test/files/run/t6387.scala @@ -0,0 +1,16 @@ +trait A { + def foo: Long +} + +object Test { + def a(): A = new A { + var foo: Long = 1000L + + val test = () => { + foo = 28 + } + } + def main(args: Array[String]) { + println(a().foo) + } +} diff --git a/test/files/run/t6715.scala b/test/files/run/t6715.scala new file mode 100644 index 0000000000..07ff34218a --- /dev/null +++ b/test/files/run/t6715.scala @@ -0,0 +1,15 @@ +import scala.reflect.runtime.universe._ + +class A { + def $$ = 1 + def $times = 1 +} + +object Test { + def main(args: Array[String]): Unit = { + val memberSet: Set[String] = typeOf[A].members.map{ _.toString }.toSet + assert(memberSet contains "method *") + assert(memberSet contains "method $$") + assert(! (memberSet contains "method")) + } +} diff --git a/test/files/run/t6725-1.check b/test/files/run/t6725-1.check new file mode 100644 index 0000000000..6ed281c757 --- /dev/null +++ b/test/files/run/t6725-1.check @@ -0,0 +1,2 @@ +1 +1 diff --git a/test/files/run/t6725-1.scala b/test/files/run/t6725-1.scala new file mode 100644 index 0000000000..a167ef8aa3 --- /dev/null +++ b/test/files/run/t6725-1.scala @@ -0,0 +1,5 @@ +object Test extends App { + val a = 1 + val s = f"$a%s%n$a%s" + println(s) +}
\ No newline at end of file diff --git a/test/files/run/t6725-2.check b/test/files/run/t6725-2.check new file mode 100644 index 0000000000..3496917ad5 --- /dev/null +++ b/test/files/run/t6725-2.check @@ -0,0 +1,8 @@ + + +aaaa + + +aaaa +aaaa +aaaa diff --git a/test/files/run/t6725-2.scala b/test/files/run/t6725-2.scala new file mode 100644 index 0000000000..e033cf5ea8 --- /dev/null +++ b/test/files/run/t6725-2.scala @@ -0,0 +1,6 @@ +object Test extends App { + println(f"%n") + println(f"aaaa%n") + println(f"%naaaa") + println(f"aaaa%naaaa") +}
\ No newline at end of file diff --git a/test/files/run/t6793.scala b/test/files/run/t6793.scala new file mode 100644 index 0000000000..0b1f1619af --- /dev/null +++ b/test/files/run/t6793.scala @@ -0,0 +1,9 @@ +package a { class C1(private[a] val v0: String) } +package b { class C2(v1: String) extends a.C1(v1) { def foo = v1 } } + +object Test extends App { + new b.C2("x") + + val c2Fields = classOf[b.C2].getDeclaredFields + assert(c2Fields.size == 1, c2Fields.map(_.getName).toList) +} diff --git a/test/files/run/t6793b.scala b/test/files/run/t6793b.scala new file mode 100644 index 0000000000..cb3f2fb2fa --- /dev/null +++ b/test/files/run/t6793b.scala @@ -0,0 +1,11 @@ +package a { + class C1(val v0: String) + class C2(v1: String) extends a.C1(v1) { def foo = v1 } +} + +object Test extends App { + new a.C2("x") + + val c2Fields = classOf[a.C2].getDeclaredFields + assert(c2Fields.isEmpty, c2Fields.map(_.getName).mkString(", ")) +} diff --git a/test/files/run/t6793c.scala b/test/files/run/t6793c.scala new file mode 100644 index 0000000000..e28c7c81a1 --- /dev/null +++ b/test/files/run/t6793c.scala @@ -0,0 +1,11 @@ +package a { + class C1(private[a] val v0: String) + class C2(v1: String) extends a.C1(v1) { def foo = v1 } +} + +object Test extends App { + new a.C2("x").foo + + val c2Fields = classOf[a.C2].getDeclaredFields + assert(c2Fields.isEmpty, c2Fields.map(_.getName).toList) +} diff --git a/test/files/run/t6900.scala b/test/files/run/t6900.scala new file mode 100644 index 0000000000..a29d388129 --- /dev/null +++ b/test/files/run/t6900.scala @@ -0,0 +1,36 @@ +import annotation.tailrec + +trait Universe { + type T <: AnyRef +} + +final class Bug { + var i = 1 + def stop() = { i -= 1; i < 0 } + // the alias bypasses the fast path in erasures InfoTransformer + // predicated on `TypeMap.noChangeToSymbols` + type Alias = Any + + @tailrec + // So we get two symbols for `universe`, the original on the ValDef + // and a clone in the MethodType of `f`. + def f(universe: Universe, l: Alias): universe.T = { + if (stop()) null.asInstanceOf[universe.T] else f(universe, null) + } + + @tailrec + def g(universe: Universe)(l: Alias): universe.T = { + if (stop()) null.asInstanceOf[universe.T] else g(universe)(l) + } + + @tailrec + def h(universe: Universe)(l: List[universe.T]): List[universe.T] = { + if (stop()) Nil else h(universe)(l) + } +} + +object Test extends App { + assert(new Bug().f(null, null) == null) + assert(new Bug().g(null)(null) == null) + assert(new Bug().h(null)(null) == Nil) +}
\ No newline at end of file diff --git a/test/files/run/t7240.check b/test/files/run/t7240.check new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/files/run/t7240.check diff --git a/test/files/run/t7240/Macros_1.scala b/test/files/run/t7240/Macros_1.scala new file mode 100644 index 0000000000..6465e18760 --- /dev/null +++ b/test/files/run/t7240/Macros_1.scala @@ -0,0 +1,48 @@ +package bakery + +import scala.language.experimental.macros +import scala.reflect.macros.Context + +trait FailureCake { + implicit def liftAnyFails[T: Manifest]: Any = ??? + + // This works + // implicit def liftAny[T]: Any = ??? +} + +object Bakery { + + def failure: Any = macro failureImpl + def failureImpl(c: Context): c.Expr[Any] = { + import c.universe._ + + def dslTrait(dslName: String) = { + val names = dslName.split("\\.").toList.reverse + assert(names.length >= 1, "DSL trait name must be in the valid format. DSL trait name is " + dslName) + + val tpeName = newTypeName(names.head) + names.tail.reverse match { + case head :: tail ⇒ + Select(tail.foldLeft[Tree](Ident(newTermName(head)))((tree, name) ⇒ Select(tree, newTermName(name))), tpeName) + case Nil ⇒ + Ident(tpeName) + } + } + + def composeDSL(transformedBody: Tree) = + ClassDef(Modifiers(), newTypeName("eval"), List(), Template( + List(dslTrait("bakery.FailureCake")), + emptyValDef, + List( + DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), + Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), + DefDef(Modifiers(), newTermName("main"), List(), List(List()), Ident(newTypeName("Any")), transformedBody)))) + + def constructor = Apply(Select(New(Ident(newTypeName("eval"))), nme.CONSTRUCTOR), List()) + + c.eval(c.Expr[Any]( + c.resetAllAttrs(Block(composeDSL(Literal(Constant(1))), constructor)))) + + c.Expr[Any](Literal(Constant(1))) + } +}
\ No newline at end of file diff --git a/test/files/run/t7240/Test_2.scala b/test/files/run/t7240/Test_2.scala new file mode 100644 index 0000000000..2450bdabf9 --- /dev/null +++ b/test/files/run/t7240/Test_2.scala @@ -0,0 +1,3 @@ +object Test extends App { + bakery.Bakery.failure +}
\ No newline at end of file diff --git a/test/files/run/t7242.scala b/test/files/run/t7242.scala new file mode 100644 index 0000000000..c995336144 --- /dev/null +++ b/test/files/run/t7242.scala @@ -0,0 +1,71 @@ +class CrashTest { + def foo = () + trait CrashTestTable { + def cols = foo + } + // This was leading to a class between the mixed in + // outer accessor and the outer accessor of this object. + object CrashTestTable extends CrashTestTable { + foo + cols + } +} + +class CrashTest1 { + def foo = () + class CrashTestTable { + def cols = foo + } + object CrashTestTable extends CrashTestTable { + foo + cols + } +} + +class CrashTest2 { + def foo = () + trait CrashTestTable { + def cols = foo + } + object Obj extends CrashTestTable { + foo + cols + } +} + +class CrashTest3 { + def foo = () + + def meth() { + trait CrashTestTable { + def cols = foo + } + object Obj extends CrashTestTable { + foo + cols + } + Obj + } +} + +object Test extends App { + { + val c = new CrashTest + c.CrashTestTable + } + + { + val c = new CrashTest1 + c.CrashTestTable + } + + { + val c = new CrashTest2 + c.Obj + } + + { + val c = new CrashTest3 + c.meth() + } +} diff --git a/test/files/run/t7246.check b/test/files/run/t7246.check new file mode 100755 index 0000000000..ce01362503 --- /dev/null +++ b/test/files/run/t7246.check @@ -0,0 +1 @@ +hello diff --git a/test/files/run/t7246/Outer.java b/test/files/run/t7246/Outer.java new file mode 100755 index 0000000000..163276fb3b --- /dev/null +++ b/test/files/run/t7246/Outer.java @@ -0,0 +1,4 @@ +public class Outer { + public class Inner { + } +}
\ No newline at end of file diff --git a/test/files/run/t7246/Test.scala b/test/files/run/t7246/Test.scala new file mode 100755 index 0000000000..9f23ca8f3a --- /dev/null +++ b/test/files/run/t7246/Test.scala @@ -0,0 +1,16 @@ +object Test extends App { + + val so = new SubOuter + val si = new so.SubInner + println(si.bar) +} + +class SubOuter extends Outer { + + val foo = "hello" + + class SubInner extends Inner { + def bar = foo + } + +}
\ No newline at end of file diff --git a/test/files/run/t7246b.check b/test/files/run/t7246b.check new file mode 100755 index 0000000000..5073bd8617 --- /dev/null +++ b/test/files/run/t7246b.check @@ -0,0 +1,2 @@ +base +sub diff --git a/test/files/run/t7246b/Base.scala b/test/files/run/t7246b/Base.scala new file mode 100755 index 0000000000..4e71d3313d --- /dev/null +++ b/test/files/run/t7246b/Base.scala @@ -0,0 +1,7 @@ +class Base { + val baseOuter = "base" + + class BaseInner { + val baseInner = baseOuter + } +} diff --git a/test/files/run/t7246b/Outer.java b/test/files/run/t7246b/Outer.java new file mode 100755 index 0000000000..53a79316ef --- /dev/null +++ b/test/files/run/t7246b/Outer.java @@ -0,0 +1,4 @@ +public class Outer extends Base { + public class Inner extends BaseInner { + } +}
\ No newline at end of file diff --git a/test/files/run/t7246b/Test.scala b/test/files/run/t7246b/Test.scala new file mode 100755 index 0000000000..f0982ea8d0 --- /dev/null +++ b/test/files/run/t7246b/Test.scala @@ -0,0 +1,14 @@ +object Test extends App { + + val so = new SubOuter + val si = new so.SubInner + println(si.baseInner) + println(si.subInner) +} + +class SubOuter extends Outer { + val subOuter = "sub" + class SubInner extends Inner { + def subInner = subOuter + } +} diff --git a/test/files/run/t7249.check b/test/files/run/t7249.check new file mode 100644 index 0000000000..7777e0a5a2 --- /dev/null +++ b/test/files/run/t7249.check @@ -0,0 +1 @@ +Yup! diff --git a/test/files/run/t7249.scala b/test/files/run/t7249.scala new file mode 100644 index 0000000000..375df5c3ad --- /dev/null +++ b/test/files/run/t7249.scala @@ -0,0 +1,7 @@ +object Test extends App { + def bnToLambda(s: => String): () => String = () => s + var x: () => String = () => sys.error("Nope") + val y = bnToLambda { x() } + x = () => "Yup!" + println(y()) +} diff --git a/test/files/run/t7290.scala b/test/files/run/t7290.scala new file mode 100644 index 0000000000..01f7e8f68e --- /dev/null +++ b/test/files/run/t7290.scala @@ -0,0 +1,9 @@ +object Test extends App { + val y = (0: Int) match { + case 1 => 1 + case 0 | 0 => 0 + case 2 | 2 | 2 | 3 | 2 | 3 => 0 + case _ => -1 + } + assert(y == 0, y) +} diff --git a/test/files/run/t7300.check b/test/files/run/t7300.check new file mode 100644 index 0000000000..51993f072d --- /dev/null +++ b/test/files/run/t7300.check @@ -0,0 +1,2 @@ +2 +2 diff --git a/test/files/run/t7300.scala b/test/files/run/t7300.scala new file mode 100644 index 0000000000..ec841690df --- /dev/null +++ b/test/files/run/t7300.scala @@ -0,0 +1,11 @@ +object Test extends App { + // single line comment in multi line comment + /*//*/ val x = 1 */*/ + val x = 2 + println(x) + + // single line comment in nested multi line comment + /*/*//*/ val y = 1 */*/*/ + val y = 2 + println(y) +} diff --git a/test/files/run/test-cpp.check b/test/files/run/test-cpp.check index bfea438c60..13f4c64be3 100644 --- a/test/files/run/test-cpp.check +++ b/test/files/run/test-cpp.check @@ -1,65 +1,81 @@ -37c37 -< locals: value args, value x, value y ---- -> locals: value args -42,43d41 -< 52 CONSTANT(2) -< 52 STORE_LOCAL(value x) -45,46d42 -< 53 LOAD_LOCAL(value x) -< 53 STORE_LOCAL(value y) -49c45 -< 54 LOAD_LOCAL(value y) ---- -> 54 CONSTANT(2) -92c88 -< locals: value args, value x, value y ---- -> locals: value args, value x -101,102d96 -< 82 LOAD_LOCAL(value x) -< 82 STORE_LOCAL(value y) -105c99 -< 83 LOAD_LOCAL(value y) ---- -> 83 LOAD_LOCAL(value x) -135c129 -< locals: value args, value x, value y ---- -> locals: value args -140,141d133 -< 66 THIS(TestAliasChainDerefThis) -< 66 STORE_LOCAL(value x) -143,144d134 -< 67 LOAD_LOCAL(value x) -< 67 STORE_LOCAL(value y) -147c137 -< 68 LOAD_LOCAL(value y) ---- -> 68 THIS(Object) -176c166 -< locals: value x, value y ---- -> locals: value x -181,182d170 -< 29 LOAD_LOCAL(value x) -< 29 STORE_LOCAL(value y) -185c173 -< 30 LOAD_LOCAL(value y) ---- -> 30 LOAD_LOCAL(value x) -223,224d210 -< 97 LOAD_LOCAL(variable x) -< 97 STORE_LOCAL(variable y) -227c213 -< 98 LOAD_LOCAL(variable y) ---- -> 98 LOAD_LOCAL(variable x) -233,234d218 -< 101 LOAD_LOCAL(variable y) -< 101 STORE_LOCAL(variable x) -236c220 -< 102 LOAD_LOCAL(variable x) ---- -> 102 LOAD_LOCAL(variable y) - +--- a ++++ b +@@ -36,3 +36,3 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, value x, value y ++ locals: value args + startBlock: 1 +@@ -41,10 +41,6 @@ + 1: +- 52 CONSTANT(2) +- 52 STORE_LOCAL(value x) + 52 SCOPE_ENTER value x +- 53 LOAD_LOCAL(value x) +- 53 STORE_LOCAL(value y) + 53 SCOPE_ENTER value y + 54 LOAD_MODULE object Predef +- 54 LOAD_LOCAL(value y) ++ 54 CONSTANT(2) + 54 BOX INT +@@ -91,3 +87,3 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, value x, value y ++ locals: value args, value x + startBlock: 1 +@@ -100,7 +96,5 @@ + 81 SCOPE_ENTER value x +- 82 LOAD_LOCAL(value x) +- 82 STORE_LOCAL(value y) + 82 SCOPE_ENTER value y + 83 LOAD_MODULE object Predef +- 83 LOAD_LOCAL(value y) ++ 83 LOAD_LOCAL(value x) + 83 BOX INT +@@ -134,3 +128,3 @@ + def main(args: Array[String] (ARRAY[REF(class String)])): Unit { +- locals: value args, value x, value y ++ locals: value args + startBlock: 1 +@@ -139,10 +133,6 @@ + 1: +- 66 THIS(TestAliasChainDerefThis) +- 66 STORE_LOCAL(value x) + 66 SCOPE_ENTER value x +- 67 LOAD_LOCAL(value x) +- 67 STORE_LOCAL(value y) + 67 SCOPE_ENTER value y + 68 LOAD_MODULE object Predef +- 68 LOAD_LOCAL(value y) ++ 68 THIS(Object) + 68 CALL_METHOD scala.Predef.println (dynamic) +@@ -175,3 +165,3 @@ + def test(x: Int (INT)): Unit { +- locals: value x, value y ++ locals: value x + startBlock: 1 +@@ -180,7 +170,5 @@ + 1: +- 29 LOAD_LOCAL(value x) +- 29 STORE_LOCAL(value y) + 29 SCOPE_ENTER value y + 30 LOAD_MODULE object Predef +- 30 LOAD_LOCAL(value y) ++ 30 LOAD_LOCAL(value x) + 30 BOX INT +@@ -222,7 +210,5 @@ + 96 SCOPE_ENTER variable x +- 97 LOAD_LOCAL(variable x) +- 97 STORE_LOCAL(variable y) + 97 SCOPE_ENTER variable y + 98 LOAD_MODULE object Predef +- 98 LOAD_LOCAL(variable y) ++ 98 LOAD_LOCAL(variable x) + 98 BOX INT +@@ -232,6 +218,4 @@ + 100 STORE_LOCAL(variable y) +- 101 LOAD_LOCAL(variable y) +- 101 STORE_LOCAL(variable x) + 102 LOAD_MODULE object Predef +- 102 LOAD_LOCAL(variable x) ++ 102 LOAD_LOCAL(variable y) + 102 BOX INT diff --git a/test/files/scalap/abstractClass/result.test b/test/files/scalap/abstractClass.check index ef1daac23d..ef1daac23d 100644 --- a/test/files/scalap/abstractClass/result.test +++ b/test/files/scalap/abstractClass.check diff --git a/test/files/scalap/abstractClass/A.scala b/test/files/scalap/abstractClass.scala index 19a528d5a1..19a528d5a1 100644 --- a/test/files/scalap/abstractClass/A.scala +++ b/test/files/scalap/abstractClass.scala diff --git a/test/files/scalap/abstractMethod/result.test b/test/files/scalap/abstractMethod.check index 40fa02d408..40fa02d408 100644 --- a/test/files/scalap/abstractMethod/result.test +++ b/test/files/scalap/abstractMethod.check diff --git a/test/files/scalap/abstractMethod/A.scala b/test/files/scalap/abstractMethod.scala index 4bedb377b3..4bedb377b3 100644 --- a/test/files/scalap/abstractMethod/A.scala +++ b/test/files/scalap/abstractMethod.scala diff --git a/test/files/scalap/caseClass/result.test b/test/files/scalap/caseClass.check index 7d7aa4fd8f..7d7aa4fd8f 100644 --- a/test/files/scalap/caseClass/result.test +++ b/test/files/scalap/caseClass.check diff --git a/test/files/scalap/caseClass/A.scala b/test/files/scalap/caseClass.scala index 95f9984519..95f9984519 100644 --- a/test/files/scalap/caseClass/A.scala +++ b/test/files/scalap/caseClass.scala diff --git a/test/files/scalap/caseObject/result.test b/test/files/scalap/caseObject.check index 867a4b2162..867a4b2162 100644 --- a/test/files/scalap/caseObject/result.test +++ b/test/files/scalap/caseObject.check diff --git a/test/files/scalap/caseObject/A.scala b/test/files/scalap/caseObject.scala index 6a3ff10d75..6a3ff10d75 100644 --- a/test/files/scalap/caseObject/A.scala +++ b/test/files/scalap/caseObject.scala diff --git a/test/files/scalap/cbnParam/result.test b/test/files/scalap/cbnParam.check index 52ecb6ae66..52ecb6ae66 100644 --- a/test/files/scalap/cbnParam/result.test +++ b/test/files/scalap/cbnParam.check diff --git a/test/files/scalap/cbnParam/A.scala b/test/files/scalap/cbnParam.scala index 2f366df64a..2f366df64a 100644 --- a/test/files/scalap/cbnParam/A.scala +++ b/test/files/scalap/cbnParam.scala diff --git a/test/files/scalap/classPrivate/result.test b/test/files/scalap/classPrivate.check index ab2d40cdaf..ab2d40cdaf 100644 --- a/test/files/scalap/classPrivate/result.test +++ b/test/files/scalap/classPrivate.check diff --git a/test/files/scalap/classPrivate/A.scala b/test/files/scalap/classPrivate.scala index 9f1bd34a6a..9f1bd34a6a 100644 --- a/test/files/scalap/classPrivate/A.scala +++ b/test/files/scalap/classPrivate.scala diff --git a/test/files/scalap/classWithExistential/result.test b/test/files/scalap/classWithExistential.check index caee3fd6de..caee3fd6de 100644 --- a/test/files/scalap/classWithExistential/result.test +++ b/test/files/scalap/classWithExistential.check diff --git a/test/files/scalap/classWithExistential/A.scala b/test/files/scalap/classWithExistential.scala index 4a5213f963..4a5213f963 100644 --- a/test/files/scalap/classWithExistential/A.scala +++ b/test/files/scalap/classWithExistential.scala diff --git a/test/files/scalap/classWithSelfAnnotation/result.test b/test/files/scalap/classWithSelfAnnotation.check index 82bbd9e8df..82bbd9e8df 100644 --- a/test/files/scalap/classWithSelfAnnotation/result.test +++ b/test/files/scalap/classWithSelfAnnotation.check diff --git a/test/files/scalap/classWithSelfAnnotation/A.scala b/test/files/scalap/classWithSelfAnnotation.scala index 9e0398622a..9e0398622a 100644 --- a/test/files/scalap/classWithSelfAnnotation/A.scala +++ b/test/files/scalap/classWithSelfAnnotation.scala diff --git a/test/files/scalap/covariantParam/result.test b/test/files/scalap/covariantParam.check index f7a3c98966..f7a3c98966 100644 --- a/test/files/scalap/covariantParam/result.test +++ b/test/files/scalap/covariantParam.check diff --git a/test/files/scalap/covariantParam/A.scala b/test/files/scalap/covariantParam.scala index 5b2c24d6fa..5b2c24d6fa 100644 --- a/test/files/scalap/covariantParam/A.scala +++ b/test/files/scalap/covariantParam.scala diff --git a/test/files/scalap/defaultParameter/result.test b/test/files/scalap/defaultParameter.check index 0c775ea7b5..0c775ea7b5 100644 --- a/test/files/scalap/defaultParameter/result.test +++ b/test/files/scalap/defaultParameter.check diff --git a/test/files/scalap/defaultParameter/A.scala b/test/files/scalap/defaultParameter.scala index d3514952f4..d3514952f4 100644 --- a/test/files/scalap/defaultParameter/A.scala +++ b/test/files/scalap/defaultParameter.scala diff --git a/test/files/scalap/implicitParam/result.test b/test/files/scalap/implicitParam.check index a2cfd6092d..a2cfd6092d 100644 --- a/test/files/scalap/implicitParam/result.test +++ b/test/files/scalap/implicitParam.check diff --git a/test/files/scalap/implicitParam/A.scala b/test/files/scalap/implicitParam.scala index 80657218d9..80657218d9 100644 --- a/test/files/scalap/implicitParam/A.scala +++ b/test/files/scalap/implicitParam.scala diff --git a/test/files/scalap/packageObject/result.test b/test/files/scalap/packageObject.check index 5732d92958..5732d92958 100644 --- a/test/files/scalap/packageObject/result.test +++ b/test/files/scalap/packageObject.check diff --git a/test/files/scalap/packageObject/A.scala b/test/files/scalap/packageObject.scala index 7e429c9935..7e429c9935 100644 --- a/test/files/scalap/packageObject/A.scala +++ b/test/files/scalap/packageObject.scala diff --git a/test/files/scalap/paramClauses/result.test b/test/files/scalap/paramClauses.check index 3a141e8faf..3a141e8faf 100644 --- a/test/files/scalap/paramClauses/result.test +++ b/test/files/scalap/paramClauses.check diff --git a/test/files/scalap/paramClauses/A.scala b/test/files/scalap/paramClauses.scala index f9d1917402..f9d1917402 100644 --- a/test/files/scalap/paramClauses/A.scala +++ b/test/files/scalap/paramClauses.scala diff --git a/test/files/scalap/paramNames/result.test b/test/files/scalap/paramNames.check index 85e37f858d..85e37f858d 100644 --- a/test/files/scalap/paramNames/result.test +++ b/test/files/scalap/paramNames.check diff --git a/test/files/scalap/paramNames/A.scala b/test/files/scalap/paramNames.scala index 7ba9ff0feb..7ba9ff0feb 100644 --- a/test/files/scalap/paramNames/A.scala +++ b/test/files/scalap/paramNames.scala diff --git a/test/files/scalap/sequenceParam/result.test b/test/files/scalap/sequenceParam.check index 142d92fea3..142d92fea3 100644 --- a/test/files/scalap/sequenceParam/result.test +++ b/test/files/scalap/sequenceParam.check diff --git a/test/files/scalap/sequenceParam/A.scala b/test/files/scalap/sequenceParam.scala index 86e13340b9..86e13340b9 100644 --- a/test/files/scalap/sequenceParam/A.scala +++ b/test/files/scalap/sequenceParam.scala diff --git a/test/files/scalap/simpleClass/result.test b/test/files/scalap/simpleClass.check index 4fdf25d1cf..4fdf25d1cf 100644 --- a/test/files/scalap/simpleClass/result.test +++ b/test/files/scalap/simpleClass.check diff --git a/test/files/scalap/simpleClass/A.scala b/test/files/scalap/simpleClass.scala index fa82e62680..fa82e62680 100644 --- a/test/files/scalap/simpleClass/A.scala +++ b/test/files/scalap/simpleClass.scala diff --git a/test/files/scalap/traitObject/result.test b/test/files/scalap/traitObject.check index 104ba14f1a..104ba14f1a 100644 --- a/test/files/scalap/traitObject/result.test +++ b/test/files/scalap/traitObject.check diff --git a/test/files/scalap/traitObject/A.scala b/test/files/scalap/traitObject.scala index d5f43181c1..d5f43181c1 100644 --- a/test/files/scalap/traitObject/A.scala +++ b/test/files/scalap/traitObject.scala diff --git a/test/files/scalap/typeAnnotations/result.test b/test/files/scalap/typeAnnotations.check index 407b0235c6..407b0235c6 100644 --- a/test/files/scalap/typeAnnotations/result.test +++ b/test/files/scalap/typeAnnotations.check diff --git a/test/files/scalap/typeAnnotations/A.scala b/test/files/scalap/typeAnnotations.scala index ff2445edc9..ff2445edc9 100644 --- a/test/files/scalap/typeAnnotations/A.scala +++ b/test/files/scalap/typeAnnotations.scala diff --git a/test/files/scalap/valAndVar/result.test b/test/files/scalap/valAndVar.check index e940da9801..e940da9801 100644 --- a/test/files/scalap/valAndVar/result.test +++ b/test/files/scalap/valAndVar.check diff --git a/test/files/scalap/valAndVar/A.scala b/test/files/scalap/valAndVar.scala index 2d89348401..2d89348401 100644 --- a/test/files/scalap/valAndVar/A.scala +++ b/test/files/scalap/valAndVar.scala diff --git a/test/files/scalap/wildcardType/result.test b/test/files/scalap/wildcardType.check index e43261db32..e43261db32 100644 --- a/test/files/scalap/wildcardType/result.test +++ b/test/files/scalap/wildcardType.check diff --git a/test/files/scalap/wildcardType/A.scala b/test/files/scalap/wildcardType.scala index 4bb0d14de5..4bb0d14de5 100644 --- a/test/files/scalap/wildcardType/A.scala +++ b/test/files/scalap/wildcardType.scala diff --git a/test/partest b/test/partest index 9b0ab02fdc..8243316cca 100755 --- a/test/partest +++ b/test/partest @@ -1,7 +1,8 @@ -#!/bin/sh +#!/usr/bin/env bash +# ############################################################################## -# Scala test runner 2.8.0 +# Scala test runner 2.10.0 ############################################################################## # (c) 2002-2013 LAMP/EPFL # @@ -10,6 +11,16 @@ # PARTICULAR PURPOSE. ############################################################################## +# Use tput to detect color-capable terminal. +term_colors=$(tput colors 2>/dev/null) +if [[ $? == 0 ]] && [[ $term_colors -gt 2 ]]; then + git_diff_options="--color=always --word-diff" + color_opts="-Dpartest.colors=$term_colors" +else + unset color_opts + git_diff_options="--nocolor" +fi + cygwin=false; darwin=false; case "`uname`" in @@ -53,8 +64,8 @@ if [ -z "$EXT_CLASSPATH" ] ; then fi done elif [ -f "$SCALA_HOME/build/pack/lib/scala-partest.jar" ] ; then - for lib in `echo "partest library reflect compiler"`; do - ext="$SCALA_HOME/build/pack/lib/scala-$lib.jar" + for lib in `echo "scala-partest scala-library scala-reflect scala-compiler diffutils"`; do + ext="$SCALA_HOME/build/pack/lib/$lib.jar" if [ -z "$EXT_CLASSPATH" ] ; then EXT_CLASSPATH="$ext" else @@ -98,6 +109,7 @@ fi "${JAVACMD:=java}" \ $JAVA_OPTS -cp "$EXT_CLASSPATH" \ ${partestDebugStr} \ + "$color_opts" \ -Dscala.home="${SCALA_HOME}" \ -Dpartest.javacmd="${JAVACMD}" \ -Dpartest.java_opts="${JAVA_OPTS}" \ diff --git a/test/scaladoc/run/SI-6580.check b/test/scaladoc/run/SI-6580.check new file mode 100644 index 0000000000..2fb6ec3258 --- /dev/null +++ b/test/scaladoc/run/SI-6580.check @@ -0,0 +1,11 @@ +Chain(List(Chain(List(Text(Here z(1) is defined as follows:), Text( +), HtmlTag(<br>), Text( +), Text( ), HtmlTag(<img src='http://example.com/fig1.png'>), Text( +), HtmlTag(<br>), Text( +), Text(plus z(1) times), Text( +), HtmlTag(<br>), Text( +), Text( ), HtmlTag(<img src='http://example.com/fig2.png'>), Text( +), HtmlTag(<br>), Text( +), Text(equals QL of something +))))) +Done. diff --git a/test/scaladoc/run/SI-6580.scala b/test/scaladoc/run/SI-6580.scala new file mode 100644 index 0000000000..c544138f44 --- /dev/null +++ b/test/scaladoc/run/SI-6580.scala @@ -0,0 +1,32 @@ +import scala.tools.nsc.doc +import scala.tools.nsc.doc.model._ +import scala.tools.nsc.doc.html.page.{Index, ReferenceIndex} +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + override def scaladocSettings = "" + override def code = """ + + object Test { + /** Here z(1) is defined as follows: + * <br> + * <img src='http://example.com/fig1.png'> + * <br> + * plus z(1) times + * <br> + * <img src='http://example.com/fig2.png'> + * <br> + * equals QL of something + */ + def f = 1 + } + + """ + + def testModel(rootPackage: Package) { + import access._ + + val f = rootPackage._object("Test")._method("f") + println(f.comment.get.short) + } +} diff --git a/test/scaladoc/run/SI-6715.check b/test/scaladoc/run/SI-6715.check new file mode 100644 index 0000000000..619c56180b --- /dev/null +++ b/test/scaladoc/run/SI-6715.check @@ -0,0 +1 @@ +Done. diff --git a/test/scaladoc/run/SI-6715.scala b/test/scaladoc/run/SI-6715.scala new file mode 100644 index 0000000000..92d3376234 --- /dev/null +++ b/test/scaladoc/run/SI-6715.scala @@ -0,0 +1,15 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + def scaladocSettings = "" + + override def code = "object A { def $$ = 123 }" + + def testModel(rootPackage: Package) = { + import access._ + + val method = rootPackage._object("A")._method("$$") + assert(method != null) + } +} |