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

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

blackdrag commented on PR #3086:
URL: https://github.com/apache/groovy/pull/3086#issuecomment-6532607554

   I do not have a technical concern for this issue, but I do have a bit of a 
problem with the nature of the issue itself. First of all... why did 
[GROOVY-12046](https://issues.apache.org/jira/browse/GROOVY-12046) not solve 
the problem? The description sounds very similar. But I have a more general 
problem... a meta class is supposed to answer for the class it stands for. An 
example of a meta class that stands in for another class is 
DelegatingMetaClass. To use a metaclass that is intended to represent a single 
class to represent another class qualifies for me as hack. Which means it may 
work, but it is not intended too and will break. At least in my thinking I was 
never considering a MetaClassImpl to do such a thing. And that is also not 
tested here. But is the code special for ExpandoMetaClass? If that would be the 
case it would be used in the implementation, which it is not. 
   Of course I see the principle. You have an mock instance that is a subclass 
of the class you really want to test. You want to give it a meta class, that 
acts like the meta class of the class you want to test, but it will have to 
live on the mock instance.  And maybe you even want to put methods on that mock 
instance, that are not supposed to "pollute" the original. At the same time you 
cannot do that on the original for the same reason. I understand that part 
perfectly. I just think that neither MetaClassImpl, nor ExpandoMetaClass are 
designed for this. Also... what if the instance you want to mock has for 
example a DelegatingMetaClass? Or even a custom meta class?




> Indy: call site keeps a method replaced on a metaclass the receiver shares 
> with another class
> ---------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12558
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12558
>             Project: Groovy
>          Issue Type: Bug
>    Affects Versions: 6.0.0
>            Reporter: James Daugherty
>            Priority: Major
>
> On Groovy 6 with invokedynamic, a call site whose receiver uses the metaclass 
> of another class keeps invoking a method after that metaclass replaces it. 
> Groovy 5, and Groovy 6 with classic call sites, invoke the replacement. This 
> is a regression from GROOVY-12191 (6.0.0-beta-1).
> Such receivers are common in tests: Spock's {{Mock()}} generates a subclass 
> of the mocked type whose {{getMetaClass()}} answers the mocked type's 
> metaclass, so a feature that replaces a method on the mock's metaclass 
> changes the {{ExpandoMetaClass}} of the mocked type, and many proxy libraries 
> behave the same way. A specification that replaces such a method in one 
> feature and runs the same code under test in a later feature gets the earlier 
> feature's closure: a {{0 * collaborator.update(*_)}} interaction fails with 
> "Too many invocations", or an expected exception is never thrown.
> *Reproducer:* 
> https://github.com/jdaugherty/groovy-indy-shared-metaclass-switchpoint-reproducer
>  ({{./run.sh 5.1.3 6.0.0}}). {{GreeterProxy extends Greeter}} and is given 
> the {{ExpandoMetaClass}} of {{Greeter}}, as a mock or proxy subclass is. One 
> call site invokes {{greet()}} and one {{+=}} site invokes {{plus()}}; each 
> method is replaced on the metaclass between two calls of the same site:
> {noformat}
> == Groovy 5.1.3
>    indy=true: method call after replacing the method:   second (OK)
>    indy=true: compound assignment after replacing plus: second (OK)
> == Groovy 6.0.0
>    indy=true: method call after replacing the method:   first (STALE: the 
> call site still invokes the replaced method)
>    indy=true: compound assignment after replacing plus: first (STALE: the 
> call site still invokes the replaced method)
>    indy=false: method call after replacing the method:   second (OK)
>    indy=false: compound assignment after replacing plus: second (OK)
> {noformat}
> *Cause:* GROOVY-12191 replaced the global indy SwitchPoint with per-class 
> domains. A call site is guarded by the SwitchPoint of the receiver's class 
> ({{Selector.MethodSelector.captureTokenAndGetMetaClass()}}, 
> {{IndyInvalidation.classSwitchPointFor(args[0])}}; likewise in 
> {{IndyCompoundAssign.resolve}}). A change to a metaclass retires the domain 
> of the class the metaclass belongs to, {{MetaClass.getTheClass()}} 
> ({{IndyInvalidation.switchPointForMetaClass}} / {{invalidateMetaClass}}). 
> When the receiver's class is not the metaclass's class, as for 
> {{GreeterProxy}} and the {{Greeter}} metaclass it uses, the change retires 
> {{Greeter}}'s domain while the site waits on {{GreeterProxy}}'s. The receiver 
> metaclass guard ({{SAME_MC}}) does not catch it either, because replacing an 
> {{ExpandoMetaClass}} method keeps the same metaclass instance.
> *Proposed fix:* when the selected metaclass belongs to a class other than the 
> receiver's, also capture that class's SwitchPoint before selection and guard 
> the site with both. That applies to {{Selector}} and to 
> {{IndyCompoundAssign}}, which captures its token the same way. A patch with 
> tests follows as a pull request against master, with a backport for the 6.0.x 
> line.



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

Reply via email to