-
Notifications
You must be signed in to change notification settings - Fork 29.3k
[SPARK-22328][Core] ClosureCleaner should not miss referenced superclass fields #19556
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
Closed
Closed
Changes from 3 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
6606910
ClosureCleaner should fill referenced superclass fields.
viirya 5ac7540
Add another test.
viirya da747ca
Refactoring tests.
viirya 5d7efd1
Address comment.
viirya de5cbde
Add a test.
viirya 4d8f91e
Address comment.
viirya e26d093
Address comments.
viirya 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,6 +91,52 @@ private[spark] object ClosureCleaner extends Logging { | |
| (seen - obj.getClass).toList | ||
| } | ||
|
|
||
| /** Initializes the accessed fields for outer classes and their super classes. */ | ||
| private def initAccessedFields( | ||
| accessedFields: Map[Class[_], Set[String]], | ||
| outerClasses: Seq[Class[_]]): Unit = { | ||
| for (cls <- outerClasses) { | ||
| accessedFields(cls) = Set.empty[String] | ||
|
|
||
| var superClass = cls.getSuperclass() | ||
| while (superClass != null) { | ||
| accessedFields(superClass) = Set.empty[String] | ||
| superClass = superClass.getSuperclass() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** Sets accessed fields for given class in clone object based on given object. */ | ||
| private def setAccessedFields( | ||
| outerClass: Class[_], | ||
| clone: AnyRef, | ||
| obj: AnyRef, | ||
| accessedFields: Map[Class[_], Set[String]]): Unit = { | ||
| for (fieldName <- accessedFields(outerClass)) { | ||
| val field = outerClass.getDeclaredField(fieldName) | ||
| field.setAccessible(true) | ||
| val value = field.get(obj) | ||
| field.set(clone, value) | ||
| } | ||
| } | ||
|
|
||
| /** Clones a given object and sets accessed fields in cloned object. */ | ||
| private def cloneAndSetFields( | ||
| parent: AnyRef, | ||
| obj: AnyRef, | ||
| outerClass: Class[_], | ||
| accessedFields: Map[Class[_], Set[String]]): AnyRef = { | ||
| val clone = instantiateClass(outerClass, parent) | ||
| setAccessedFields(outerClass, clone, obj, accessedFields) | ||
|
|
||
| var superClass = outerClass.getSuperclass() | ||
| while (superClass != null) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be something more like ... Just avoids repeating the key method call here. Same above and below.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. Looks good. |
||
| setAccessedFields(superClass, clone, obj, accessedFields) | ||
| superClass = superClass.getSuperclass() | ||
| } | ||
| clone | ||
| } | ||
|
|
||
| /** | ||
| * Clean the given closure in place. | ||
| * | ||
|
|
@@ -202,9 +248,8 @@ private[spark] object ClosureCleaner extends Logging { | |
| logDebug(s" + populating accessed fields because this is the starting closure") | ||
| // Initialize accessed fields with the outer classes first | ||
| // This step is needed to associate the fields to the correct classes later | ||
| for (cls <- outerClasses) { | ||
| accessedFields(cls) = Set.empty[String] | ||
| } | ||
| initAccessedFields(accessedFields, outerClasses) | ||
|
|
||
| // Populate accessed fields by visiting all fields and methods accessed by this and | ||
| // all of its inner closures. If transitive cleaning is enabled, this may recursively | ||
| // visits methods that belong to other classes in search of transitively referenced fields. | ||
|
|
@@ -250,13 +295,8 @@ private[spark] object ClosureCleaner extends Logging { | |
| // required fields from the original object. We need the parent here because the Java | ||
| // language specification requires the first constructor parameter of any closure to be | ||
| // its enclosing object. | ||
| val clone = instantiateClass(cls, parent) | ||
| for (fieldName <- accessedFields(cls)) { | ||
| val field = cls.getDeclaredField(fieldName) | ||
| field.setAccessible(true) | ||
| val value = field.get(obj) | ||
| field.set(clone, value) | ||
| } | ||
| val clone = cloneAndSetFields(parent, obj, cls, accessedFields) | ||
|
|
||
| // If transitive cleaning is enabled, we recursively clean any enclosing closure using | ||
| // the already populated accessed fields map of the starting closure | ||
| if (cleanTransitively && isClosure(clone.getClass)) { | ||
|
|
@@ -397,6 +437,13 @@ private[util] class FieldAccessFinder( | |
| visitedMethods += m | ||
| ClosureCleaner.getClassReader(cl).accept( | ||
| new FieldAccessFinder(fields, findTransitively, Some(m), visitedMethods), 0) | ||
|
|
||
| var superClass = cl.getSuperclass() | ||
| while (superClass != null) { | ||
| ClosureCleaner.getClassReader(superClass).accept( | ||
| new FieldAccessFinder(fields, findTransitively, Some(m), visitedMethods), 0) | ||
| superClass = superClass.getSuperclass() | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
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
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.
what if multiple outer classes have the same parent class?
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 think from the view of closure, even multiple outer classes have the same parent class, the access of the fields in the parent class shouldn't conflict.
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 added a related test. Please see if it can clarify your concern.