jdaugherty commented on PR #3086:
URL: https://github.com/apache/groovy/pull/3086#issuecomment-6747627782
@paulk-asert I'd like your thoughts on something I found while working out
what to document for Jochen's point above. This PR doesn't cause it, since it
only adds guards and doesn't touch method selection.
When `Y extends X` answers X's metaclass from `getMetaClass()`, the members
only Y has were reachable on 4.0, but not before or since:
| Case | 3.0.25 | 4.0.26 to 4.0.33 | 5.0.7, 5.1.2, 5.1.3, 6.0.0, this PR |
|---|---|---|---|
| `y.onlyY()` from outside | `MissingMethodException` | works |
`MissingMethodException` |
| `onlyY()` from Y's own code | `MissingMethodException` | works |
`MissingMethodException` |
| `super.method()` from Y | `StackOverflowError` | works |
`MissingMethodException` |
| Method from a trait only on Y | `MissingMethodException` | works |
`MissingMethodException` |
| Constructing a class with a stateful trait only on it |
`MissingMethodException` | works | `MissingMethodException` |
| Property only on Y | `MissingPropertyException` |
`MissingPropertyException` | `MissingPropertyException` |
These are invokedynamic results on Java 21. Classic call sites on 4.0.33,
5.1.3 and 6.0.0 give the same results, except that an implicit-`this` call in
Y's own code still works on 5.1.3 and 6.0.0, because it compiles to a direct
`invokevirtual` that skips the metaclass.
The 4.0 behaviour comes from a catch in `MetaClassImpl.invokeMethod`: on
`MissingMethodException` it looked the method up on the receiver's real class
through `GroovyObjectHelper.lookup`. That was added for JDK 16 support
(d4f1636dec, GROOVY-10145) and removed along with `$getLookup` in d465bec90e
(GROOVY-10931), first released in 5.0.0-alpha-1. The GROOVY-10931 discussion is
about illegal access and `$getLookup`, and I didn't see this receiver shape
come up, so it may have been a side effect rather than a decision. The fallback
only covered methods, which is why the property row never worked.
Generated proxies have this shape. I haven't tested a real framework, but
calling a GORM proxy's `getHibernateLazyInitializer()` from dynamic Groovy
looks like the same case.
Two questions:
1. Should the 4.0 behaviour come back, or was it an accident that 5.0
rightly ended? Either way, I'd handle any fix in its own issue, as it's in a
different code path from this PR and also affects 5.x.
2. For the documentation Jochen asked for, should we say these members
aren't found, or leave them unspecified until that's decided?
<details>
<summary>Reproducer</summary>
```groovy
class X {
String callsOwn() { 'X.callsOwn' }
String callsSuper() { 'X.callsSuper' }
}
trait Stateless { String fromTrait() { 'Stateless.fromTrait' } }
trait Stateful { String state = 'Stateful.state' }
class Y extends X implements Stateless {
@Override MetaClass getMetaClass() {
GroovySystem.metaClassRegistry.getMetaClass(X) }
String ownProp = 'Y.ownProp'
String onlyY() { 'Y.onlyY' }
@Override String callsOwn() { onlyY() }
@Override String callsSuper() { super.callsSuper() }
}
class Z extends X implements Stateful {
@Override MetaClass getMetaClass() {
GroovySystem.metaClassRegistry.getMetaClass(X) }
}
def attempt(String label, Closure c) {
try { println "$label: ${c()}" }
catch (MissingMethodException | MissingPropertyException |
StackOverflowError e) { println "$label: ${e.class.simpleName}" }
}
def y = new Y()
attempt('y.onlyY() from outside') { y.onlyY() }
attempt('onlyY() from Y') { y.callsOwn() }
attempt('super.callsSuper() from Y') { y.callsSuper() }
attempt('method from a trait only on Y') { y.fromTrait() }
attempt('new Z() with a stateful trait') { new Z(); 'constructed' }
attempt('property only on Y') { y.ownProp }
```
</details>
--
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]