[ 
https://issues.apache.org/jira/browse/GROOVY-12489?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18134129#comment-18134129
 ] 

ASF GitHub Bot commented on GROOVY-12489:
-----------------------------------------

paulk-asert opened a new pull request, #3078:
URL: https://github.com/apache/groovy/pull/3078

   …setProperty or getProperty
   
   A dynamic property access that carries a sender must know whether the 
receiver overrides the setProperty or getProperty that GroovyObject provides: 
an override receives the access, while for the GroovyObject default the runtime 
calls the metaclass itself with the sender, which is how a superclass reaches 
its own private field on a subclass instance. ScriptBytecodeAdapter, the indy 
property selectors and the classic call sites answered this with 
Class.getMethod on every access, and handled a failed lookup inconsistently: 
the adapter let it escape, SetPropertySelector treated it as an override, 
PropertySelector went around the class's own getProperty to the metaclass, and 
the classic sites fell back only for NoSuchMethodException.
   
   Reflection over a class can fail although the class works, notably in a 
GraalVM native image without reflection metadata for the class, so writes and 
adapter reads to such objects failed and indy reads were answered by the 
metaclass, sometimes with a wrong value and no error.
   
   The new internal GroovyObjectOverrides caches each answer per class in a 
ClassValue: overridesSetProperty for all writes, overridesGetProperty for 
ScriptBytecodeAdapter.getProperty, and hasCustomGetProperty (which also sets 
aside synthetic and @Internal implementations) for the indy and classic read 
sites, keeping the existing split between the two read criteria. When 
reflection cannot read the class, it counts as overriding, so the access 
reaches the object's own method instead of failing or bypassing a real override.
   
   The checks now set aside only GroovyObject's own default. Since Groovy 5 
(GROOVY-8283) any interface default was taken for it, so a setProperty or 
getProperty from a default method of another interface was skipped by obj.x = v 
writes and computed-name reads while other accesses reached it. It now receives 
every access, as a declared method does and as in Groovy 4.




> Dynamic property access fails or bypasses getProperty/setProperty when the 
> receiver class has no reflection metadata (GraalVM native image)
> -------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12489
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12489
>             Project: Groovy
>          Issue Type: Bug
>          Components: groovy-runtime
>    Affects Versions: 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> A dynamic property access that carries a sender has to know whether the 
> receiver overrides the {{setProperty}} or {{getProperty}} that 
> {{GroovyObject}} provides. Groovy answers this with {{Class.getMethod}} on 
> every access:
> * {{ScriptBytecodeAdapter.setProperty}}, {{setGroovyObjectProperty}} and 
> {{getProperty}} let any exception from the lookup escape.
> * The invokedynamic {{SetPropertySelector}} treats a failed lookup as an 
> override.
> * The invokedynamic {{PropertySelector}} goes to the meta-class instead, 
> reading around the class's own {{getProperty}}.
> Reflection over a class can fail while the class itself works. In a GraalVM 
> native image without reflection metadata for the class, {{getMethod}} throws 
> {{NoSuchMethodException}}. As a result, every write that carries a sender to 
> such an object fails, and so does every read through the adapter. Reads 
> through invokedynamic sites are answered by the meta-class instead, with a 
> {{MissingPropertyException}}, or, when the meta-class knows the property 
> through a registered superclass, with the wrong value and no error. 
> Applications such as Grails register every Groovy class for reflection only 
> so these checks can be answered. On the JVM, a public method naming a type 
> absent at run time makes the lookups throw {{NoClassDefFoundError}}. And 
> where reflection works, every access pays for a method search and a 
> {{Method}} copy.
> Proposed fix: cache each answer per class in a {{ClassValue}}, keeping the 
> existing criteria, and when reflection cannot answer, treat the class as 
> overriding so the access goes to the object's own method.
> The check has a second problem since 5.0 (GROOVY-8283): it takes any 
> interface default for {{GroovyObject}}'s own. A class that gets 
> {{setProperty}} or {{getProperty}} from a default method of another 
> interface, such as one extending {{GroovyObject}}, has that method skipped by 
> every {{obj.x = v}} write and every untyped {{obj."$name"}} read, which go to 
> the meta-class, while {{obj.setProperty(...)}} and {{obj.x}} reads reach it. 
> Groovy 4 reached it on every path, as a declared method or a trait method 
> still does. The PR also sets aside only {{GroovyObject}}'s own default, so 
> such a method receives every access again.
> PR: https://github.com/apache/groovy/pull/3023



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to