Skip to content

Commit 7a69474

Browse files
committed
Merge pull request scala#3927 from lrytz/innerClassesTest
test for InnerClass and EnclosingMethod attributes
2 parents eb7b834 + 0270972 commit 7a69474

File tree

3 files changed

+310
-0
lines changed

3 files changed

+310
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
class A1 {
2+
class B
3+
}
4+
5+
class A2 {
6+
object B
7+
}
8+
9+
object A3 {
10+
class B1
11+
object B2
12+
}
13+
14+
class A4 {
15+
def f(l: List[Int]): List[Int] = {
16+
l map (_ + 1)
17+
}
18+
}
19+
20+
class A5 {
21+
def f(): Object = {
22+
object B
23+
B
24+
}
25+
}
26+
27+
trait A6 {
28+
def hui = -6
29+
trait TT
30+
}
31+
32+
class A7 extends A6
33+
34+
abstract class A8 extends A6 {
35+
def fish: TT
36+
}
37+
38+
class A9 {
39+
class brick extends annotation.StaticAnnotation
40+
}
41+
42+
class A10 {
43+
val a9 = new A9()
44+
// there's no reference to brick in the bytecode (only in the pickle), so there's no InnerClass attribute for it.
45+
@a9.brick def f = -7
46+
}
47+
48+
class A11 {
49+
@JavaAnnot_1.Ann def f = -8
50+
}
51+
52+
object A12 {
53+
object B {
54+
class C
55+
}
56+
}
57+
58+
class A13 {
59+
def oak: A12.B.C = new A12.B.C
60+
}
61+
62+
class A14 {
63+
def f = {
64+
val x: Object = {
65+
class K
66+
new K
67+
}
68+
x
69+
}
70+
def g = {
71+
val x: Object = new A6 { }
72+
}
73+
}
74+
75+
object A15 {
76+
def f = {
77+
class B { // static (does not have an outer pointer)
78+
class C // non-static
79+
}
80+
}
81+
}
82+
83+
class A16 {
84+
val x: A6 = {
85+
class U extends A6
86+
new A6 { }
87+
}
88+
89+
{
90+
class V extends A6
91+
new A6 { }
92+
}
93+
}
94+
95+
class A17 {
96+
object B {
97+
class C // not static, has an outer pointer.
98+
}
99+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public class JavaAnnot_1 {
2+
public static @interface Ann {}
3+
}
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import scala.tools.partest.BytecodeTest
2+
import scala.tools.asm
3+
import asm.tree.{ClassNode, InnerClassNode}
4+
import asm.{Opcodes => Flags}
5+
import scala.collection.JavaConverters._
6+
7+
object Test extends BytecodeTest {
8+
def assertSame(a: Any, b: Any) = {
9+
assert(a == b, s"\na: $a\nb: $b")
10+
}
11+
12+
val publicStatic = Flags.ACC_PUBLIC | Flags.ACC_STATIC
13+
val publicAbstractInterface = Flags.ACC_PUBLIC | Flags.ACC_ABSTRACT | Flags.ACC_INTERFACE
14+
15+
def innerClassNodes(className: String): List[InnerClassNode] = {
16+
loadClassNode(className).innerClasses.asScala.toList.sortBy(_.name)
17+
}
18+
19+
final case class EnclosingMethod(name: String, descriptor: String, outerClass: String)
20+
def enclosingMethod(className: String) = {
21+
val n = loadClassNode(className)
22+
EnclosingMethod(n.outerMethod, n.outerMethodDesc, n.outerClass)
23+
}
24+
25+
def assertMember(node: InnerClassNode, outer: String, inner: String, name: Option[String] = None, flags: Int = Flags.ACC_PUBLIC) = {
26+
assertSame(node.name, name.getOrElse(s"$outer$$$inner"))
27+
assertSame(node.outerName, outer)
28+
assertSame(node.innerName, inner)
29+
assertSame(node.access, flags)
30+
}
31+
32+
def assertAnonymous(node: InnerClassNode, name: String, flags: Int = Flags.ACC_PUBLIC | Flags.ACC_FINAL) = {
33+
assertSame(node.name, name)
34+
assertSame(node.outerName, null)
35+
assertSame(node.innerName, null)
36+
assertSame(node.access, flags)
37+
}
38+
39+
def assertLocal(node: InnerClassNode, name: String, inner: String, flags: Int = Flags.ACC_PUBLIC) = {
40+
assertSame(node.name, name)
41+
assertSame(node.outerName, null)
42+
assertSame(node.innerName, inner)
43+
assertSame(node.access, flags)
44+
}
45+
46+
def assertEnclosingMethod(enclosingMethod: EnclosingMethod, outerClass: String, name: String, descriptor: String) = {
47+
assertSame(enclosingMethod.outerClass, outerClass)
48+
assertSame(enclosingMethod.name, name)
49+
assertSame(enclosingMethod.descriptor, descriptor)
50+
}
51+
52+
def testA1() = {
53+
val List(b1) = innerClassNodes("A1")
54+
assertMember(b1, "A1", "B")
55+
val List(b2) = innerClassNodes("A1$B")
56+
assertMember(b2, "A1", "B")
57+
}
58+
59+
def testA2() = {
60+
val List(b1) = innerClassNodes("A2")
61+
assertMember(b1, "A2", "B$")
62+
val List(b2) = innerClassNodes("A2$B$")
63+
assertMember(b2, "A2", "B$")
64+
}
65+
66+
def testA3() = {
67+
def t(c: String) = {
68+
val List(b1, b2) = innerClassNodes(c)
69+
// the outer class for classes nested inside top-level modules is not the module class, but the mirror class.
70+
// this is a hack for java interop, handled in the backend. see BTypes.scala, comment on "Java Compatibility".
71+
assertMember(b1, "A3", "B1", flags = publicStatic)
72+
assertMember(b2, "A3", "B2$", flags = publicStatic)
73+
}
74+
t("A3$")
75+
// the mirror class has the same inner class attributes as the module
76+
// class (added when the mirror is created in the backend)
77+
t("A3")
78+
}
79+
80+
def testA4() = {
81+
val List(an1) = innerClassNodes("A4")
82+
assertAnonymous(an1, "A4$$anonfun$f$1")
83+
val List(an2) = innerClassNodes("A4$$anonfun$f$1")
84+
assertAnonymous(an2, "A4$$anonfun$f$1")
85+
assertEnclosingMethod(
86+
enclosingMethod("A4$$anonfun$f$1"),
87+
"A4", "f", "(Lscala/collection/immutable/List;)Lscala/collection/immutable/List;")
88+
}
89+
90+
def testA5() = {
91+
val List(b1) = innerClassNodes("A5")
92+
assertLocal(b1, "A5$B$2$", "B$2$")
93+
val List(b2) = innerClassNodes("A5$B$2$")
94+
assertLocal(b2, "A5$B$2$", "B$2$")
95+
assertEnclosingMethod(
96+
enclosingMethod("A5$B$2$"),
97+
"A5", "f", "()Ljava/lang/Object;")
98+
}
99+
100+
def testA6() = {
101+
val List(tt1) = innerClassNodes("A6")
102+
assertMember(tt1, "A6", "TT", flags = publicAbstractInterface)
103+
val List() = innerClassNodes("A6$class")
104+
val List(tt2) = innerClassNodes("A6$TT")
105+
assertMember(tt2, "A6", "TT", flags = publicAbstractInterface)
106+
}
107+
108+
def testA7() = {
109+
val List() = innerClassNodes("A7")
110+
}
111+
112+
def testA8() = {
113+
val List(tt) = innerClassNodes("A8")
114+
assertMember(tt, "A6", "TT", flags = publicAbstractInterface)
115+
}
116+
117+
def testA10() = {
118+
val List() = innerClassNodes("A10")
119+
}
120+
121+
def testA11() = {
122+
val List(ann) = innerClassNodes("A11")
123+
// in the java class file, the INNERCLASS attribute has more flags (public | static | abstract | interface | annotation)
124+
// the scala compiler has its own interpretation of java annotations ant their flags.. it only emits publicStatic.
125+
assertMember(ann, "JavaAnnot_1", "Ann", flags = publicStatic)
126+
}
127+
128+
def testA13() = {
129+
val List(b, c) = innerClassNodes("A13")
130+
assertMember(b, "A12", "B$", flags = publicStatic)
131+
assertMember(c, "A12$B$", "C", name = Some("A12$B$C"), flags = publicStatic)
132+
}
133+
134+
def testA14() = {
135+
val List(anon, k) = innerClassNodes("A14")
136+
137+
assertLocal(k, "A14$K$1", "K$1")
138+
assertEnclosingMethod(
139+
enclosingMethod("A14$K$1"),
140+
"A14", "f", "()Ljava/lang/Object;")
141+
142+
assertAnonymous(anon, "A14$$anon$1")
143+
assertEnclosingMethod(
144+
enclosingMethod("A14$$anon$1"),
145+
"A14", "g", "()V")
146+
}
147+
148+
def testA15() = {
149+
val List(b) = innerClassNodes("A15")
150+
assertLocal(b, "A15$B$3", "B$3", flags = publicStatic)
151+
152+
val List(_, c) = innerClassNodes("A15$B$3")
153+
// TODO this is a bug in the backend, C should be a member. Instead, its outerClass is null
154+
// assertMember(c, "A15$B$3", "C")
155+
assertLocal(c, "A15$B$3$C", "C")
156+
}
157+
158+
def testA16() = {
159+
val List(anon1, anon2, u, v) = innerClassNodes("A16")
160+
// TODO there's a bug in the backend: anon$2 has outerClass A16, but anonymous classes should have outerClass null
161+
// assertAnonymous(anon1, "A16$$anon$2")
162+
assertMember(anon1, "A16", null, name = Some("A16$$anon$2"), flags = Flags.ACC_PUBLIC | Flags.ACC_FINAL)
163+
assertAnonymous(anon2, "A16$$anon$3")
164+
// TODO this is a bug in the backend, U should not be a member, its outerClass should be null
165+
// assertLocal(u, "A16$U$1", "U$1")
166+
assertMember(u, "A16", "U$1")
167+
assertLocal(v, "A16$V$1", "V$1")
168+
169+
assertEnclosingMethod(
170+
enclosingMethod("A16$$anon$2"),
171+
"A16", "<init>", "()V")
172+
assertEnclosingMethod(
173+
enclosingMethod("A16$$anon$3"),
174+
"A16", "<init>", "()V")
175+
// TODO this is a bug, there should be an enclosingMethod attribute in U
176+
// assertEnclosingMethod(
177+
// enclosingMethod("A16$U$1"),
178+
// "A16", "<init>", "()V")
179+
assertEnclosingMethod(
180+
enclosingMethod("A16$V$1"),
181+
"A16", "<init>", "()V")
182+
}
183+
184+
def testA17() = {
185+
val List(b, c) = innerClassNodes("A17$B$")
186+
assertMember(b, "A17", "B$")
187+
// TODO this is a bug, should not be static.
188+
assertMember(c, "A17$B$", "C", name = Some("A17$B$C"), flags = publicStatic) // (should be) not static, has an outer pointer.
189+
}
190+
191+
def show(): Unit = {
192+
testA1()
193+
testA2()
194+
testA3()
195+
testA4()
196+
testA5()
197+
testA6()
198+
testA7()
199+
testA8()
200+
testA10()
201+
testA11()
202+
testA13()
203+
testA14()
204+
testA15()
205+
testA16()
206+
testA17()
207+
}
208+
}

0 commit comments

Comments
 (0)