-
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
Changes from 6 commits
6606910
5ac7540
da747ca
5d7efd1
de5cbde
4d8f91e
e26d093
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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) { | ||
| var currentClass = cls | ||
| assert(currentClass != null, "The outer class can't be null.") | ||
|
|
||
| while (currentClass != null) { | ||
| accessedFields(currentClass) = Set.empty[String] | ||
| currentClass = currentClass.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) | ||
|
|
||
| var currentClass = outerClass | ||
| do { | ||
|
Contributor
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. please update this do while loop too
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. Ok. It's late, I will update this and below tomorrow. Thanks. |
||
| setAccessedFields(currentClass, clone, obj, accessedFields) | ||
|
Contributor
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. Assume we have class
Contributor
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. Seems this is also a issue for the
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. Seems that is true. For a closure that only accessed
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. As this is not a regression, IIUC, will it block this change?
Contributor
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. yea let's leave it |
||
| currentClass = currentClass.getSuperclass() | ||
| } while (currentClass != null) | ||
|
|
||
| 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)) { | ||
|
|
@@ -395,8 +435,13 @@ private[util] class FieldAccessFinder( | |
| if (!visitedMethods.contains(m)) { | ||
| // Keep track of visited methods to avoid potential infinite cycles | ||
| visitedMethods += m | ||
| ClosureCleaner.getClassReader(cl).accept( | ||
| new FieldAccessFinder(fields, findTransitively, Some(m), visitedMethods), 0) | ||
|
|
||
| var currentClass = cl | ||
| do { | ||
|
Contributor
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. here too |
||
| ClosureCleaner.getClassReader(currentClass).accept( | ||
| new FieldAccessFinder(fields, findTransitively, Some(m), visitedMethods), 0) | ||
| currentClass = currentClass.getSuperclass() | ||
| } while (currentClass != null) | ||
| } | ||
| } | ||
| } | ||
|
|
||
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.