diff options
author | Lukas Rytz <lukas.rytz@typesafe.com> | 2014-09-01 22:00:20 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@typesafe.com> | 2014-09-01 22:00:20 +0200 |
commit | bb3b08dd675a68c5245a8ad64470c796e346ba91 (patch) | |
tree | 92f5a1108d68114b6c9423630998c4bdbeea0432 /test/files | |
parent | 7693cecc8b3cf56984a041bb2d7979e2a040314a (diff) | |
parent | 2256753502c2c099da17a20d45c3f23974d0eafd (diff) | |
download | scala-bb3b08dd675a68c5245a8ad64470c796e346ba91.tar.gz scala-bb3b08dd675a68c5245a8ad64470c796e346ba91.tar.bz2 scala-bb3b08dd675a68c5245a8ad64470c796e346ba91.zip |
Merge pull request #3931 from lrytz/opt/tracked-final
GenBCode refactoring (remove Tracked) and fix InnerClass / EnclosingMethod attributes
Diffstat (limited to 'test/files')
-rw-r--r-- | test/files/jvm/innerClassAttribute.check | 54 | ||||
-rw-r--r-- | test/files/jvm/innerClassAttribute/Classes_1.scala | 92 | ||||
-rw-r--r-- | test/files/jvm/innerClassAttribute/Java_A_1.java | 10 | ||||
-rw-r--r-- | test/files/jvm/innerClassAttribute/Test.scala | 218 | ||||
-rw-r--r-- | test/files/jvm/javaReflection.check | 259 | ||||
-rw-r--r-- | test/files/jvm/javaReflection/Classes_1.scala | 84 | ||||
-rw-r--r-- | test/files/jvm/javaReflection/Test.scala | 137 | ||||
-rw-r--r-- | test/files/run/t5256c.check | 2 |
8 files changed, 816 insertions, 40 deletions
diff --git a/test/files/jvm/innerClassAttribute.check b/test/files/jvm/innerClassAttribute.check new file mode 100644 index 0000000000..20518aa49e --- /dev/null +++ b/test/files/jvm/innerClassAttribute.check @@ -0,0 +1,54 @@ +#partest !-Ydelambdafy:method +-- A4 -- +A4$$anonfun$f$1 / null / null / 17 +A4$$anonfun$f$1 / null / null / 17 +A4 / f / (Lscala/collection/immutable/List;)Lscala/collection/immutable/List; +-- A19 -- +A19$$anonfun$1 / null / null / 17 +A19$$anonfun$2 / null / null / 17 +A19$$anonfun$3 / null / null / 17 +A19$$anonfun$1 / null / null / 17 +A19$$anonfun$2 / null / null / 17 +A19$$anonfun$3 / null / null / 17 +A19 / null / null +A19 / null / null +A19 / null / null +-- A20 -- +A20$$anonfun$4 / null / null / 17 +fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1` +A20$$anonfun$4 / null / null / 17 +A20$$anonfun$4$$anonfun$apply$1 / null / null / 17 +A20$$anonfun$4$$anonfun$apply$3 / null / null / 17 +fun2 () => (): itself and the outer closure +A20$$anonfun$4 / null / null / 17 +A20$$anonfun$4$$anonfun$apply$1 / null / null / 17 +fun3 () => () => (): itself, the outer closure and its child closure +A20$$anonfun$4 / null / null / 17 +A20$$anonfun$4$$anonfun$apply$3 / null / null / 17 +A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17 +fun4: () => 1: itself and the two outer closures +A20$$anonfun$4 / null / null / 17 +A20$$anonfun$4$$anonfun$apply$3 / null / null / 17 +A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2 / null / null / 17 +enclosing: nested closures have the apply method of the outer closure +A20 / null / null +A20$$anonfun$4 / apply / ()Lscala/Function0; +A20$$anonfun$4 / apply / ()Lscala/Function0; +A20$$anonfun$4$$anonfun$apply$3 / apply / ()Lscala/Function0; +#partest -Ydelambdafy:method +-- A4 -- +null / null / null +-- A19 -- +null / null / null +null / null / null +null / null / null +-- A20 -- +fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1` +fun2 () => (): itself and the outer closure +fun3 () => () => (): itself, the outer closure and its child closure +fun4: () => 1: itself and the two outer closures +enclosing: nested closures have the apply method of the outer closure +null / null / null +null / null / null +null / null / null +null / null / null diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala index 0875d9160c..9c3ea7f013 100644 --- a/test/files/jvm/innerClassAttribute/Classes_1.scala +++ b/test/files/jvm/innerClassAttribute/Classes_1.scala @@ -74,7 +74,7 @@ class A14 { object A15 { def f = { - class B { // static (does not have an outer pointer) + class B { // non-static, even though it doesn't have an outer pointer class C // non-static } } @@ -90,10 +90,98 @@ class A16 { class V extends A6 new A6 { } } + + new A6 { } } class A17 { object B { - class C // not static, has an outer pointer. + class C // not static, also has an outer pointer. + } +} + +class A18 { + def f = { + def g = { + class A + new A6 { } + val y = { + if ((new Object).hashCode() == 1) {class B {} ; new B} else 2 + if ((new Object).hashCode() == 1) new A6 { } else "haifish" + } + } + } +} + +class A19 { + ((x: Int) => x + 3) + + val x = { + ((x: Int) => x + 1) + } + + { + ((x: Int) => x + 2) + } +} + +class A20 { + () => { + {() => ()} + {() => () => 1} + } +} + +class A21 { + class I1 + def f = { class J1 } +} +object A21 { + class I2 + object I3 { + class J2 // static + } + def g = { class J3 } // non-static + val x = { class J4 } // non-static + { + class J5 // non-static (!) + new J5 + } +} + +class A22 { + class C + object C { + class D // inner class of C$, not of C. Not added to the inner class table of C, only to C$ + } +} + +class A23 { + def f = { + val a = new Java_A_1() + val c = new Java_A_1.C() + val d = new Java_A_1.C.D() + val e = new c.E() + val f = new a.F() + val g = new f.G() + } +} + +trait A24Sym + +trait A24Base { + // trait with concrete members: interface plus (absract) impl class + trait DefinitionsApi { + def Abs: A24Sym + def Conc: A24Sym = new A24Sym { } + } +} + +trait A24 extends A24Base { + class DefinitionsClass extends DefinitionsApi { + // bridge methods are generated for Abs and Conc. there used to be a bug: the bridge symbol was a ModuleSymbol, + // calling companionClass would return NoSymbol. i changed erasure to make the bridge symbol is a MethodSymbol. + object Abs extends A24Sym + override object Conc extends A24Sym } } diff --git a/test/files/jvm/innerClassAttribute/Java_A_1.java b/test/files/jvm/innerClassAttribute/Java_A_1.java new file mode 100644 index 0000000000..3357d05e2b --- /dev/null +++ b/test/files/jvm/innerClassAttribute/Java_A_1.java @@ -0,0 +1,10 @@ +public class Java_A_1 { + public static class C { + public static class D { } + public class E { } + } + + public class F { + public class G { } + } +} diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala index 6cf60ab92d..882edbcdd7 100644 --- a/test/files/jvm/innerClassAttribute/Test.scala +++ b/test/files/jvm/innerClassAttribute/Test.scala @@ -43,10 +43,30 @@ object Test extends BytecodeTest { assertSame(node.access, flags) } - def assertEnclosingMethod(enclosingMethod: EnclosingMethod, outerClass: String, name: String, descriptor: String) = { - assertSame(enclosingMethod.outerClass, outerClass) - assertSame(enclosingMethod.name, name) - assertSame(enclosingMethod.descriptor, descriptor) + def assertEnclosingMethod(className: String, outerClass: String, name: String, descriptor: String) = { + val encl = enclosingMethod(className) + assertSame(encl.outerClass, outerClass) + assertSame(encl.name, name) + assertSame(encl.descriptor, descriptor) + } + + def assertNoEnclosingMethod(className: String) = { + assertSame(enclosingMethod(className).outerClass, null) + } + + def printInnerClassNodes(className: String) = { + for (n <- innerClassNodes(className)) { + println(s"${n.name} / ${n.outerName} / ${n.innerName} / ${n.access}") + } + } + + def printEnclosingMethod(className: String) = { + val e = enclosingMethod(className) + println(s"${e.outerClass} / ${e.name} / ${e.descriptor}") + } + + def lambdaClass(anonfunName: String, lambdaName: String): String = { + if (classpath.findClass(anonfunName).isDefined) anonfunName else lambdaName } def testA1() = { @@ -78,13 +98,11 @@ object Test extends BytecodeTest { } def testA4() = { - val List(an1) = innerClassNodes("A4") - assertAnonymous(an1, "A4$$anonfun$f$1") - val List(an2) = innerClassNodes("A4$$anonfun$f$1") - assertAnonymous(an2, "A4$$anonfun$f$1") - assertEnclosingMethod( - enclosingMethod("A4$$anonfun$f$1"), - "A4", "f", "(Lscala/collection/immutable/List;)Lscala/collection/immutable/List;") + println("-- A4 --") + printInnerClassNodes("A4") + val fun = lambdaClass("A4$$anonfun$f$1", "A4$lambda$$f$1") + printInnerClassNodes(fun) + printEnclosingMethod(fun) } def testA5() = { @@ -93,7 +111,7 @@ object Test extends BytecodeTest { val List(b2) = innerClassNodes("A5$B$2$") assertLocal(b2, "A5$B$2$", "B$2$") assertEnclosingMethod( - enclosingMethod("A5$B$2$"), + "A5$B$2$", "A5", "f", "()Ljava/lang/Object;") } @@ -136,56 +154,175 @@ object Test extends BytecodeTest { assertLocal(k, "A14$K$1", "K$1") assertEnclosingMethod( - enclosingMethod("A14$K$1"), + "A14$K$1", "A14", "f", "()Ljava/lang/Object;") assertAnonymous(anon, "A14$$anon$1") assertEnclosingMethod( - enclosingMethod("A14$$anon$1"), + "A14$$anon$1", "A14", "g", "()V") } def testA15() = { val List(b) = innerClassNodes("A15") - assertLocal(b, "A15$B$3", "B$3", flags = publicStatic) + assertLocal(b, "A15$B$3", "B$3") val List(_, c) = innerClassNodes("A15$B$3") - // TODO this is a bug in the backend, C should be a member. Instead, its outerClass is null - // assertMember(c, "A15$B$3", "C") - assertLocal(c, "A15$B$3$C", "C") + assertMember(c, "A15$B$3", "C") + + assertEnclosingMethod( + "A15$B$3", + "A15$", "f", "()V") + assertNoEnclosingMethod("A15$B$3$C") } def testA16() = { - val List(anon1, anon2, u, v) = innerClassNodes("A16") - // TODO there's a bug in the backend: anon$2 has outerClass A16, but anonymous classes should have outerClass null - // assertAnonymous(anon1, "A16$$anon$2") - assertMember(anon1, "A16", null, name = Some("A16$$anon$2"), flags = Flags.ACC_PUBLIC | Flags.ACC_FINAL) + val List(anon1, anon2, anon3, u, v) = innerClassNodes("A16") + assertAnonymous(anon1, "A16$$anon$2") assertAnonymous(anon2, "A16$$anon$3") - // TODO this is a bug in the backend, U should not be a member, its outerClass should be null - // assertLocal(u, "A16$U$1", "U$1") - assertMember(u, "A16", "U$1") + assertAnonymous(anon3, "A16$$anon$4") + + assertLocal(u, "A16$U$1", "U$1") assertLocal(v, "A16$V$1", "V$1") assertEnclosingMethod( - enclosingMethod("A16$$anon$2"), - "A16", "<init>", "()V") + "A16$$anon$2", + "A16", null, null) + assertEnclosingMethod( + "A16$$anon$3", + "A16", null, null) + assertEnclosingMethod( + "A16$$anon$4", + "A16", null, null) + assertEnclosingMethod( - enclosingMethod("A16$$anon$3"), - "A16", "<init>", "()V") - // TODO this is a bug, there should be an enclosingMethod attribute in U - // assertEnclosingMethod( - // enclosingMethod("A16$U$1"), - // "A16", "<init>", "()V") + "A16$U$1", + "A16", null, null) assertEnclosingMethod( - enclosingMethod("A16$V$1"), - "A16", "<init>", "()V") + "A16$V$1", + "A16", null, null) } def testA17() = { val List(b, c) = innerClassNodes("A17$B$") assertMember(b, "A17", "B$") - // TODO this is a bug, should not be static. - assertMember(c, "A17$B$", "C", name = Some("A17$B$C"), flags = publicStatic) // (should be) not static, has an outer pointer. + assertMember(c, "A17$B$", "C", name = Some("A17$B$C")) // not static, has an outer pointer. + } + + def testA18() = { + val List(anon1, anon2, a, b) = innerClassNodes("A18") + assertAnonymous(anon1, "A18$$anon$5") + assertAnonymous(anon2, "A18$$anon$6") + + assertLocal(a, "A18$A$1", "A$1") + assertLocal(b, "A18$B$4", "B$4") + + assertEnclosingMethod( + "A18$$anon$5", + "A18", "g$1", "()V") + assertEnclosingMethod( + "A18$$anon$6", + "A18", "g$1", "()V") + + assertEnclosingMethod( + "A18$A$1", + "A18", "g$1", "()V") + assertEnclosingMethod( + "A18$B$4", + "A18", "g$1", "()V") + } + + def testA19() = { + println("-- A19 --") + + printInnerClassNodes("A19") + + val fun1 = lambdaClass("A19$$anonfun$1", "A19$lambda$1") + val fun2 = lambdaClass("A19$$anonfun$2", "A19$lambda$2") + val fun3 = lambdaClass("A19$$anonfun$3", "A19$lambda$3") + + printInnerClassNodes(fun1) + printInnerClassNodes(fun2) + printInnerClassNodes(fun3) + + printEnclosingMethod(fun1) + printEnclosingMethod(fun2) + printEnclosingMethod(fun3) + } + + def testA20() = { + println("-- A20 --") + + printInnerClassNodes("A20") + + val fun1 = lambdaClass("A20$$anonfun$4", "A20$lambda$1") + val fun2 = lambdaClass("A20$$anonfun$4$$anonfun$apply$1", "A20$lambda$$$anonfun$5$1") + val fun3 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3", "A20$lambda$$$anonfun$5$2") + val fun4 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$anonfun$7$1") + + println("fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`") + printInnerClassNodes(fun1) + println("fun2 () => (): itself and the outer closure") + printInnerClassNodes(fun2) + println("fun3 () => () => (): itself, the outer closure and its child closure") + printInnerClassNodes(fun3) + println("fun4: () => 1: itself and the two outer closures") + printInnerClassNodes(fun4) + + println("enclosing: nested closures have the apply method of the outer closure") + printEnclosingMethod(fun1) + printEnclosingMethod(fun2) + printEnclosingMethod(fun3) + printEnclosingMethod(fun4) + } + + def testA21() = { + val List(i1c, i2c, i3c, j1) = innerClassNodes("A21") + assertMember(i1c, "A21", "I1") + assertMember(i2c, "A21", "I2", flags = publicStatic) + assertMember(i3c, "A21", "I3$", flags = publicStatic) + assertLocal(j1, "A21$J1$1", "J1$1") + + val List(i2m, i3m, j3, j4, j5) = innerClassNodes("A21$") + assertMember(i2m, "A21", "I2", flags = publicStatic) + assertMember(i3m, "A21", "I3$", flags = publicStatic) + assertLocal(j3, "A21$J3$1", "J3$1") + assertLocal(j4, "A21$J4$1", "J4$1") + assertLocal(j5, "A21$J5$1", "J5$1") // non-static! + + val List(i3x, j2x) = innerClassNodes("A21$I3$J2") + assertMember(j2x, "A21$I3$", "J2", name = Some("A21$I3$J2"), flags = publicStatic) + + assertNoEnclosingMethod("A21$I3$J2") + assertEnclosingMethod("A21$J3$1", "A21$", "g", "()V") + assertEnclosingMethod("A21$J4$1", "A21$", null, null) + assertEnclosingMethod("A21$J5$1", "A21$", null, null) + } + + def testA22() = { + val List(cc) = innerClassNodes("A22$C") + assertMember(cc, "A22", "C") + val List(cm, d) = innerClassNodes("A22$C$") + assertMember(cm, "A22", "C$") + assertMember(d, "A22$C$", "D", name = Some("A22$C$D")) + } + + def testA23() { + val List(c, d, e, f, g) = innerClassNodes("A23") + assertMember(c, "Java_A_1", "C", flags = publicStatic) + assertMember(d, "Java_A_1$C", "D", flags = publicStatic) + assertMember(e, "Java_A_1$C", "E") + assertMember(f, "Java_A_1", "F") + assertMember(g, "Java_A_1$F", "G") + } + + def testA24() { + val List(defsCls, abs, conc, defsApi, defsApiImpl) = innerClassNodes("A24$DefinitionsClass") + assertMember(defsCls, "A24", "DefinitionsClass") + assertMember(abs, "A24$DefinitionsClass", "Abs$") + assertMember(conc, "A24$DefinitionsClass", "Conc$") + assertMember(defsApi, "A24Base", "DefinitionsApi", flags = publicAbstractInterface) + assertMember(defsApiImpl, "A24Base", "DefinitionsApi$class", flags = Flags.ACC_PUBLIC | Flags.ACC_ABSTRACT) } def show(): Unit = { @@ -204,5 +341,12 @@ object Test extends BytecodeTest { testA15() testA16() testA17() + testA18() + testA19() + testA20() + testA21() + testA22() + testA23() + testA24() } } diff --git a/test/files/jvm/javaReflection.check b/test/files/jvm/javaReflection.check new file mode 100644 index 0000000000..aeb894f741 --- /dev/null +++ b/test/files/jvm/javaReflection.check @@ -0,0 +1,259 @@ +#partest !-Ydelambdafy:method +A$$anonfun$$lessinit$greater$1 / null (canon) / $anonfun$$lessinit$greater$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth) +- properties : true (local) / false (member) +A$$anonfun$$lessinit$greater$1$$anonfun$apply$1 / null (canon) / $anonfun$apply$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A$$anonfun$$lessinit$greater$1 (cls) / null (constr) / public final scala.Function0 A$$anonfun$$lessinit$greater$1.apply() (meth) +- properties : true (local) / false (member) +A$$anonfun$2 / null (canon) / $anonfun$2 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$$anonfun$3 / null (canon) / $anonfun$3 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$$anonfun$4 / null (canon) / $anonfun$4 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$$anonfun$f$1 / null (canon) / $anonfun$f$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$$anonfun$f$2 / null (canon) / $anonfun$f$2 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$D$$anonfun$1 / null (canon) / anonfun$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A$D$ (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +AO$$anonfun$5 / null (canon) / anonfun$5 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class AO$ (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +AT$$anonfun$6 / null (canon) / $anonfun$6 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / interface AT (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +#partest -Ydelambdafy:method +A$D$lambda$1 / A$D$lambda$1 (canon) / A$D$lambda$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$lambda$$$anonfun$7$1 / A$lambda$$$anonfun$7$1 (canon) / A$lambda$$$anonfun$7$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$lambda$$$lessinit$greater$1 / A$lambda$$$lessinit$greater$1 (canon) / A$lambda$$$lessinit$greater$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$lambda$$f$1 / A$lambda$$f$1 (canon) / A$lambda$$f$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$lambda$$f$2 / A$lambda$$f$2 (canon) / A$lambda$$f$2 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$lambda$1 / A$lambda$1 (canon) / A$lambda$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$lambda$2 / A$lambda$2 (canon) / A$lambda$2 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$lambda$3 / A$lambda$3 (canon) / A$lambda$3 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +AO$lambda$1 / AO$lambda$1 (canon) / AO$lambda$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +AT$class$lambda$1 / AT$class$lambda$1 (canon) / AT$class$lambda$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +#partest +A / A (canon) / A (simple) +- declared cls: List(class A$B, interface A$C, class A$D$) +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +A$$anon$1 / null (canon) / $anon$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$$anon$3 / null (canon) / $anon$3 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$$anon$4 / null (canon) / $anon$4 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$$anon$5 / null (canon) / $anon$5 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$$anon$6 / null (canon) / $anon$6 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$$anon$7 / null (canon) / $anon$7 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth) +- properties : true (local) / false (member) +A$B / A.B (canon) / B (simple) +- declared cls: List() +- enclosing : class A (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +A$C / A.C (canon) / C (simple) +- declared cls: List() +- enclosing : class A (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +A$D$ / A.D$ (canon) / D$ (simple) +- declared cls: List(class A$D$B, interface A$D$C, class A$D$D$) +- enclosing : class A (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +A$D$$anon$2 / null (canon) / anon$2 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A$D$ (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$D$B / Malformed class name (canon) / Malformed class name (simple) +- declared cls: List() +- enclosing : class A$D$ (declaring cls) / class A$D$ (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +A$D$C / Malformed class name (canon) / Malformed class name (simple) +- declared cls: List() +- enclosing : class A$D$ (declaring cls) / class A$D$ (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +A$D$D$ / Malformed class name (canon) / Malformed class name (simple) +- declared cls: List() +- enclosing : class A$D$ (declaring cls) / class A$D$ (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +A$D$KB$1 / null (canon) / Malformed class name (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A$D$ (cls) / null (constr) / public void A$D$.f() (meth) +- properties : Malformed class name (local) / false (member) +A$E$1 / null (canon) / E$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$F$1 / null (canon) / F$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$G$2$ / null (canon) / G$2$ (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$H$1 / null (canon) / H$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$I$1 / null (canon) / I$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$J$2$ / null (canon) / J$2$ (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / public java.lang.Object A.f() (meth) +- properties : true (local) / false (member) +A$K$1 / null (canon) / K$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$L$1 / null (canon) / L$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$M$2$ / null (canon) / M$2$ (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$N$1 / null (canon) / N$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$O$1 / null (canon) / O$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$P$2$ / null (canon) / P$2$ (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +A$Q$1 / null (canon) / Q$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth) +- properties : true (local) / false (member) +A$R$1 / null (canon) / R$1 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth) +- properties : true (local) / false (member) +A$S$2$ / null (canon) / S$2$ (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class A (cls) / public A(int) (constr) / null (meth) +- properties : true (local) / false (member) +AO / AO (canon) / AO (simple) +- declared cls: List(class AO$B, interface AO$C, class AO$D$) +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +AO$ / AO$ (canon) / AO$ (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +AO$$anon$8 / null (canon) / anon$8 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / class AO$ (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +AO$B / AO.B (canon) / B (simple) +- declared cls: List() +- enclosing : class AO (declaring cls) / class AO (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +AO$C / AO.C (canon) / C (simple) +- declared cls: List() +- enclosing : class AO (declaring cls) / class AO (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +AO$D$ / AO.D$ (canon) / D$ (simple) +- declared cls: List() +- enclosing : class AO (declaring cls) / class AO (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +AT / AT (canon) / AT (simple) +- declared cls: List(class AT$B, interface AT$C, class AT$D$) +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +AT$$anon$9 / null (canon) / $anon$9 (simple) +- declared cls: List() +- enclosing : null (declaring cls) / interface AT (cls) / null (constr) / null (meth) +- properties : true (local) / false (member) +AT$B / AT.B (canon) / B (simple) +- declared cls: List() +- enclosing : interface AT (declaring cls) / interface AT (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +AT$C / AT.C (canon) / C (simple) +- declared cls: List() +- enclosing : interface AT (declaring cls) / interface AT (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +AT$D$ / AT.D$ (canon) / D$ (simple) +- declared cls: List() +- enclosing : interface AT (declaring cls) / interface AT (cls) / null (constr) / null (meth) +- properties : false (local) / true (member) +AT$class / AT$class (canon) / AT$class (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +T / T (canon) / T (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) +T$class / T$class (canon) / T$class (simple) +- declared cls: List() +- enclosing : null (declaring cls) / null (cls) / null (constr) / null (meth) +- properties : false (local) / false (member) diff --git a/test/files/jvm/javaReflection/Classes_1.scala b/test/files/jvm/javaReflection/Classes_1.scala new file mode 100644 index 0000000000..11963e2770 --- /dev/null +++ b/test/files/jvm/javaReflection/Classes_1.scala @@ -0,0 +1,84 @@ +// See Test.scala for comments + +trait T { def f = 1 } + +class A { + // member class + class B + // member trait + trait C + // member object + object D { + class B + trait C + object D + new T { } + (() => -1) + def f = { class KB } + } + + // anonymous class, not a member + new T { } + + // anonymous function, not a member + (() => 1) + + def f = { + class E + trait F + object G + new T { } + (() => 2) + + if (new Object().hashCode == 1) { + class H + trait I + object J + new T { } + (() => 3) + } else { + () + } + } + + { + class K + trait L + object M + new T { } + (() => 4) + } + + val x = { + class N + trait O + object P + new T { } + (() => 5) + } + + def this(x: Int) { + this() + class Q + trait R + object S + new T { } + (() => () => 5) + } +} + +object AO { + class B + trait C + object D + new T { } + (() => 1) +} + +trait AT { + class B + trait C + object D + new T { } + (() => 1) +} diff --git a/test/files/jvm/javaReflection/Test.scala b/test/files/jvm/javaReflection/Test.scala new file mode 100644 index 0000000000..5b6ef1b573 --- /dev/null +++ b/test/files/jvm/javaReflection/Test.scala @@ -0,0 +1,137 @@ +/** +Interesting aspects of Java reflection applied to scala classes. TL;DR: you should not use +getSimpleName / getCanonicalName / isAnonymousClass / isLocalClass / isSynthetic. + + - Some methods in Java reflection assume a certain structure in the class names. Scalac + can produce class files that don't respect this structure. Certain methods in reflection + therefore give surprising answers or may even throw an exception. + + In particular, the method "getSimpleName" assumes that classes are named after the Java spec + http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.1 + + Consider the following Scala example: + class A { object B { class C } } + + The classfile for C has the name "A$B$C", while the classfile for the module B has the + name "A$B$". + + For "cClass.getSimpleName, the implementation first strips the name of the enclosing class, + which produces "C". The implementation then expects a "$" character, which is missing, and + throws an InternalError. + + Consider another example: + trait T + class A { val x = new T {} } + object B { val x = new T {} } + + The anonymous classes are named "A$$anon$1" and "B$$anon$2". If you call "getSimpleName", + you get "$anon$1" (leading $) and "anon$2" (no leading $). + + - There are certain other methods in the Java reflection API that depend on getSimpleName. + These should be avoided, they yield unexpected results: + + - isAnonymousClass is always false. Scala-defined classes are never anonymous for Java + reflection. Java reflection insepects the class name to decide whether a class is + anonymous, based on the name spec referenced above. + Also, the implementation of "isAnonymousClass" calls "getSimpleName", which may throw. + + - isLocalClass: should be true true for local classes (nested classes that are not + members), but not for anonymous classes. Since "isAnonymousClass" is always false, + Java reflection thinks that all Scala-defined anonymous classes are local. + The implementation may also throw, since it uses "isAnonymousClass": + class A { object B { def f = { class KB; new KB } } } + (new A).B.f.getClass.isLocalClass // boom + + - getCanonicalName: uses "getSimpleName" in the implementation. In the first example, + cClass.getCanonicalName also fails with an InternalError. + + - Scala-defined classes are never synthetic for Java reflection. The implementation + checks for the SYNTHETEIC flag, which does not seem to be added by scalac (maybe this + will change some day). +*/ + +object Test { + + def tr[T](m: => T): String = try { + val r = m + if (r == null) "null" + else r.toString + } catch { case e: InternalError => e.getMessage } + + def assertNotAnonymous(c: Class[_]) = { + val an = try { + c.isAnonymousClass + } catch { + // isAnonymousClass is implemented using getSimpleName, which may throw. + case e: InternalError => false + } + assert(!an, c) + } + + def ruleMemberOrLocal(c: Class[_]) = { + // if it throws, then it's because of the call from isLocalClass to isAnonymousClass. + // we know that isAnonymousClass is always false, so it has to be a local class. + val loc = try { c.isLocalClass } catch { case e: InternalError => true } + if (loc) + assert(!c.isMemberClass, c) + if (c.isMemberClass) + assert(!loc, c) + } + + def ruleMemberDeclaring(c: Class[_]) = { + if (c.isMemberClass) + assert(c.getDeclaringClass.getDeclaredClasses.toList.map(_.getName) contains c.getName) + } + + def ruleScalaAnonClassIsLocal(c: Class[_]) = { + if (c.getName contains "$anon$") + assert(c.isLocalClass, c) + } + + def ruleScalaAnonFunInlineIsLocal(c: Class[_]) = { + // exclude lambda classes generated by delambdafy:method. nested closures have both "anonfun" and "lambda". + if (c.getName.contains("$anonfun$") && !c.getName.contains("$lambda$")) + assert(c.isLocalClass, c) + } + + def ruleScalaAnonFunMethodIsToplevel(c: Class[_]) = { + if (c.getName.contains("$lambda$")) + assert(c.getEnclosingClass == null, c) + } + + def showClass(name: String) = { + val c = Class.forName(name) + + println(s"${c.getName} / ${tr(c.getCanonicalName)} (canon) / ${tr(c.getSimpleName)} (simple)") + println( "- declared cls: "+ c.getDeclaredClasses.toList.sortBy(_.getName)) + println(s"- enclosing : ${c.getDeclaringClass} (declaring cls) / ${c.getEnclosingClass} (cls) / ${c.getEnclosingConstructor} (constr) / ${c.getEnclosingMethod} (meth)") + println(s"- properties : ${tr(c.isLocalClass)} (local) / ${c.isMemberClass} (member)") + + assertNotAnonymous(c) + assert(!c.isSynthetic, c) + + ruleMemberOrLocal(c) + ruleMemberDeclaring(c) + ruleScalaAnonClassIsLocal(c) + ruleScalaAnonFunInlineIsLocal(c) + ruleScalaAnonFunMethodIsToplevel(c) + } + + def main(args: Array[String]): Unit = { + def isAnonFunClassName(s: String) = s.contains("$anonfun$") || s.contains("$lambda$") + + val classfiles = new java.io.File(sys.props("partest.output")).listFiles().toList.map(_.getName).collect({ + // exclude files from Test.scala, just take those from Classes_1.scala + case s if !s.startsWith("Test") && s.endsWith(".class") => s.substring(0, s.length - 6) + }).sortWith((a, b) => { + // sort such that first there are all anonymous funcitions, then all other classes. + // within those cathegories, sort lexically. + // this makes the check file smaller: it differs for anonymous functions between -Ydelambdafy:inline/method. + // the other classes are the same. + if (isAnonFunClassName(a)) !isAnonFunClassName(b) || a < b + else !isAnonFunClassName(b) && a < b + }) + + classfiles foreach showClass + } +}
\ No newline at end of file diff --git a/test/files/run/t5256c.check b/test/files/run/t5256c.check index 7fcd0eb722..3eb7b13a97 100644 --- a/test/files/run/t5256c.check +++ b/test/files/run/t5256c.check @@ -2,5 +2,5 @@ class A$1 Test.A$1 java.lang.Object { def foo(): Nothing - def <init>(): A$1 + def <init>(): Test.A$1 } |