-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #4192: Properly set InnerClasses end EnclosingMethod in class files #11589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
smarter
merged 2 commits into
scala:master
from
dotty-staging:fix-inner-class-table-generation
Mar 10, 2021
Merged
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
derive-generic.scala | ||
eff-dependent.scala | ||
enum-java | ||
i4192 | ||
i5257.scala | ||
i7212 | ||
i7868.scala | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package checks | ||
|
||
import reflect.ClassTag | ||
|
||
/* This test checks whether InnerClasses and EnclosingMethod sections in generated class files are correct | ||
* for different possibilities of nesting of classes in other classes, objects and methods (the attributes are accessed via java reflection). | ||
* Names of nested definitions are derived from the name of their enclosing definition by appending a letter following the scheme below: | ||
* A - a class without a companion object | ||
* B - an object without a companion class | ||
* C - a class with its companion object | ||
* D - a method | ||
* Additionally a number may be added to avoid clashes between definitions from classes and their companion objects | ||
* (1 - defined in the companion class; 2 - defined in the companion object), | ||
* e.g. ACD2 - a method inside the companion object of a class inside a top level class | ||
*/ | ||
trait Checks: | ||
val expectedTopLevelChecksCount: Int | ||
val expectedMemberChecksCount: Int | ||
val expectedLocalChecksCount: Int | ||
|
||
var topLevelChecksCount = 0 | ||
var memberChecksCount = 0 | ||
var localChecksCount = 0 | ||
|
||
def verifyChecksCounts() = | ||
assert(topLevelChecksCount == expectedTopLevelChecksCount, | ||
s"top level checks: expected $expectedTopLevelChecksCount but was $topLevelChecksCount") | ||
assert(memberChecksCount == expectedMemberChecksCount, | ||
s"member checks: expected $expectedMemberChecksCount but was $memberChecksCount") | ||
assert(localChecksCount == expectedLocalChecksCount, | ||
s"local checks: expected $expectedLocalChecksCount but was $localChecksCount") | ||
|
||
/** Check JVM class properties of a top level class */ | ||
def checkTopLevel[ThisClass](using thisTag: ClassTag[ThisClass]) = | ||
val cls = thisTag.runtimeClass | ||
assert(cls.getEnclosingClass == null, s"Top level class $cls should have no enclosing class") | ||
assert(cls.getDeclaringClass == null, s"Top level class $cls should have no declaring class") | ||
assert(cls.getEnclosingMethod == null, s"Top level class $cls should have no enclosing method") | ||
topLevelChecksCount += 1 | ||
|
||
/** Check JVM class properties of a member class (defined directly inside another class) */ | ||
def checkMember[ThisClass, EnclosingClass](using thisTag: ClassTag[ThisClass], enclosingTag: ClassTag[EnclosingClass]) = | ||
val cls = thisTag.runtimeClass | ||
def className = cls.simpleName | ||
def enclosingClassName = cls.getEnclosingClass.simpleName | ||
def declaringClassName = cls.getDeclaringClass.simpleName | ||
val expectedEnclosingClassName = enclosingTag.runtimeClass.simpleName match | ||
case "B$" => "B" // classes defined directly in top level objects should be moved to their companion/mirror classes | ||
case "C$" => "C" | ||
case name => name | ||
assert(cls.getEnclosingClass != null, | ||
s"Member class $className should have an enclosing class") | ||
assert(enclosingClassName == expectedEnclosingClassName, | ||
s"The enclosing class of class $className should be $expectedEnclosingClassName but was $enclosingClassName") | ||
assert(cls.getDeclaringClass == cls.getEnclosingClass, | ||
s"The declaring class of class $className should be the same as its enclosing class but was $declaringClassName") | ||
assert(cls.getEnclosingMethod == null, | ||
s"Member class $className should have no enclosing method") | ||
memberChecksCount += 1 | ||
|
||
/** Check JVM class properties of a local class (defined directly inside a method) */ | ||
def checkLocal[ThisClass, EnclosingClass](using thisTag: ClassTag[ThisClass], enclosingTag: ClassTag[EnclosingClass]) = | ||
val cls = thisTag.runtimeClass | ||
def className = cls.simpleName | ||
def enclosingClassName = cls.getEnclosingClass.simpleName | ||
def meth = cls.getEnclosingMethod | ||
val expectedEnclosingClassName = enclosingTag.runtimeClass.simpleName | ||
// extracting method name basing on the described naming convention | ||
// $1 gets added during lambdaLift in case of a method defined inside another method | ||
val expectedEnclosingMethodName = | ||
val prefix = className.init | ||
val suffix = if prefix.filter(_.isLetter).endsWith("DD") then "$1" else "" | ||
prefix ++ suffix | ||
assert(cls.getEnclosingClass != null, | ||
s"Local class $className should have an enclosing class") | ||
assert(enclosingClassName == expectedEnclosingClassName, | ||
s"The enclosing class of class $className should be $expectedEnclosingClassName but was $enclosingClassName") | ||
assert(cls.getDeclaringClass == null, | ||
s"Local class $className should have no declaring class") | ||
assert(meth != null, | ||
s"Local class $className should have an enclosing method") | ||
assert(meth.getName == expectedEnclosingMethodName, | ||
s"The enclosing method of class $className should be $expectedEnclosingMethodName but was ${meth.getName}") | ||
localChecksCount += 1 | ||
|
||
extension (cls: Class[?]) | ||
// java 8 implementation of cls.getSimpleName() is buggy - https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8057919 | ||
def simpleName = cls.getName | ||
.stripSuffix("$1").stripSuffix("$2") | ||
.split("\\.").last | ||
.split("\\$").last |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
object Test { | ||
def main(args: Array[String]): Unit = { | ||
foo.bar.Checks.run() | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure what are the criteria for skipping pickling tests - how can I know if this is OK or the failing pickling test is a sign that there was a bug somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's always some sort of bug, but sometimes it's a bug in the way we run the test itself (when there's a "pickling difference" it means there's a difference in the pretty-printed trees, but sometimes that just means we need to adjust how we pretty-print these trees), it's a good idea to add a comment mentioning what kind of error lead to blacklisting the test in any case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've found the reason of the pickling tests failure #11648 and applied a workaround not to disable the test