[
https://issues.apache.org/jira/browse/GROOVY-12580?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18135261#comment-18135261
]
ASF GitHub Bot commented on GROOVY-12580:
-----------------------------------------
codeconsole commented on PR #3096:
URL: https://github.com/apache/groovy/pull/3096#issuecomment-6526833129
@daniellansun thanks.
1. Fixed in 79028a0926. `java.lang.ClassValue`, the default store, has no
get-if-present, so `ClassInfo` keeps the lookup itself: the global set now also
indexes its `ClassInfo`s by class, keys and values held weakly, and that index
replaces the map of detached instances. `ClassInfo.remove` looks the instance
up there and never creates one; a class without a `ClassInfo` is left without
one. `computeValue` gets a still reachable (detached) instance back from the
same index, as before.
`Groovy12142.testRemovingAClassWithoutClassInfoCreatesNone` checks it through
`ClassInfo.getAllClassInfo()` and fails on the previous `ClassInfo.remove`. The
set's queue stays as it is because Gradle's
`ClassInfoCleaningGroovySystemLoader` reads it reflectively.
2. The put is gone. The instance stays in the index from creation, so
`remove` only clears the MetaClasses under the `ClassInfo` lock and then drops
the association. A `get` before the drop sees the association; one after it
gets the same instance back from the index.
3. #3096 is stacked on #3095 and already contains it (merged with master).
#3095 should merge first; #3096 then shows only its own GROOVY-12580 commits.
The `lts (17, windows-latest, 1)` timeout is the same 60m Gradle step
timeout as on #3095. `bugs.Groovy12142` and `bugs.Groovy12363` pass locally on
JDK 17.0.20.
> ClassInfo.remove takes a class's default Groovy methods with it
> ---------------------------------------------------------------
>
> Key: GROOVY-12580
> URL: https://issues.apache.org/jira/browse/GROOVY-12580
> Project: Groovy
> Issue Type: Bug
> Components: groovy-runtime
> Affects Versions: 4.0.33, 5.0.8, 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> {{ClassInfo.remove(Class)}} drops the class's association, and the next use
> of the class creates a new {{ClassInfo}}. The default Groovy methods and
> extension methods of a class are written into its {{ClassInfo}} once, by the
> registry at startup, so the class loses them for the rest of the runtime's
> life:
> {code:groovy}
> import org.codehaus.groovy.reflection.ClassInfo
> ClassInfo.remove(StringBuilder)
> def sb = new StringBuilder('ab')
> sb[0..0] = 'z' // MissingMethodException: No signature of method: putAt for
> class: java.lang.StringBuilder
> {code}
> {{sb << 'x'}} falls back to {{CharSequence.leftShift}}, which returns a new
> builder and leaves {{sb}} empty, or to {{Appendable.leftShift}}, depending on
> hash order. After the sweep the integration guide documents for application
> shutdown, a runtime still in use loses every default Groovy method:
> {code:groovy}
> import org.codehaus.groovy.reflection.ClassInfo
> for (ClassInfo ci : ClassInfo.getAllClassInfo()) { Class<?> c =
> ci.getTheClass(); if (c != null) ClassInfo.remove(c) }
> [1, 2, 3].collect { it * 2 } // MissingMethodException: No signature of
> method: multiply for class: java.lang.Integer
> {code}
> {{bugs.Groovy12142.testClassInfoRemoveSweepIsSafeToRunRepeatedly}} removes
> the {{ClassInfo}} of {{StringBuilder}}, so {{bugs.Groovy12363}}, whose
> anonymous class does {{sink << who}}, fails when it runs later in the same
> test JVM.
> The guide's sweep, written as dynamic Groovy and run by the copy it cleans
> up, also keeps that copy's class loader reachable, since a dynamic call made
> after the first removal associates a class with that runtime again.
> PR: https://github.com/apache/groovy/pull/3096
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)