diff options
author | Paul Phillips <paulp@improving.org> | 2011-08-22 19:49:46 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-08-22 19:49:46 +0000 |
commit | 80952759fbaaf3a99f6bc0ca528da726b5a438e7 (patch) | |
tree | 6a81273c34bc51830ce60a7b4d65aa6111a8ef51 /src | |
parent | 4b29535009d7848d870252b5b08f53844544de64 (diff) | |
download | scala-80952759fbaaf3a99f6bc0ca528da726b5a438e7.tar.gz scala-80952759fbaaf3a99f6bc0ca528da726b5a438e7.tar.bz2 scala-80952759fbaaf3a99f6bc0ca528da726b5a438e7.zip |
Created infrastructure for testing icode + sett...
Created infrastructure for testing icode + settings/partest yak shaving.
See enclosed test files/run/inline-ex-handlers.scala. To compare
optimized and unoptimized icode after a given phase, all you need in a
partest source file is this:
import scala.tools.partest.IcodeTest
object Test extends IcodeTest {
override def printIcodeAfterPhase = "inlineExceptionHandlers"
}
Other things can be done, see IcodeTest.scala. Review by ureche.
Diffstat (limited to 'src')
14 files changed, 178 insertions, 48 deletions
diff --git a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala index f74654efed..0dfd12cc2d 100644 --- a/src/compiler/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/compiler/scala/reflect/internal/settings/MutableSettings.scala @@ -19,7 +19,8 @@ abstract class MutableSettings extends AbsSettings { protected var setByUser: Boolean = false def postSetHook(): Unit = () - def isDefault: Boolean = !setByUser + def isDefault = !setByUser + def isSetByUser = setByUser def value: T = v def value_=(arg: T) = { setByUser = true diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 1622b71bf2..50c0816bca 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -31,7 +31,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { |Boolean settings are always false unless set. |Where multiple values are accepted, they should be comma-separated. | example: -Xplugin:plugin1,plugin2 - |<phase> means one or a list of: + |<phases> means one or a comma-separated list of: | (partial) phase names, phase ids, phase id ranges, or the string "all". | example: -Xprint:all prints all phases. | example: -Xprint:expl,24-26 prints phases explicitouter, closelim, dce, jvm. diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index dd067750c8..592540f450 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -262,7 +262,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb // debugging def checkPhase = wasActive(settings.check) def logPhase = isActive(settings.log) - def writeICode = settings.writeICode.value + + // Write *.icode files the setting was given. + def writeICode = settings.writeICode.isSetByUser && isActive(settings.writeICode) // showing/printing things def browsePhase = isActive(settings.browse) @@ -1019,7 +1021,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb phaseTimings(globalPhase) = currentTime - startTime // write icode to *.icode files - if (opt.writeICode && (runIsAt(icodePhase) || opt.printPhase && runIsPast(icodePhase))) + if (opt.writeICode) writeICode() // print trees diff --git a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala index 958a708111..ff4abbb757 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Printers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Printers.scala @@ -3,13 +3,11 @@ * @author Martin Odersky */ - package scala.tools.nsc package backend package icode import java.io.PrintWriter - import scala.tools.nsc.symtab.Flags import scala.tools.nsc.util.Position @@ -105,7 +103,7 @@ trait Printers { self: ICodes => def printExceptionHandler(e: ExceptionHandler) { indent; - println("catch (" + e.cls.simpleName + ") in " + e.covered + " starting at: " + e.startBlock); + println("catch (" + e.cls.simpleName + ") in " + e.covered.toSeq.sortBy(_.label) + " starting at: " + e.startBlock); println("consisting of blocks: " + e.blocks); undent; println("with finalizer: " + e.finalizer); diff --git a/src/compiler/scala/tools/nsc/interactive/tests/core/TestSettings.scala b/src/compiler/scala/tools/nsc/interactive/tests/core/TestSettings.scala index e92634e731..681204172b 100644 --- a/src/compiler/scala/tools/nsc/interactive/tests/core/TestSettings.scala +++ b/src/compiler/scala/tools/nsc/interactive/tests/core/TestSettings.scala @@ -16,4 +16,4 @@ private[tests] trait TestSettings { protected val sourceDir = "src" protected implicit val reporter: Reporter = ConsoleReporter -}
\ No newline at end of file +} diff --git a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala index be2f8a370a..d4ac490134 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala @@ -29,7 +29,7 @@ trait AbsScalaSettings { def MultiStringSetting(name: String, helpArg: String, descr: String): MultiStringSetting def OutputSetting(outputDirs: OutputDirs, default: String): OutputSetting def PathSetting(name: String, descr: String, default: String): PathSetting - def PhasesSetting(name: String, descr: String): PhasesSetting + def PhasesSetting(name: String, descr: String, default: String): PhasesSetting def StringSetting(name: String, helpArg: String, descr: String, default: String): StringSetting def PrefixSetting(name: String, prefix: String, descr: String): PrefixSetting diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index 96175ff89d..0ccb8df4bb 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -202,7 +202,7 @@ class MutableSettings(val errorFn: String => Unit) def IntSetting(name: String, descr: String, default: Int, range: Option[(Int, Int)], parser: String => Option[Int]) = add(new IntSetting(name, descr, default, range, parser)) def MultiStringSetting(name: String, arg: String, descr: String) = add(new MultiStringSetting(name, arg, descr)) def OutputSetting(outputDirs: OutputDirs, default: String) = add(new OutputSetting(outputDirs, default)) - def PhasesSetting(name: String, descr: String) = add(new PhasesSetting(name, descr)) + def PhasesSetting(name: String, descr: String, default: String = "") = add(new PhasesSetting(name, descr, default)) def StringSetting(name: String, arg: String, descr: String, default: String) = add(new StringSetting(name, arg, descr, default)) def PathSetting(name: String, descr: String, default: String): PathSetting = { val prepend = StringSetting(name + "/p", "", "", "").internalOnly() @@ -554,6 +554,12 @@ class MutableSettings(val errorFn: String => Unit) withHelpSyntax(name + ":<" + helpArg + ">") } + private def mkPhasesHelp(descr: String, default: String) = { + descr + " <phases>" + ( + if (default == "") "" else " (default: " + default + ")" + ) + } + /** A setting represented by a list of strings which should be prefixes of * phase names. This is not checked here, however. Alternatively the string * `"all"` can be used to represent all phases. @@ -561,8 +567,11 @@ class MutableSettings(val errorFn: String => Unit) */ class PhasesSetting private[nsc]( name: String, - descr: String) - extends Setting(name, descr + " <phase>.") { + descr: String, + default: String + ) extends Setting(name, mkPhasesHelp(descr, default)) { + private[nsc] def this(name: String, descr: String) = this(name, descr, "") + type T = List[String] v = Nil override def value = if (v contains "all") List("all") else super.value @@ -590,9 +599,12 @@ class MutableSettings(val errorFn: String => Unit) case fns => fns.reduceLeft((f1, f2) => id => f1(id) || f2(id)) } - def tryToSet(args: List[String]) = errorAndValue("missing phase", None) + def tryToSet(args: List[String]) = + if (default == "") errorAndValue("missing phase", None) + else { tryToSetColon(List(default)) ; Some(args) } + override def tryToSetColon(args: List[String]) = args match { - case Nil => errorAndValue("missing phase", None) + case Nil => if (default == "") errorAndValue("missing phase", None) else tryToSetColon(List(default)) case xs => value = (value ++ xs).distinct.sorted ; Some(Nil) } // we slightly abuse the usual meaning of "contains" here by returning @@ -605,6 +617,9 @@ class MutableSettings(val errorFn: String => Unit) def doAllPhases = stringValues contains "all" def unparse: List[String] = value map (name + ":" + _) - withHelpSyntax(name + ":<phase>") + withHelpSyntax( + if (default == "") name + ":<phases>" + else name + "[:phases]" + ) } } diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 09ad877d25..5ffc2e1aad 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -78,7 +78,7 @@ trait ScalaSettings extends AbsScalaSettings val require = MultiStringSetting("-Xplugin-require", "plugin", "Abort unless the given plugin(s) are available.") val pluginsDir = StringSetting ("-Xpluginsdir", "path", "Path to search compiler plugins.", Defaults.scalaPluginPath) val Xprint = PhasesSetting ("-Xprint", "Print out program after") - val writeICode = BooleanSetting ("-Xprint-icode", "Log internal icode to *.icode files.") + val writeICode = PhasesSetting ("-Xprint-icode", "Log internal icode to *.icode files after", "icode") val Xprintpos = BooleanSetting ("-Xprint-pos", "Print tree positions, as offsets.") val printtypes = BooleanSetting ("-Xprint-types", "Print tree types (debugging option).") val prompt = BooleanSetting ("-Xprompt", "Display a prompt after each error (debugging option).") @@ -128,7 +128,7 @@ trait ScalaSettings extends AbsScalaSettings val noimports = BooleanSetting ("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") val nopredef = BooleanSetting ("-Yno-predef", "Compile without importing Predef.") val noAdaptedArgs = BooleanSetting ("-Yno-adapted-args", "Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.") - val Yprofile = PhasesSetting ("-Yprofile", "(Requires jvm -agentpath to contain yjgpagent) Profile CPU usage of given phases.") + val Yprofile = PhasesSetting ("-Yprofile", "(Requires jvm -agentpath to contain yjgpagent) Profile CPU usage of") val YprofileMem = BooleanSetting ("-Yprofile-memory", "Profile memory, get heap snapshot after each compiler run (requires yjpagent, see above).") val YprofileClass = StringSetting ("-Yprofile-class", "class", "Name of profiler class.", "scala.tools.util.YourkitProfiling") val Yrecursion = IntSetting ("-Yrecursion", "Set recursion depth used when locking symbols.", 0, Some((0, Int.MaxValue)), (_: String) => None) @@ -140,8 +140,8 @@ trait ScalaSettings extends AbsScalaSettings val Ynosqueeze = BooleanSetting ("-Yno-squeeze", "Disable creation of compact code in matching.") val Ystatistics = BooleanSetting ("-Ystatistics", "Print compiler statistics.") . withPostSetHook(set => util.Statistics.enabled = set.value) - val stopAfter = PhasesSetting ("-Ystop-after", "Stop after given phase") withAbbreviation ("-stop") // backward compat - val stopBefore = PhasesSetting ("-Ystop-before", "Stop before given phase") + val stopAfter = PhasesSetting ("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat + val stopBefore = PhasesSetting ("-Ystop-before", "Stop before") val refinementMethodDispatch = ChoiceSetting ("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala new file mode 100644 index 0000000000..95a8ae57dd --- /dev/null +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -0,0 +1,49 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.partest + +import scala.tools.nsc._ +import io.Directory +import util.BatchSourceFile + +/** A class for testing code which is embedded as a string. + * It allows for more complete control over settings, compiler + * configuration, sequence of events, etc. than does partest. + */ +abstract class DirectTest extends App { + // The program being tested in some fashion + def code: String + // produce the output to be compared against a checkfile + def show(): Unit + + // override to add additional settings with strings + def extraSettings: String = "" + // a default Settings object + def settings: Settings = newSettings(extraSettings) + // a custom Settings object + def newSettings(argString: String) = { + val s = new Settings + s processArgumentString (argString + " " + debugSettings) + s + } + // compile the code, optionally first adding to the settings + def compile(args: String*) = { + val settings = newSettings(extraSettings +: args mkString " ") + val global = new Global(settings) + new global.Run compileSources List(new BatchSourceFile("<partest>", code)) + !global.reporter.hasErrors + } + + /** Constructor/main body **/ + try show() + catch { case t => println(t) ; sys.exit(1) } + + /** Debugger interest only below this line **/ + protected val isDebug = (sys.props contains "partest.debug") || (sys.env contains "PARTEST_DEBUG") + protected def debugSettings = sys.props.getOrElse("partest.debug.settings", "") + + final def log(msg: => Any) { if (isDebug) Console println msg } +} diff --git a/src/partest/scala/tools/partest/IcodeTest.scala b/src/partest/scala/tools/partest/IcodeTest.scala new file mode 100644 index 0000000000..5a8ec2890f --- /dev/null +++ b/src/partest/scala/tools/partest/IcodeTest.scala @@ -0,0 +1,45 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools.partest + +import scala.tools.nsc._ +import nest.FileUtil._ +import io.Directory + +/** A trait for testing icode. All you need is this in a + * partest source file: + * {{{ + * object Test extends IcodeTest + * }}} + * And then the generated output will be the icode for everything + * in that file. See source for possible customizations. + */ +abstract class IcodeTest extends DirectTest { + // override to check icode at a different point. + def printIcodeAfterPhase = "icode" + // override to use source code other than the file being tested. + def code = io.File(sys.props("partest.test-path")).slurp() + + override def extraSettings: String = "-usejavacp -Xprint-icode:" + printIcodeAfterPhase + + // Compile, read in all the *.icode files, delete them, and return their contents + def collectIcode(args: String*): List[String] = { + compile(args: _*) + val icodeFiles = Directory(".").files.toList filter (_ hasExtension "icode") + + try icodeFiles sortBy (_.name) flatMap (f => f.lines.toList) + finally icodeFiles foreach (f => f.delete()) + } + + // Default show() compiles the code with and without optimization and + // outputs the diff. + def show() { + val lines1 = collectIcode("") + val lines2 = collectIcode("-optimise") + + println(compareContents(lines1, lines2)) + } +} diff --git a/src/partest/scala/tools/partest/ReplTest.scala b/src/partest/scala/tools/partest/ReplTest.scala index 02ab154d4b..02cf61902a 100644 --- a/src/partest/scala/tools/partest/ReplTest.scala +++ b/src/partest/scala/tools/partest/ReplTest.scala @@ -12,28 +12,16 @@ import java.lang.reflect.{ Method => JMethod, Field => JField } /** A trait for testing repl code. It drops the first line * of output because the real repl prints a version number. */ -abstract class ReplTest extends App { - def code: String - // override to add additional settings with strings - def extraSettings: String = "" +abstract class ReplTest extends DirectTest { // override to transform Settings object immediately before the finish def transformSettings(s: Settings): Settings = s - // final because we need to enforce the existence of a couple settings. - final def settings: Settings = { - val s = new Settings + final override def settings: Settings = { + val s = super.settings s.Yreplsync.value = true s.Xnojline.value = true - val settingString = sys.props("scala.partest.debug.repl-args") match { - case null => extraSettings - case s => extraSettings + " " + s - } - s processArgumentString settingString transformSettings(s) } def eval() = ILoop.runForTranscript(code, settings).lines drop 1 def show() = eval() foreach println - - try show() - catch { case t => println(t) ; sys.exit(1) } } diff --git a/src/partest/scala/tools/partest/nest/FileManager.scala b/src/partest/scala/tools/partest/nest/FileManager.scala index bf564ea9a7..a39aa27f32 100644 --- a/src/partest/scala/tools/partest/nest/FileManager.scala +++ b/src/partest/scala/tools/partest/nest/FileManager.scala @@ -16,7 +16,7 @@ import scala.tools.nsc.io.{ Path, Directory, File => SFile } import sys.process._ import scala.collection.mutable -trait FileManager { +trait FileUtil { /** * Compares two files using a Java implementation of the GNU diff * available at http://www.bmsi.com/java/#diff. @@ -33,6 +33,24 @@ trait FileManager { val res = diffWriter.toString if (res startsWith "No") "" else res } + 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 + } +} +object FileUtil extends FileUtil { } + +trait FileManager extends FileUtil { def testRootDir: Directory def testRootPath: String diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala index a00b94eba9..f2e34ced30 100644 --- a/src/partest/scala/tools/partest/nest/TestFile.scala +++ b/src/partest/scala/tools/partest/nest/TestFile.scala @@ -13,29 +13,32 @@ import scala.tools.nsc.Settings import scala.tools.nsc.util.ClassPath import scala.tools.nsc.io._ -abstract class TestFile(kind: String) { +trait TestFileCommon { def file: JFile - def fileManager: FileManager + def kind: String - val dir = file.toAbsolute.parent - val fileBase = file.stripExtension - lazy val objectDir = dir / "%s-%s.obj".format(fileBase, kind) createDirectory true - val flags: Option[String] = dir / "%s.flags".format(fileBase) ifFile { _.slurp().trim } + 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): Boolean = { + def defineSettings(settings: Settings, setOutDir: Boolean) = { settings.classpath append dir.path if (setOutDir) - settings.outdir.value = setOutDirTo.path + settings.outputDirs setSingleOutput setOutDirTo.path // have to catch bad flags somewhere - flags foreach { f => - if (!settings.processArgumentString(f)._1) - return false + (flags forall (f => settings.processArgumentString(f)._1)) && { + settings.classpath append fileManager.CLASSPATH + true } - settings.classpath append fileManager.CLASSPATH - true } override def toString(): String = "%s %s".format(kind, file) diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 2298c097b9..a8900e115d 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -298,6 +298,16 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor 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 @@ -313,6 +323,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor "-Dpartest.output="+outDir.getAbsolutePath, "-Dpartest.lib="+LATEST_LIB, "-Dpartest.cwd="+outDir.getParent, + "-Dpartest.test-path="+testFullPath, "-Dpartest.testname="+fileBase, "-Djavacmd="+JAVACMD, "-Djavaccmd="+javacCmd, |