paulk-asert opened a new pull request, #3205:
URL: https://github.com/apache/groovy/pull/3205
This bundles eight PRs by @codeconsole that all touch closure wrappers,
memoize serialization and object input stream class resolution, so the
combined behaviour can be reviewed and merged as one consistent set. The
individual PRs interact (several change the same lines of `Memoize.java`
and `Closure.java`), which made them hard to judge one at a time.
Cherry-picked in this order, following the merge notes in each PR:
| JIRA | PR | Change |
|---|---|---|
| GROOVY-12665 | #3186 | `dehydrate()`/`rehydrate()` through curried,
writable, composed and trampolined closures |
| GROOVY-12673 | #3194 | wrappers report and take the delegate, resolve
strategy and `thisObject` of the closure they wrap |
| GROOVY-12669 | #3190 | `dehydrate()`/`rehydrate()` through memoized
closures, with a fresh cache |
| GROOVY-12674 | #3195 | `memoizeAtMost`, `memoizeAtLeast` and
`memoizeBetween` closures serialize |
| GROOVY-12677 | #3198 | a memoized closure that has cached `null`
serializes |
| GROOVY-12679 | #3200 | async wrappers forward delegate, resolve strategy
and parameter types |
| GROOVY-12676 | #3197 | `ObjectInputStream` with a class loader resolves
primitive classes |
| GROOVY-12681 | #3202 | `ObjectInputStream` with a class loader resolves
proxy interfaces through that loader |
Four cherry-picks needed manual resolution (#3190 against #3194 in
`Memoize.java`; #3195 against #3190 in `Closure.java`, `Memoize.java` and
`AbstractMemoizeTestCase`; the test move in #3198; the cycle test in #3200),
each resolved the way the PR's own merge note describes. Scott's commits are
kept as his, with his authorship.
Two commits follow on top:
- **GROOVY-12669: serialize the memoize cache factories as named classes,
not lambdas.**
#3190 passed `(Supplier & Serializable)` lambdas as cache factories, so a
memoized closure serialized with a `SerializedLambda` naming a
javac-generated method of `Closure`. Such a stream cannot be read by 6.0.0
or 5.1.3, and the generated name changes with the enclosing method's
signature, lambda order and compiler. The factories are now four private
static nested classes of `Closure` with fixed `serialVersionUID`s. A plain
`memoize()` stream written this way reads on 6.0.0 and 5.1.3 (verified
against the distributions), and streams written by those versions read
here, as before.
- **GROOVY-12673: document how wrapped closures share delegation settings.**
Javadoc only. A memoized or trampolined closure calls the very closure it
was given, as it always has, and now also forwards a delegate or resolve
strategy set on the wrapper to it; `clone()` gives an independent copy.
Keeping the sharing (rather than cloning in the constructor) preserves the
6.0.0 behaviour where a binding made on the original after wrapping is
seen by the wrapper.
## Is anything deliberate being undone?
No. All eight main-source files are byte-identical between the 6.0.0 tag and
master, and a probe of about sixty behaviours this set touches gives the same
results on 4.0.33, 5.0.8, 5.1.3 and 6.0.0. The gaps being closed are
long-standing (the trampoline constructor, for example, has passed the
wrapped closure's delegate as `thisObject` since 2010), not 5→6 regressions.
The set only extends the deliberate 6.0.0 work: the deserialization cycle
check (GROOVY-12109, GROOVY-12276, GROOVY-12339), which walks fields rather
than getters and so is unaffected by the new forwarding, and
resolve-without-initialization in `newObjectInputStream` (GROOVY-12268).
## Behaviour changes, for the record
- Wrappers report the wrapped closure's `thisObject` (was `null` or the
owner).
- Memoize, trampoline and async wrappers forward `delegate` and
`resolveStrategy` to the wrapped closure. Previously the setting landed in
a
field nothing read, so a wrapped closure could not be handed to a builder
or to `with()`/`tap()`.
- Async wrappers, and trampolined writable or composed closures, report the
wrapped closure's parameter types instead of a single `Object[]`.
- `rehydrate()`/`dehydrate()` on a wrapper return a wrapper over the copy
instead of a copy of the wrapper still calling the old closure.
- The sized and soft memoize variants, and cached `null` results, serialize;
softly referenced values are dropped from the stream.
- The anonymous `AsyncClosureUtils$1`/`$2` wrappers become named classes, so
a 6.0.0 stream of a `wrapAsync` closure does not read here (async/await is
incubating).
- `Memoize` gains two public overloads taking a `Supplier` of the cache;
`japicmp` sees only additions.
A `COMPATIBILITY.md` entry (Groovy 6.0.1 — closure wrappers report and take
the delegation settings of the closure they wrap) describes all of the
above, since the intent is to backport the set to 6.0.x: every change either
turns an exception into working behaviour or is confined to introspecting a
wrapper closure.
## Verification
- Targeted suites (`groovy.lang.*Closure*`, `groovy.*Closure*`,
`org.codehaus.groovy.runtime.memoize.*`, trampoline, async, serialization
cycle, `BinaryStreamsTest`, `NioExtensionsTest`, `@Memoized` transform):
2837 tests, 0 failures, 7 skipped on the merged set; 207/0 after the
factory-class change.
- `./gradlew lintGate` passes.
- Cross-version serialization checked by writing streams with this build and
reading them with the 6.0.0 and 5.1.3 distributions, and the reverse.
## Not included
GROOVY-12661 (#3182) and GROOVY-12666 (#3187), which rehydrate packed
closures, are left for a separate review: they are the closure-packing
family and conflict with each other.
Closes #3186, closes #3194, closes #3190, closes #3195, closes #3198,
closes #3200, closes #3197, closes #3202.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]