|
| 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