Skip to content

Commit 04d367e

Browse files
authored
Merge pull request #11589 from dotty-staging/fix-inner-class-table-generation
Fix #4192: Properly set InnerClasses end EnclosingMethod in class files
2 parents de9567f + 4d03ef9 commit 04d367e

File tree

5 files changed

+458
-4
lines changed

5 files changed

+458
-4
lines changed

Diff for: compiler/src/dotty/tools/backend/jvm/BCodeAsmCommon.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ final class BCodeAsmCommon[I <: DottyBackendInterface](val interface: I) {
2626
// always top-level. However, SI-8900 shows an example where the weak name-based implementation
2727
// of isDelambdafyFunction failed (for a function declared in a package named "lambda").
2828
classSym.isAnonymousClass || {
29-
val originalOwnerLexicallyEnclosingClass = classSym.originalOwner.originalLexicallyEnclosingClass
30-
originalOwnerLexicallyEnclosingClass != NoSymbol && !originalOwnerLexicallyEnclosingClass.isClass
29+
val originalOwner = classSym.originalOwner
30+
originalOwner != NoSymbol && !originalOwner.isClass
3131
}
3232
}
3333

@@ -59,9 +59,9 @@ final class BCodeAsmCommon[I <: DottyBackendInterface](val interface: I) {
5959
def enclosingMethod(sym: Symbol): Option[Symbol] = {
6060
if (sym.isClass || sym == NoSymbol) None
6161
else if (sym.is(Method)) Some(sym)
62-
else enclosingMethod(sym.originalOwner.originalLexicallyEnclosingClass)
62+
else enclosingMethod(sym.originalOwner)
6363
}
64-
enclosingMethod(classSym.originalOwner.originalLexicallyEnclosingClass)
64+
enclosingMethod(classSym.originalOwner)
6565
}
6666

6767
/**

Diff for: compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala

+5
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,11 @@ trait BCodeSkelBuilder extends BCodeHelpers {
221221
addClassFields()
222222

223223
innerClassBufferASM ++= classBTypeFromSymbol(claszSymbol).info.memberClasses
224+
225+
val companion = claszSymbol.companionClass
226+
if companion.isTopLevelModuleClass then
227+
innerClassBufferASM ++= classBTypeFromSymbol(companion).info.memberClasses
228+
224229
gen(cd.rhs)
225230
addInnerClassesASM(cnode, innerClassBufferASM.toList)
226231

Diff for: tests/run/i4192/Checks.scala

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package checks
2+
3+
import reflect.ClassTag
4+
5+
6+
trait Checks:
7+
val expectedTopLevelChecksCount: Int
8+
val expectedMemberChecksCount: Int
9+
val expectedLocalChecksCount: Int
10+
11+
var topLevelChecksCount = 0
12+
var memberChecksCount = 0
13+
var localChecksCount = 0
14+
15+
def verifyChecksCounts() =
16+
assert(topLevelChecksCount == expectedTopLevelChecksCount,
17+
s"top level checks: expected $expectedTopLevelChecksCount but was $topLevelChecksCount")
18+
assert(memberChecksCount == expectedMemberChecksCount,
19+
s"member checks: expected $expectedMemberChecksCount but was $memberChecksCount")
20+
assert(localChecksCount == expectedLocalChecksCount,
21+
s"local checks: expected $expectedLocalChecksCount but was $localChecksCount")
22+
23+
// The methods below rely on the naming convention described in TestCases.scala
24+
25+
/** Check JVM class properties of a top level class */
26+
def checkTopLevel(self: AnyRef) =
27+
val cls = self.getClass
28+
assert(cls.getEnclosingClass == null, s"Top level class $cls should have no enclosing class")
29+
assert(cls.getDeclaringClass == null, s"Top level class $cls should have no declaring class")
30+
assert(cls.getEnclosingMethod == null, s"Top level class $cls should have no enclosing method")
31+
topLevelChecksCount += 1
32+
33+
/** Check JVM class properties of a member class (defined directly inside another class) */
34+
def checkMember(self: AnyRef, outer: AnyRef) =
35+
val cls = self.getClass
36+
def className = cls.simpleName
37+
def enclosingClassName = cls.getEnclosingClass.simpleName
38+
def declaringClassName = cls.getDeclaringClass.simpleName
39+
// Classes defined directly in top level objects should be moved to their companion/mirror classes
40+
val expectedEnclosingClassName = outer.getClass.simpleName match
41+
case "B$" => "B"
42+
case "C$" => "C"
43+
case name => name
44+
assert(cls.getEnclosingClass != null,
45+
s"Member class $className should have an enclosing class")
46+
assert(enclosingClassName == expectedEnclosingClassName,
47+
s"The enclosing class of class $className should be $expectedEnclosingClassName but was $enclosingClassName")
48+
assert(cls.getDeclaringClass == cls.getEnclosingClass,
49+
s"The declaring class of class $className should be the same as its enclosing class but was $declaringClassName")
50+
assert(cls.getEnclosingMethod == null,
51+
s"Member class $className should have no enclosing method")
52+
memberChecksCount += 1
53+
54+
/** Check JVM class properties of a local class (defined directly inside a method) */
55+
def checkLocal(self: AnyRef, outer: AnyRef) =
56+
val cls = self.getClass
57+
def className = cls.simpleName
58+
def enclosingClassName = cls.getEnclosingClass.simpleName
59+
def method = cls.getEnclosingMethod
60+
val expectedEnclosingClassName = outer.getClass.simpleName
61+
// extracting method name basing on the described naming convention
62+
// $1 gets added during lambdaLift in case of a method defined inside another method
63+
val expectedEnclosingMethodName =
64+
val prefix = className.init
65+
val suffix = if prefix.filter(_.isLetter).endsWith("DD") then "$1" else ""
66+
prefix ++ suffix
67+
assert(cls.getEnclosingClass != null,
68+
s"Local class $className should have an enclosing class")
69+
assert(enclosingClassName == expectedEnclosingClassName,
70+
s"The enclosing class of class $className should be $expectedEnclosingClassName but was $enclosingClassName")
71+
assert(cls.getDeclaringClass == null,
72+
s"Local class $className should have no declaring class")
73+
assert(method != null,
74+
s"Local class $className should have an enclosing method")
75+
assert(method.getName == expectedEnclosingMethodName,
76+
s"The enclosing method of class $className should be $expectedEnclosingMethodName but was ${method.getName}")
77+
localChecksCount += 1
78+
79+
extension (cls: Class[?])
80+
// java 8 implementation of cls.getSimpleName() is buggy - https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8057919
81+
def simpleName = cls.getName
82+
.stripSuffix("$1").stripSuffix("$2")
83+
.split("\\.").last
84+
.split("\\$").last

Diff for: tests/run/i4192/Test.scala

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test {
2+
def main(args: Array[String]): Unit = {
3+
foo.bar.Checks.run()
4+
}
5+
}

0 commit comments

Comments
 (0)