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

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

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

   A cast call site reused `MethodSelector.setGuards()`, which guards a 
`GroovyObject` receiver with its metaclass, so every cast of a `GroovyObject` 
called `getMetaClass()` on the value: once to build the guard and again on each 
invocation through it. A class-based proxy of a Groovy class, such as a Spring 
scoped proxy, forwards `getMetaClass()` to its target, so `Report report = 
proxy` failed outside the proxy's scope, where classic call sites call nothing 
on the value.
   
   Only a cast to boolean selects a method, `asBoolean`, through the metaclass; 
every other cast is decided by the class of the value, which the argument 
guards and the class switch point already cover. `CastSelector` now keeps the 
receiver metaclass guard for that case only.
   
   `org.codehaus.groovy.vmplugin.v8.IndyCastReceiverMetaClassTest` checks that 
a cast of a value that already has the target type makes no `getMetaClass()` 
call and succeeds when `getMetaClass()` throws, that a cast to boolean still 
selects `asBoolean` through each value's metaclass, and that a cast site still 
rejects a value of another class. It fails without the fix.
   
   - Jira: [GROOVY-12557](https://issues.apache.org/jira/browse/GROOVY-12557)
   - Reproducer: 
https://github.com/jdaugherty/groovy-indy-cast-getmetaclass-reproducer
   - Backport to `GROOVY_6_0_X`: 
https://github.com/jdaugherty/groovy/commit/bc97fa58f126beb18d67013110cc5488b198eb16,
 on the branch 
[`GROOVY-12557-6_0_X`](https://github.com/jdaugherty/groovy/tree/GROOVY-12557-6_0_X).
   - Backport to `GROOVY_5_1_X`: 
https://github.com/jdaugherty/groovy/commit/0300e782ece81007601006722560b59fa9ba670c,
 on the branch 
[`GROOVY-12557-5_1_X`](https://github.com/jdaugherty/groovy/tree/GROOVY-12557-5_1_X).
 The 5.1.x `Selector` predates the per-class switch points, so the guard change 
is applied by hand there.
   
   It reproduces on 4.0.33, 5.1.3, 6.0.0 and master with invokedynamic, and not 
with classic call sites. Could this be backported to 6.0.x and 5.x once it is 
merged?
   




> Indy: casting a GroovyObject calls getMetaClass() on the value being cast
> -------------------------------------------------------------------------
>
>                 Key: GROOVY-12557
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12557
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 4.0.33, 5.1.3, 6.0.0
>            Reporter: James Daugherty
>            Priority: Major
>
> With invokedynamic, a cast of a {{GroovyObject}} that already has the target 
> type, such as {{Widget widget = value}}, calls {{value.getMetaClass()}}: once 
> to build the call site's guard and again on each call through it. With 
> classic call sites the same cast calls nothing on the value.
> That is observable whenever {{getMetaClass()}} is not a plain field read. A 
> Spring class-based (CGLIB) proxy of a Groovy class forwards every public 
> method, {{getMetaClass()}} included, to its target. For a step-, job-, 
> request- or session-scoped bean the target exists only while the scope is 
> active, so merely assigning the proxy to a typed variable outside the scope 
> fails:
> {noformat}
> org.springframework.beans.factory.support.ScopeNotActiveException: Error 
> creating bean with name 'scopedTarget.widget': Scope 'step' is not active for 
> the current thread
>   at 
> org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
>   at 
> org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
>   at Widget$$SpringCGLIB$$0.getMetaClass
>   at org.codehaus.groovy.vmplugin.v8.Selector$MethodSelector.setGuards
>   at org.codehaus.groovy.vmplugin.v8.Selector$CastSelector.castAndSetGuards
>   at org.codehaus.groovy.vmplugin.v8.Selector$CastSelector.setCallSiteTarget
>   at org.codehaus.groovy.vmplugin.v8.IndyInterface.fallback
> {noformat}
> A Grails application moving from classic call sites to invokedynamic (the 
> Grails 9 default) meets this in code that handles scoped proxies dynamically.
> *Reproducer:* 
> https://github.com/jdaugherty/groovy-indy-cast-getmetaclass-reproducer 
> ({{./run.sh 4.0.33 5.1.3 6.0.0}}). With indy the cast {{Watched typed = 
> value}} makes 2 {{getMetaClass()}} calls on the value and fails when 
> {{getMetaClass()}} throws; with classic call sites it makes none. Reproduced 
> on 4.0.33, 5.1.3 and 6.0.0.
> *Cause:* {{Selector.CastSelector.castAndSetGuards()}} reuses 
> {{MethodSelector.setGuards(args[0])}}, which calls {{getMetaClass()}} on a 
> {{GroovyObject}} receiver to bind a {{SAME_MC}} guard. Only a cast to boolean 
> selects a method ({{asBoolean}}) through the metaclass; every other cast is 
> decided by the class of the value, which the argument guards and the class 
> SwitchPoint already cover.
> *Proposed fix:* keep the receiver metaclass guard for the {{asBoolean}} path 
> only, so a cast decided by the value's class never calls into the value, as 
> with classic call sites. A patch with tests follows as a pull request against 
> master, with backports for the 6.0.x and 5.1.x lines.



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

Reply via email to