package scala.tools.nsc.backend.jvm import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import scala.tools.asm.Opcodes._ import scala.tools.partest.ASMConverters._ import scala.tools.testing.BytecodeTesting import scala.tools.testing.BytecodeTesting._ @RunWith(classOf[JUnit4]) class OptimizedBytecodeTest extends BytecodeTesting { override def compilerArgs = "-Yopt:l:classpath -Yopt-warnings" import compiler._ @Test def t2171(): Unit = { val code = """class C { | final def m(msg: => String) = try 0 catch { case ex: Throwable => println(msg) } | def t(): Unit = while (true) m("...") |} """.stripMargin val c = compileClass(code) assertSameCode(getMethod(c, "t"), List(Label(0), Jump(GOTO, Label(0)))) } @Test def t3430(): Unit = { val code = """class C { | final def m(f: String => Boolean) = f("a") | def t(): Boolean = | m { s1 => | m { s2 => | while (true) { } | true | } | } |} """.stripMargin val c = compileClass(code) assertSameSummary(getMethod(c, "t"), List( LDC, ASTORE, ALOAD /*0*/, ALOAD /*1*/, "C$$$anonfun$1", IRETURN)) assertSameSummary(getMethod(c, "C$$$anonfun$1"), List(LDC, "C$$$anonfun$2", IRETURN)) assertSameSummary(getMethod(c, "C$$$anonfun$2"), List(-1 /*A*/, GOTO /*A*/)) } @Test def t3252(): Unit = { val code = """class C { | def t(x: Boolean): Thread = { | g { | x match { | case false => Tat.h { } | } | } | } | | private def g[T](block: => T) = ??? |} |object Tat { | def h(block: => Unit): Nothing = ??? |} """.stripMargin val List(c, t, tMod) = compileClasses(code, allowMessage = _.msg.contains("not be exhaustive")) assertSameSummary(getMethod(c, "t"), List(GETSTATIC, "$qmark$qmark$qmark", ATHROW)) } @Test def t6157(): Unit = { val code = """class C { | def t = println(ErrorHandler.defaultIfIOException("String")("String")) |} |object ErrorHandler { | import java.io.IOException | @inline | def defaultIfIOException[T](default: => T)(closure: => T): T = try closure catch { | case e: IOException => default | } |} """.stripMargin val msg = """ErrorHandler$::defaultIfIOException(Lscala/Function0;Lscala/Function0;)Ljava/lang/Object; is annotated @inline but could not be inlined: |The operand stack at the callsite in C::t()V contains more values than the |arguments expected by the callee ErrorHandler$::defaultIfIOException(Lscala/Function0;Lscala/Function0;)Ljava/lang/Object;. These values would be discarded |when entering an exception handler declared in the inlined method.""".stripMargin compileClasses(code, allowMessage = _.msg == msg) } @Test def t6547(): Unit = { // "pos" test -- check that it compiles val code = """trait ConfigurableDefault[@specialized V] { | def fillArray(arr: Array[V], v: V) = (arr: Any) match { | case x: Array[Int] => null | case x: Array[Long] => v.asInstanceOf[Long] | } |} """.stripMargin compileToBytes(code) } @Test def t8062(): Unit = { val c1 = """package warmup |object Warmup { def filter[A](p: Any => Boolean): Any = filter[Any](p) } """.stripMargin val c2 = "class C { def t = warmup.Warmup.filter[Any](x => false) }" val List(c, _, _) = compileClassesSeparately(List(c1, c2), extraArgs = compilerArgs) assertInvoke(getMethod(c, "t"), "warmup/Warmup$", "filter") } @Test def t8306(): Unit = { // "pos" test val code = """class C { | def foo: Int = 123 | lazy val extension: Int = foo match { | case idx if idx != -1 => 15 | case _ => 17 | } |} """.stripMargin compileToBytes(code) } @Test def t8359(): Unit = { // "pos" test // This is a minimization of code that crashed the compiler during bootstrapping // in the first iteration of https://github.com/scala/scala/pull/4373, the PR // that adjusted the order of free and declared params in LambdaLift. // Was: // java.lang.AssertionError: assertion failed: // Record Record(<$anon: Function1>,Map(value a$1 -> Deref(LocalVar(value b)))) does not contain a field value b$1 // at scala.tools.nsc.Global.assert(Global.scala:262) // at scala.tools.nsc.backend.icode.analysis.CopyPropagation$copyLattice$State.getFieldNonRecordValue(CopyPropagation.scala:113) // at scala.tools.nsc.backend.icode.analysis.CopyPropagation$copyLattice$State.getFieldNonRecordValue(CopyPropagation.scala:122) // at scala.tools.nsc.backend.opt.ClosureElimination$ClosureElim$$anonfun$analyzeMethod$1$$anonfun$apply$2.replaceFieldAccess$1(ClosureElimination.scala:124) val code = """package test |class Typer { | def bar(a: Boolean, b: Boolean): Unit = { | @inline | def baz(): Unit = { | ((_: Any) => (Typer.this, a, b)).apply("") | } | ((_: Any) => baz()).apply("") | } |} """.stripMargin compileToBytes(code) } @Test def t9123(): Unit = { // "pos" test val code = """trait Setting { | type T | def value: T |} |object Test { | def test(x: Some[Setting]) = x match { | case Some(dep) => Some(dep.value) map (_ => true) | } |} """.stripMargin compileToBytes(code) } @Test def traitForceInfo(): Unit = { // This did NOT crash unless it's in the interactive package. // error: java.lang.AssertionError: assertion failed: trait Contexts.NoContext$ linkedModule: List() // at scala.Predef$.assert(Predef.scala:160) // at scala.tools.nsc.symtab.classfile.ClassfileParser$innerClasses$.innerSymbol$1(ClassfileParser.scala:1211) // at scala.tools.nsc.symtab.classfile.ClassfileParser$innerClasses$.classSymbol(ClassfileParser.scala:1223) // at scala.tools.nsc.symtab.classfile.ClassfileParser.classNameToSymbol(ClassfileParser.scala:489) // at scala.tools.nsc.symtab.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:757) // at scala.tools.nsc.symtab.classfile.ClassfileParser.sig2type$1(ClassfileParser.scala:789) val code = """package scala.tools.nsc |package interactive | |trait MyContextTrees { | val self: Global | val NoContext = self.analyzer.NoContext |} """.stripMargin compileClasses(code) } @Test def t9160(): Unit = { val code = """class C { | def getInt: Int = 0 | def t(trees: Object): Int = { | trees match { | case Some(elems) => | case tree => getInt | } | 55 | } |} """.stripMargin val c = compileClass(code) assertSameSummary(getMethod(c, "t"), List( ALOAD /*1*/, INSTANCEOF /*Some*/, IFNE /*A*/, ALOAD /*0*/, "getInt", POP, -1 /*A*/, BIPUSH, IRETURN)) } @Test def t8796(): Unit = { val code = """final class C { | def pr(): Unit = () | def t(index: Int): Unit = index match { | case 0 => pr() | case 1 => pr() | case _ => t(index - 2) | } |} """.stripMargin val c = compileClass(code) assertSameSummary(getMethod(c, "t"), List( -1 /*A*/, ILOAD /*1*/, TABLESWITCH, -1, ALOAD, "pr", RETURN, -1, ALOAD, "pr", RETURN, -1, ILOAD, ICONST_2, ISUB, ISTORE, GOTO /*A*/)) } @Test def t8524(): Unit = { val c1 = """package library |object Library { | @inline def pleaseInlineMe() = 1 | object Nested { @inline def pleaseInlineMe() = 2 } |} """.stripMargin val c2 = """class C { | def t = library.Library.pleaseInlineMe() + library.Library.Nested.pleaseInlineMe() |} """.stripMargin val cls = compileClassesSeparately(List(c1, c2), extraArgs = compilerArgs) val c = findClass(cls, "C") assertSameSummary(getMethod(c, "t"), List( GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, // module load and null checks not yet eliminated -1, ICONST_1, GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, -1, ICONST_2, IADD, IRETURN)) } @Test def privateInline(): Unit = { val code = """final class C { | private var x1 = false | var x2 = false | | @inline private def wrapper1[T](body: => T): T = { | val saved = x1 | x1 = true | try body | finally x1 = saved | } | | @inline private def wrapper2[T](body: => T): T = { | val saved = x2 | x2 = true | try body | finally x2 = saved | } | // inlined | def f1a() = wrapper1(5) | // not inlined: even after inlining `identity`, the Predef module is already on the stack for the | // subsequent null check (the receiver of an inlined method, in this case Predef, is checked for | // nullness, to ensure an NPE is thrown) | def f1b() = identity(wrapper1(5)) | | def f2a() = wrapper2(5) // inlined | def f2b() = identity(wrapper2(5)) // not inlined |} """.stripMargin val c = compileClass(code, allowMessage = _.msg.contains("exception handler declared in the inlined method")) assertInvoke(getMethod(c, "f1a"), "C", "C$$$anonfun$1") assertInvoke(getMethod(c, "f1b"), "C", "wrapper1") assertInvoke(getMethod(c, "f2a"), "C", "C$$$anonfun$3") assertInvoke(getMethod(c, "f2b"), "C", "wrapper2") } @Test def t7060(): Unit = { val code = """class C { | @inline final def mbarray_apply_minibox(array: Any, tag: Byte): Long = | if (tag == 0) array.asInstanceOf[Array[Long]](0) | else array.asInstanceOf[Array[Byte]](0).toLong | | def t = mbarray_apply_minibox(null, 0) |} """.stripMargin val c = compileClass(code) assertNoInvoke(getMethod(c, "t")) } @Test def t8315(): Unit = { val code = """class C { | def t(as: Listt): Unit = { | map(as, (_: Any) => return) | } | final def map(x: Listt, f: Any => Any): Any = { | if (x eq Nill) "" else f("") | } |} |object Nill extends Listt |class Listt """.stripMargin val List(c, nil, nilMod, listt) = compileClasses(code) assertInvoke(getMethod(c, "t"), "C", "C$$$anonfun$1") } @Test def t8315b(): Unit = { val code = """class C { | def crash: Unit = { | val key = "" | try map(new F(key)) | catch { case _: Throwable => } | } | final def map(f: F): Any = f.apply("") |} |final class F(key: String) { | final def apply(a: Any): Any = throw new RuntimeException(key) |} """.stripMargin val List(c, f) = compileClasses(code) assertInvoke(getMethod(c, "crash"), "C", "map") } @Test def optimiseEnablesNewOpt(): Unit = { val code = """class C { def t = (1 to 10) foreach println }""" val List(c) = readAsmClasses(newCompiler(extraArgs = "-optimise -deprecation").compileToBytes(code, allowMessage = _.msg.contains("is deprecated"))) assertInvoke(getMethod(c, "t"), "C", "C$$$anonfun$1") // range-foreach inlined from classpath } }