James Daugherty created GROOVY-12557:
----------------------------------------

             Summary: 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


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