diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2015-01-21 19:57:45 +0100 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2015-02-07 07:45:47 +0100 |
commit | 0bcc871e3f4c4331fb53ec0e7087669589a607d6 (patch) | |
tree | 86af55ac347552c35a8d5b9bf1a6085e9177b3fb /test | |
parent | d7b11520222c9e995d27de46fecc19c5a1fa5b74 (diff) | |
download | scala-0bcc871e3f4c4331fb53ec0e7087669589a607d6.tar.gz scala-0bcc871e3f4c4331fb53ec0e7087669589a607d6.tar.bz2 scala-0bcc871e3f4c4331fb53ec0e7087669589a607d6.zip |
SI-9105 Fix EnclosingMethod for classes defined in lambdas
This change fixes both GenASM and GenBCode, except for the change
to renaming in LamdaLift mentioned below.
The reason for an inconsistent EnclosingMethod attribute was the
symbol owner chain. Initially, closure class symbols don't exist, they
are only created in UnCurry (delambdafy:inline). So walking the
originalOwner of a definition does not yield closure classes.
The commit also fixes uses of isAnonymousClass, isAnonymousFunction
and isDelambdafyFunction in two ways:
1. by phase-travelling to an early phase. after flatten, the name
includes the name of outer classes, so the properties may become
accidentally true (they check for a substring in the name)
2. by ensuring that the (destructive) renames during LambdaLift
don't make the above properties accidentally true. This was in
fact the cause for SI-8900.
Diffstat (limited to 'test')
-rw-r--r-- | test/files/jvm/innerClassAttribute.check | 30 | ||||
-rw-r--r-- | test/files/jvm/innerClassAttribute/Classes_1.scala | 38 | ||||
-rw-r--r-- | test/files/jvm/innerClassAttribute/Test.scala | 78 | ||||
-rw-r--r-- | test/files/jvm/t9105.check | 18 | ||||
-rw-r--r-- | test/files/jvm/t9105.scala | 22 |
5 files changed, 167 insertions, 19 deletions
diff --git a/test/files/jvm/innerClassAttribute.check b/test/files/jvm/innerClassAttribute.check index 20518aa49e..8395ff0b09 100644 --- a/test/files/jvm/innerClassAttribute.check +++ b/test/files/jvm/innerClassAttribute.check @@ -14,27 +14,27 @@ A19 / null / null A19 / null / null A19 / null / null -- A20 -- -A20$$anonfun$4 / null / null / 17 +A20$$anonfun$6 / 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 +A20$$anonfun$6 / null / null / 17 +A20$$anonfun$6$$anonfun$apply$1 / null / null / 17 +A20$$anonfun$6$$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 +A20$$anonfun$6 / null / null / 17 +A20$$anonfun$6$$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 +A20$$anonfun$6 / null / null / 17 +A20$$anonfun$6$$anonfun$apply$3 / null / null / 17 +A20$$anonfun$6$$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 +A20$$anonfun$6 / null / null / 17 +A20$$anonfun$6$$anonfun$apply$3 / null / null / 17 +A20$$anonfun$6$$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; +A20$$anonfun$6 / apply / ()Lscala/Function0; +A20$$anonfun$6 / apply / ()Lscala/Function0; +A20$$anonfun$6$$anonfun$apply$3 / apply / ()Lscala/Function0; #partest -Ydelambdafy:method -- A4 -- null / null / null diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala index 9c3ea7f013..a2ade70c18 100644 --- a/test/files/jvm/innerClassAttribute/Classes_1.scala +++ b/test/files/jvm/innerClassAttribute/Classes_1.scala @@ -185,3 +185,41 @@ trait A24 extends A24Base { override object Conc extends A24Sym } } + +class SI_9105 { + // the EnclosingMethod attributes depend on the delambdafy strategy (inline vs method) + + // outerClass-inline enclMeth-inline outerClass-method enclMeth-method + val fun = () => { + class A // closure null (*) SI_9105 null + def m: Object = { class B; new B } // closure m$1 SI_9105 m$1 + val f: Object = { class C; new C } // closure null (*) SI_9105 null + } + def met = () => { + class D // closure null (*) SI_9105 met + def m: Object = { class E; new E } // closure m$1 SI_9105 m$1 + val f: Object = { class F; new F } // closure null (*) SI_9105 met + } + + // (*) the originalOwner chain of A (similar for D) is: SI_9105.fun.$anonfun-value.A + // we can get to the anonfun-class (created by uncurry), but not to the apply method. + // + // for C and F, the originalOwner chain is fun.$anonfun-value.f.C. at later phases, the rawowner of f is + // an apply$sp method of the closure class. we could use that as enclosing method, but it would be unsystematic + // (A / D don't have an encl meth either), and also strange to use the $sp, which is a compilation artifact. + // So using `null` looks more like the situation in the source code: C / F are nested classes of the anon-fun, and + // there's no method in between. + + def byName[T](op: => T) = 0 + + val bnV = byName { + class G // closure null (*) SI_9105 null + def m: Object = { class H; new H } // closure m$1 SI_9105 m$1 + val f: Object = { class I; new I } // closure null (*) SI_9105 null + } + def bnM = byName { + class J // closure null (*) SI_9105 bnM + def m: Object = { class K; new K } // closure m$1 SI_9105 m$1 + val f: Object = { class L; new L } // closure null (*) SI_9105 bnM + } +} diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala index 3820048cb4..30dc412ff7 100644 --- a/test/files/jvm/innerClassAttribute/Test.scala +++ b/test/files/jvm/innerClassAttribute/Test.scala @@ -16,6 +16,8 @@ object Test extends BytecodeTest { loadClassNode(className).innerClasses.asScala.toList.sortBy(_.name) } + def ownInnerClassNode(n: String) = innerClassNodes(n).filter(_.name == n).head + final case class EnclosingMethod(name: String, descriptor: String, outerClass: String) def enclosingMethod(className: String) = { val n = loadClassNode(className) @@ -256,10 +258,10 @@ object Test extends BytecodeTest { printInnerClassNodes("A20") - val fun1 = lambdaClass("A20$$anonfun$4", "A20$lambda$1") - val fun2 = lambdaClass("A20$$anonfun$4$$anonfun$apply$1", "A20$lambda$$$nestedInAnonfun$5$1") - val fun3 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3", "A20$lambda$$$nestedInAnonfun$5$2") - val fun4 = lambdaClass("A20$$anonfun$4$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$nestedInAnonfun$7$1") + val fun1 = lambdaClass("A20$$anonfun$6", "A20$lambda$1") + val fun2 = lambdaClass("A20$$anonfun$6$$anonfun$apply$1", "A20$lambda$$$nestedInAnonfun$5$1") + val fun3 = lambdaClass("A20$$anonfun$6$$anonfun$apply$3", "A20$lambda$$$nestedInAnonfun$5$2") + val fun4 = lambdaClass("A20$$anonfun$6$$anonfun$apply$3$$anonfun$apply$2", "A20$lambda$$$nestedInAnonfun$7$1") println("fun1: attribute for itself and the two child closures `() => ()` and `() => () => 1`") printInnerClassNodes(fun1) @@ -324,6 +326,73 @@ object Test extends BytecodeTest { assertMember(defsApiImpl, "A24Base", "DefinitionsApi$class", flags = Flags.ACC_PUBLIC | Flags.ACC_ABSTRACT) } + def testSI_9105() { + val isDelambdafyMethod = classpath.findClass("SI_9105$lambda$1").isDefined + if (isDelambdafyMethod) { + assertEnclosingMethod ("SI_9105$A$2" , "SI_9105", null , null) + assertEnclosingMethod ("SI_9105$B$5" , "SI_9105", "m$1", "()Ljava/lang/Object;") + assertEnclosingMethod ("SI_9105$C$1" , "SI_9105", null , null) + assertEnclosingMethod ("SI_9105$D$1" , "SI_9105", "met", "()Lscala/Function0;") + assertEnclosingMethod ("SI_9105$E$1" , "SI_9105", "m$3", "()Ljava/lang/Object;") + assertEnclosingMethod ("SI_9105$F$1" , "SI_9105", "met", "()Lscala/Function0;") + assertNoEnclosingMethod("SI_9105$lambda$$met$1") + assertNoEnclosingMethod("SI_9105$lambda$1") + assertNoEnclosingMethod("SI_9105") + + assertLocal(innerClassNodes("SI_9105$A$2").head, "SI_9105$A$2", "A$2") + assertLocal(innerClassNodes("SI_9105$B$5").head, "SI_9105$B$5", "B$5") + assertLocal(innerClassNodes("SI_9105$C$1").head, "SI_9105$C$1", "C$1") + assertLocal(innerClassNodes("SI_9105$D$1").head, "SI_9105$D$1", "D$1") + assertLocal(innerClassNodes("SI_9105$E$1").head, "SI_9105$E$1", "E$1") + assertLocal(innerClassNodes("SI_9105$F$1").head, "SI_9105$F$1", "F$1") + + // by-name + assertEnclosingMethod("SI_9105$G$1", "SI_9105", null , null) + assertEnclosingMethod("SI_9105$H$1", "SI_9105", "m$2", "()Ljava/lang/Object;") + assertEnclosingMethod("SI_9105$I$1", "SI_9105", null , null) + assertEnclosingMethod("SI_9105$J$1", "SI_9105", "bnM", "()I") + assertEnclosingMethod("SI_9105$K$2", "SI_9105", "m$4", "()Ljava/lang/Object;") + assertEnclosingMethod("SI_9105$L$1", "SI_9105", "bnM", "()I") + + assert(innerClassNodes("SI_9105$lambda$$met$1").isEmpty) + assert(innerClassNodes("SI_9105$lambda$1").isEmpty) + assert(innerClassNodes("SI_9105").length == 12) // the 12 local classes + } else { + // comment in innerClassAttribute/Classes_1.scala explains the difference between A / C and D / F. + assertEnclosingMethod ("SI_9105$$anonfun$4$A$2" , "SI_9105$$anonfun$4" , null , null) + assertEnclosingMethod ("SI_9105$$anonfun$4$B$5" , "SI_9105$$anonfun$4" , "m$1" , "()Ljava/lang/Object;") + assertEnclosingMethod ("SI_9105$$anonfun$4$C$1" , "SI_9105$$anonfun$4" , null , null) + assertEnclosingMethod ("SI_9105$$anonfun$met$1$D$1", "SI_9105$$anonfun$met$1", null , null) + assertEnclosingMethod ("SI_9105$$anonfun$met$1$E$1", "SI_9105$$anonfun$met$1", "m$3" , "()Ljava/lang/Object;") + assertEnclosingMethod ("SI_9105$$anonfun$met$1$F$1", "SI_9105$$anonfun$met$1", null , null) + assertEnclosingMethod ("SI_9105$$anonfun$4" , "SI_9105" , null , null) + assertEnclosingMethod ("SI_9105$$anonfun$met$1" , "SI_9105" , "met" , "()Lscala/Function0;") + assertNoEnclosingMethod("SI_9105") + + assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$A$2"), "SI_9105$$anonfun$4$A$2" , "A$2") + assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$B$5"), "SI_9105$$anonfun$4$B$5" , "B$5") + assertLocal(ownInnerClassNode("SI_9105$$anonfun$4$C$1"), "SI_9105$$anonfun$4$C$1" , "C$1") + assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$D$1"), "SI_9105$$anonfun$met$1$D$1", "D$1") + assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$E$1"), "SI_9105$$anonfun$met$1$E$1", "E$1") + assertLocal(ownInnerClassNode("SI_9105$$anonfun$met$1$F$1"), "SI_9105$$anonfun$met$1$F$1", "F$1") + + // by-name + assertEnclosingMethod("SI_9105$$anonfun$5$G$1", "SI_9105$$anonfun$5", null, null) + assertEnclosingMethod("SI_9105$$anonfun$5$H$1", "SI_9105$$anonfun$5", "m$2", "()Ljava/lang/Object;") + assertEnclosingMethod("SI_9105$$anonfun$5$I$1", "SI_9105$$anonfun$5", null, null) + assertEnclosingMethod("SI_9105$$anonfun$bnM$1$J$1", "SI_9105$$anonfun$bnM$1", null, null) + assertEnclosingMethod("SI_9105$$anonfun$bnM$1$K$2", "SI_9105$$anonfun$bnM$1", "m$4", "()Ljava/lang/Object;") + assertEnclosingMethod("SI_9105$$anonfun$bnM$1$L$1", "SI_9105$$anonfun$bnM$1", null, null) + + assertAnonymous(ownInnerClassNode("SI_9105$$anonfun$4"), "SI_9105$$anonfun$4") + assertAnonymous(ownInnerClassNode("SI_9105$$anonfun$met$1"), "SI_9105$$anonfun$met$1") + + assert(innerClassNodes("SI_9105$$anonfun$4").length == 4) // itself and three of the local classes + assert(innerClassNodes("SI_9105$$anonfun$met$1").length == 4) // itself and three of the local classes + assert(innerClassNodes("SI_9105").length == 4) // the four anon funs + } + } + def show(): Unit = { testA1() testA2() @@ -347,5 +416,6 @@ object Test extends BytecodeTest { testA22() testA23() testA24() + testSI_9105() } } diff --git a/test/files/jvm/t9105.check b/test/files/jvm/t9105.check new file mode 100644 index 0000000000..34750833f1 --- /dev/null +++ b/test/files/jvm/t9105.check @@ -0,0 +1,18 @@ +#partest !-Ydelambdafy:method +(class C$$anonfun$1$A$1,class C$$anonfun$1,null) +(class C$$anonfun$1$B$1,class C$$anonfun$1,private final java.lang.Object C$$anonfun$1.m$1()) +(class C$$anonfun$1$C$1,class C$$anonfun$1,null) +(class C$$anonfun$1$$anonfun$2$D$1,class C$$anonfun$1$$anonfun$2,null) +(class C$$anonfun$met$1$E$1,class C$$anonfun$met$1,null) +(class C$$anonfun$met$1$F$1,class C$$anonfun$met$1,private final java.lang.Object C$$anonfun$met$1.m$2()) +(class C$$anonfun$met$1$G$1,class C$$anonfun$met$1,null) +(class C$$anonfun$met$1$$anonfun$3$H$1,class C$$anonfun$met$1$$anonfun$3,null) +#partest -Ydelambdafy:method +(class C$A$1,class C,null) +(class C$B$1,class C,private final java.lang.Object C.m$1()) +(class C$C$1,class C,null) +(class C$D$1,class C,null) +(class C$E$1,class C,public scala.Function0 C.met()) +(class C$F$1,class C,private final java.lang.Object C.m$2()) +(class C$G$1,class C,public scala.Function0 C.met()) +(class C$H$1,class C,public scala.Function0 C.met()) diff --git a/test/files/jvm/t9105.scala b/test/files/jvm/t9105.scala new file mode 100644 index 0000000000..636ee8a768 --- /dev/null +++ b/test/files/jvm/t9105.scala @@ -0,0 +1,22 @@ +class C { + val fun = () => { + class A + def m: Object = { class B; new B } + val f: Object = { class C; new C } + val g = () => { class D; new D } + List[Object](new A, m, f, g()) + } + def met = () => { + class E + def m: Object = { class F; new F } + val f: Object = { class G; new G } + val g = () => { class H; new H } + List[Object](new E, m, f, g()) + } +} + +object Test extends App { + val x = new C().fun.apply() ::: new C().met.apply() + val results = x.map(_.getClass).map(cls => (cls, cls.getEnclosingClass, cls.getEnclosingMethod)) + println(results.mkString("\n")) +} |