summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-08-22 19:49:46 +0000
committerPaul Phillips <paulp@improving.org>2011-08-22 19:49:46 +0000
commit80952759fbaaf3a99f6bc0ca528da726b5a438e7 (patch)
tree6a81273c34bc51830ce60a7b4d65aa6111a8ef51
parent4b29535009d7848d870252b5b08f53844544de64 (diff)
downloadscala-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.
-rw-r--r--src/compiler/scala/reflect/internal/settings/MutableSettings.scala3
-rw-r--r--src/compiler/scala/tools/nsc/CompilerCommand.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Printers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/interactive/tests/core/TestSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/AbsScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala27
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala8
-rw-r--r--src/partest/scala/tools/partest/DirectTest.scala49
-rw-r--r--src/partest/scala/tools/partest/IcodeTest.scala45
-rw-r--r--src/partest/scala/tools/partest/ReplTest.scala18
-rw-r--r--src/partest/scala/tools/partest/nest/FileManager.scala20
-rw-r--r--src/partest/scala/tools/partest/nest/TestFile.scala29
-rw-r--r--src/partest/scala/tools/partest/nest/Worker.scala11
-rw-r--r--test/files/run/inline-ex-handlers.check314
-rw-r--r--test/files/run/inline-ex-handlers.scala329
16 files changed, 821 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,
diff --git a/test/files/run/inline-ex-handlers.check b/test/files/run/inline-ex-handlers.check
new file mode 100644
index 0000000000..29daca3471
--- /dev/null
+++ b/test/files/run/inline-ex-handlers.check
@@ -0,0 +1,314 @@
+268c268
+< locals: value x$1, value temp1
+---
+> locals: value x$1, value temp1, variable boxed1
+270c270
+< blocks: [1,2,3,4]
+---
+> blocks: [1,2,3]
+283,285d282
+< 92 JUMP 4
+<
+< 4:
+291a289,290
+> 92 STORE_LOCAL(variable boxed1)
+> 92 LOAD_LOCAL(variable boxed1)
+372c371
+< blocks: [1,2,3,4,5,7,8,10]
+---
+> blocks: [1,2,3,4,5,7,8,10,11]
+396c395,404
+< 103 THROW(MyException)
+---
+> ? STORE_LOCAL(value ex$1)
+> ? JUMP 11
+>
+> 11:
+> 101 LOAD_LOCAL(value ex$1)
+> 101 STORE_LOCAL(value temp2)
+> 101 SCOPE_ENTER value temp2
+> 101 LOAD_LOCAL(value temp2)
+> 101 IS_INSTANCE REF(class MyException)
+> 101 CZJUMP (BOOL)NE ? 4 : 5
+487c495
+< blocks: [1,2,3,4,6,7,8,9,10]
+---
+> blocks: [1,2,3,4,6,7,8,9,10,11,12,13]
+516c524,529
+< 306 THROW(MyException)
+---
+> ? JUMP 11
+>
+> 11:
+> ? LOAD_LOCAL(variable monitor4)
+> 305 MONITOR_EXIT
+> ? JUMP 12
+522c535,541
+< ? THROW(Throwable)
+---
+> ? JUMP 12
+>
+> 12:
+> ? LOAD_LOCAL(variable monitor3)
+> 304 MONITOR_EXIT
+> ? STORE_LOCAL(value t)
+> ? JUMP 13
+528c547,560
+< ? THROW(Throwable)
+---
+> ? STORE_LOCAL(value t)
+> ? JUMP 13
+>
+> 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)
+> 310 JUMP 2
+552c584
+< catch (Throwable) in ArrayBuffer(7, 8, 9, 10) starting at: 6
+---
+> catch (Throwable) in ArrayBuffer(7, 8, 9, 10, 11) starting at: 6
+555c587
+< catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10) starting at: 3
+---
+> catch (Throwable) in ArrayBuffer(4, 6, 7, 8, 9, 10, 11, 12) starting at: 3
+587c619
+< blocks: [1,2,3,4,5,6,7,9,10]
+---
+> blocks: [1,2,3,4,5,6,7,9,10,11,12]
+611c643,649
+< 78 THROW(IllegalArgumentException)
+---
+> ? STORE_LOCAL(value e)
+> ? JUMP 11
+>
+> 11:
+> 81 LOAD_LOCAL(value e)
+> ? STORE_LOCAL(variable exc1)
+> ? JUMP 12
+640c678,692
+< 81 THROW(Exception)
+---
+> ? STORE_LOCAL(variable exc1)
+> ? JUMP 12
+>
+> 12:
+> 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)
+662c714
+< catch (<none>) in ArrayBuffer(4, 6, 7, 9) starting at: 3
+---
+> catch (<none>) in ArrayBuffer(4, 6, 7, 9, 11) starting at: 3
+688c740
+< blocks: [1,2,3,4,5,6,7,8,11,12,13,14,15,16,18,19]
+---
+> blocks: [1,2,3,4,5,6,7,8,11,12,13,14,15,16,18,19,20,21,22]
+712c764,773
+< 172 THROW(MyException)
+---
+> ? STORE_LOCAL(value ex$4)
+> ? JUMP 20
+>
+> 20:
+> 170 LOAD_LOCAL(value ex$4)
+> 170 STORE_LOCAL(value temp11)
+> 170 SCOPE_ENTER value temp11
+> 170 LOAD_LOCAL(value temp11)
+> 170 IS_INSTANCE REF(class MyException)
+> 170 CZJUMP (BOOL)NE ? 12 : 13
+766c827,828
+< 177 THROW(MyException)
+---
+> ? STORE_LOCAL(value ex$5)
+> ? JUMP 21
+770c832,841
+< 170 THROW(Throwable)
+---
+> ? STORE_LOCAL(value ex$5)
+> ? JUMP 21
+>
+> 21:
+> 169 LOAD_LOCAL(value ex$5)
+> 169 STORE_LOCAL(value temp14)
+> 169 SCOPE_ENTER value temp14
+> 169 LOAD_LOCAL(value temp14)
+> 169 IS_INSTANCE REF(class MyException)
+> 169 CZJUMP (BOOL)NE ? 5 : 6
+801c872,873
+< 182 THROW(MyException)
+---
+> ? STORE_LOCAL(variable exc2)
+> ? JUMP 22
+805c877,891
+< 169 THROW(Throwable)
+---
+> ? STORE_LOCAL(variable exc2)
+> ? JUMP 22
+>
+> 22:
+> 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)
+827c913
+< catch (Throwable) in ArrayBuffer(11, 12, 13, 14, 15, 16, 18) starting at: 4
+---
+> catch (Throwable) in ArrayBuffer(11, 12, 13, 14, 15, 16, 18, 20) starting at: 4
+830c916
+< catch (<none>) in ArrayBuffer(4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 18) starting at: 3
+---
+> catch (<none>) in ArrayBuffer(4, 5, 6, 7, 11, 12, 13, 14, 15, 16, 18, 20, 21) starting at: 3
+856c942
+< blocks: [1,2,3,6,7,8,10,11,13]
+---
+> blocks: [1,2,3,6,7,8,10,11,13,14]
+880c966,975
+< 124 THROW(MyException)
+---
+> ? STORE_LOCAL(value ex$2)
+> ? JUMP 14
+>
+> 14:
+> 122 LOAD_LOCAL(value ex$2)
+> 122 STORE_LOCAL(value temp5)
+> 122 SCOPE_ENTER value temp5
+> 122 LOAD_LOCAL(value temp5)
+> 122 IS_INSTANCE REF(class MyException)
+> 122 CZJUMP (BOOL)NE ? 7 : 8
+928c1023
+< catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 10, 11, 13) starting at: 3
+---
+> catch (IllegalArgumentException) in ArrayBuffer(6, 7, 8, 10, 11, 13, 14) starting at: 3
+954c1049
+< blocks: [1,2,3,4,5,9,10,11,13]
+---
+> blocks: [1,2,3,4,5,9,10,11,13,14]
+978c1073,1082
+< 148 THROW(MyException)
+---
+> ? STORE_LOCAL(value ex$3)
+> ? JUMP 14
+>
+> 14:
+> 145 LOAD_LOCAL(value ex$3)
+> 145 STORE_LOCAL(value temp8)
+> 145 SCOPE_ENTER value temp8
+> 145 LOAD_LOCAL(value temp8)
+> 145 IS_INSTANCE REF(class MyException)
+> 145 CZJUMP (BOOL)NE ? 4 : 5
+1222c1326
+< blocks: [1,2,3,4,5,7]
+---
+> blocks: [1,2,3,4,5,7,8]
+1246c1350,1357
+< 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
+1295c1406
+< blocks: [1,2,3,4,5,7,8,10,11,13]
+---
+> blocks: [1,2,3,4,5,7,8,10,11,13,14]
+1319c1430,1431
+< 203 THROW(MyException)
+---
+> ? STORE_LOCAL(value ex$6)
+> ? JUMP 14
+1339c1451,1460
+< 209 THROW(MyException)
+---
+> ? STORE_LOCAL(value ex$6)
+> ? JUMP 14
+>
+> 14:
+> 200 LOAD_LOCAL(value ex$6)
+> 200 STORE_LOCAL(value temp17)
+> 200 SCOPE_ENTER value temp17
+> 200 LOAD_LOCAL(value temp17)
+> 200 IS_INSTANCE REF(class MyException)
+> 200 CZJUMP (BOOL)NE ? 4 : 5
+1402c1523
+< blocks: [1,2,3,4,5,7]
+---
+> blocks: [1,2,3,4,5,7,8]
+1426c1547,1554
+< 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
+1475c1603
+< blocks: [1,2,3,4]
+---
+> blocks: [1,2,3,4,5]
+1495c1623,1628
+< 229 THROW(MyException)
+---
+> ? JUMP 5
+>
+> 5:
+> ? LOAD_LOCAL(variable monitor1)
+> 228 MONITOR_EXIT
+> 228 THROW(Throwable)
+1501c1634
+< ? THROW(Throwable)
+---
+> 228 THROW(Throwable)
+1529c1662
+< locals: value args, variable result, variable monitor2, variable monitorResult1
+---
+> locals: value exception$1, value args, variable result, variable monitor2, variable monitorResult1
+1531c1664
+< blocks: [1,2,3,4]
+---
+> blocks: [1,2,3,4,5]
+1554c1687,1695
+< 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)
+1560c1701
+< ? THROW(Throwable)
+---
+> 244 THROW(Throwable)
+
diff --git a/test/files/run/inline-ex-handlers.scala b/test/files/run/inline-ex-handlers.scala
new file mode 100644
index 0000000000..a96b938e13
--- /dev/null
+++ b/test/files/run/inline-ex-handlers.scala
@@ -0,0 +1,329 @@
+import scala.tools.partest.IcodeTest
+
+object Test extends IcodeTest {
+ override def printIcodeAfterPhase = "inlineExceptionHandlers"
+}
+
+import scala.util.Random._
+
+/** There should be no inlining taking place in this class */
+object TestInlineHandlersNoInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersNoInline")
+ var result = -1
+
+ try {
+ if (nextInt % 2 == 0)
+ throw new IllegalArgumentException("something")
+ result = 1
+ } catch {
+ case e: StackOverflowError =>
+ println("Stack overflow")
+ }
+
+ result
+ }
+}
+
+/** Just a simple inlining should take place in this class */
+object TestInlineHandlersSimpleInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersSimpleInline")
+ var result = -1
+
+ try {
+ if (nextInt % 2 == 0)
+ throw new IllegalArgumentException("something")
+ result = 1
+ } catch {
+ case e: IllegalArgumentException =>
+ println("IllegalArgumentException")
+ }
+
+ result
+ }
+}
+
+/** Inlining should take place because the handler is taking a superclass of the exception thrown */
+object TestInlineHandlersSubclassInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersSubclassInline")
+ var result = -1
+
+ try {
+ if (nextInt % 2 == 0)
+ throw new IllegalArgumentException("something")
+ result = 1
+ } catch {
+ case e: RuntimeException =>
+ println("RuntimeException")
+ }
+
+ result
+ }
+}
+
+/** For this class, the finally handler should be inlined */
+object TestInlineHandlersFinallyInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersFinallyInline")
+ var result = -1
+
+ try {
+ if (nextInt % 2 == 0)
+ throw new IllegalArgumentException("something")
+ result = 1
+ } catch {
+ case e: Exception => throw e
+ } finally {
+ println("finally")
+ result = (result - 1) / 2
+ }
+
+ result
+ }
+}
+
+
+case class MyException(message: String) extends RuntimeException(message)
+
+/** For this class, we test inlining for a case class error */
+object TestInlineHandlersCaseClassExceptionInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersCaseClassExceptionInline")
+ var result = -1
+
+ try {
+ if (nextInt % 2 == 0)
+ throw new MyException("something")
+ result = 1
+ } catch {
+ case MyException(message) => println(message)
+ }
+
+ result
+ }
+}
+
+
+/** For this class, inline should take place in the inner handler */
+object TestInlineHandlersNestedHandlerInnerInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersNestedHandlersInnerInline")
+ var result = -1
+
+ try {
+ try {
+ if (nextInt % 2 == 0)
+ throw new MyException("something")
+ result = 1
+ } catch {
+ case MyException(message) => println(message)
+ }
+ } catch {
+ case e: IllegalArgumentException => println("IllegalArgumentException")
+ }
+
+ result
+ }
+}
+
+
+/** For this class, inline should take place in the outer handler */
+object TestInlineHandlersNestedHandlerOuterInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersNestedHandlersOuterInline")
+ var result = -1
+
+ try {
+ try {
+ if (nextInt % 2 == 0)
+ throw new MyException("something")
+ result = 1
+ } catch {
+ case e: IllegalArgumentException => println("IllegalArgumentException")
+ }
+ } catch {
+ case MyException(message) => println(message)
+ }
+
+ result
+ }
+}
+
+
+/** For this class, inline should take place in the all handlers (inner, outer and finally) */
+object TestInlineHandlersNestedHandlerAllInline {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersNestedHandlersOuterInline")
+ var result = -1
+
+ try {
+ try {
+ if (nextInt % 2 == 0)
+ throw new MyException("something")
+ result = 1
+ } catch {
+ case MyException(message) =>
+ println(message)
+ throw MyException(message)
+ }
+ } catch {
+ case MyException(message) =>
+ println(message)
+ throw MyException(message)
+ } finally {
+ println("finally")
+ result = (result - 1) / 2
+ }
+
+ result
+ }
+}
+
+
+/** This class is meant to test whether the inline handler is copied only once for multiple inlines */
+object TestInlineHandlersSingleCopy {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersSingleCopy")
+ var result = -1
+
+ try {
+
+ if (nextInt % 2 == 0)
+ throw new MyException("something")
+
+ println("A side effect in the middle")
+ result = 3 // another one
+
+ if (nextInt % 3 == 2)
+ throw new MyException("something else")
+ result = 1
+ } catch {
+ case MyException(message) =>
+ println(message)
+ }
+
+ result
+ }
+}
+
+/** This should test the special exception handler for synchronized blocks */
+object TestInlineHandlersSynchronized {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersSynchronized")
+ var result = "hello"
+
+ // any exception thrown here will be caught by a default handler that does MONTIOR_EXIT on result :)
+ result.synchronized {
+ throw MyException(result)
+ }
+
+ result.length
+ }
+}
+
+/** This should test the special exception handler for synchronized blocks with stack */
+object TestInlineHandlersSynchronizedWithStack {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersSynchronizedWithStack")
+ var result = "hello"
+
+ // any exception thrown here will be caught by a default handler that does MONTIOR_EXIT on result :)
+ result = "abc" + result.synchronized {
+ throw MyException(result)
+ }
+
+ result.length
+ }
+}
+
+/** This test should trigger a bug in the dead code elimination phase - it actually crashes ICodeCheckers
+object TestInlineHandlersSynchronizedWithStackDoubleThrow {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersSynchronizedWithStackDoubleThrow")
+ var result = "a"
+
+ // any exception thrown here will be caught by a default handler that does MONTIOR_EXIT on result :)
+ result += result.synchronized { throw MyException(result) }
+ result += result.synchronized { throw MyException(result) }
+
+ result.length
+ }
+}
+*/
+
+/** This test should check the preciseness of the inliner: it should not do any inlining here
+* as it is not able to discern between the different exceptions
+*/
+object TestInlineHandlersPreciseness {
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersCorrectHandler")
+
+ try {
+ val exception: Throwable =
+ if (scala.util.Random.nextInt % 2 == 0)
+ new IllegalArgumentException("even")
+ else
+ new StackOverflowError("odd")
+ throw exception
+ } catch {
+ case e: IllegalArgumentException =>
+ println("Correct, IllegalArgumentException")
+ case e: StackOverflowError =>
+ println("Correct, StackOverflowException")
+ case t: Throwable =>
+ println("WROOOONG, not Throwable!")
+ }
+ }
+}
+
+/** This check should verify that the double no-local exception handler is duplicated correctly */
+object TestInlineHandlersDoubleNoLocal {
+
+ val a1: String = "a"
+ val a2: String = "b"
+
+ def main(args: Array[String]): Unit = {
+ println("TestInlineHandlersDoubleNoLocal")
+
+ try {
+ a1.synchronized {
+ a2. synchronized {
+ throw new MyException("crash")
+ }
+ }
+ } catch {
+ case t: Throwable => println("Caught crash: " + t.toString)
+ }
+
+ /* try {
+ val exception: Throwable =
+ if (scala.util.Random.nextInt % 2 == 0)
+ new IllegalArgumentException("even")
+ else
+ new StackOverflowError("odd")
+ throw exception
+ } catch {
+ case e: IllegalArgumentException =>
+ println("Correct, IllegalArgumentException")
+ case e: StackOverflowError =>
+ println("Correct, StackOverflowException")
+ case t: Throwable =>
+ println("WROOOONG, not Throwable!")
+ }*/
+ }
+}