codeconsole opened a new pull request, #3023:
URL: https://github.com/apache/groovy/pull/3023

   When a dynamic property write carries a sender, Groovy needs to know whether 
the receiver overrides the `setProperty` that `GroovyObject` provides.
   
   - If the receiver has an override, the override receives the write.
   - If it only has the interface default, the runtime calls the metaclass 
itself, so the write can be resolved for the sender. That is how a superclass 
writes its own private field on a subclass instance.
   
   `ScriptBytecodeAdapter.setProperty` and `setGroovyObjectProperty` answered 
that question by calling `Class.getMethod("setProperty", String, Object)` on 
every write, and let any exception escape. The indy `SetPropertySelector` makes 
the same check, documented as mirroring the adapter, but treats a failed lookup 
as an override.
   
   Reflection over a class can fail while the class itself works:
   
   - In a GraalVM native image, `getMethod` on a class with no reflection 
metadata throws `NoSuchMethodException`. Every dynamic write with a sender then 
failed, indy writes included, even to an object whose own `setProperty` needs 
no reflection. Applications such as Grails had to register every Groovy class 
for reflection to work around it.
   - On the JVM, a public method naming a type absent at run time makes 
`getMethod` throw `NoClassDefFoundError`.
   - Where reflection works, every write still paid for a method search and a 
`Method` copy.
   
   The three sites now share an internal helper, 
`org.apache.groovy.internal.metaclass.GroovyObjectOverrides`, which caches the 
answer per class in a `ClassValue`.
   
   - Where reflection works, the answer is the same as before.
   - Where it fails, the helper answers "overrides", as the selector already 
did. The write goes to the object's own `setProperty`, which always reaches the 
implementation the object really has. If that implementation is the interface 
default, the write is resolved for the receiver's class instead of the sender. 
`setGroovyObjectProperty`'s existing retry with the sender still covers a 
superclass's private property.
   - The opposite answer would bypass a real override.
   
   No public API changes.
   
   Tests:
   
   - `GroovyObjectOverridesTest` covers the decision: the interface default, a 
declared override, an inherited override, a re-declared override, and a class 
whose public methods reflection cannot read.
   - `SetPropertyDispatchTest` covers behavior under indy and classic 
compilation: a custom `setProperty` intercepts static-name, dynamic-name and 
`this` writes, and a class with the default has the write resolved for its 
sender.
   - `UnreadableClassSetPropertyTest` (Java) shows writes reaching a class's 
own `setProperty` when reflection over the class fails. It fails on master with 
`NoClassDefFoundError` from `ScriptBytecodeAdapter.setProperty`.
   
   The runtime, indy, MOP, property and closure suites pass (5,010 tests), as 
do the Checkstyle and CodeNarc gates.
   
   A native-image probe built with GraalVM 25.2.4, using only the metadata 
Groovy ships, writes to an unregistered class that overrides `setProperty`:
   
   | Write path | master | this change |
   |---|---|---|
   | `ScriptBytecodeAdapter.setProperty` | `NoSuchMethodException` | reaches 
the override |
   | `setGroovyObjectProperty` | `NoSuchMethodException` | reaches the override 
|
   | dynamic-name write | `NoSuchMethodException` | reaches the override |
   | indy `obj.x = v` | `NoSuchMethodException` | reaches the override |
   
   In a rough JVM measurement, adapter writes went from about 85 to 21 ns for a 
class with the default, and from 27 to 1.4 ns for a class with an override.
   
   The read side has the same shape and is not changed here: 
`ScriptBytecodeAdapter.getProperty` throws when the lookup fails, and 
`PropertySelector` falls through to the metaclass.
   
   There is no JIRA issue for this yet.
   


-- 
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]

Reply via email to