James Daugherty created GROOVY-12552:
----------------------------------------

             Summary: STC: property access calls an interface's accessor 
instead of the property's own when the property's class is in the same 
compilation unit
                 Key: GROOVY-12552
                 URL: https://issues.apache.org/jira/browse/GROOVY-12552
             Project: Groovy
          Issue Type: Bug
          Components: Static Type Checker
    Affects Versions: 5.1.3
            Reporter: James Daugherty


When an interface declares the setter and getter of a property that a class 
implements as a Groovy property, static compilation calls the interface's 
methods with {{invokeinterface}} when the class is compiled together with the 
caller, and the class's own accessors with {{invokevirtual}} when the class is 
already compiled. The behaviour at run time is the same, but the same source 
compiles to a different class file depending on what else is compiled. Groovy 
4.0.33 calls the class's accessors in both cases.

{code:groovy}
interface Named {
    void setName(String name)
    String getName()
}

abstract class Base implements Named {
    String name
}

class Impl extends Base {}

@groovy.transform.CompileStatic
class Svc {
    String rename() {
        Base base = new Impl()
        base.name = 'x'     // invokeinterface Named.setName when Base is in 
the unit, invokevirtual Impl.setName when it is compiled
        base.setName('y')
        base.getName()
    }
}
{code}

In a real application the interface is often a library one, such as Spring's 
{{BeanNameAware}} implemented by a {{beanName}} property.

Reproduced with 5.1.3 and master. A reproducer with a groovyc script and an 
incremental Gradle build: 
https://github.com/jdaugherty/groovy-property-accessor-interface-method

*Cause*

A property of a class in the compilation unit does not have its accessors yet 
when static type checking runs. {{StaticTypeCheckingVisitor.findMethod}} adds a 
synthetic accessor for the property when every method found is abstract 
(GROOVY-10922), but after the interface's method. 
{{StaticTypeCheckingSupport.removeCovariantsAndInterfaceEquivalents}} only 
drops an interface method that comes after the method of a class implementing 
it, so the interface method stays and is chosen. A compiled class lists its 
real accessor first, and the interface method is dropped.

*Proposed fix*

Add the synthetic accessor first, where a compiled class has its accessor, so 
that it is preferred over the interface method the same way.

Found while making incremental builds of a Grails application produce the same 
classes as a full build; see also GROOVY-12550 and GROOVY-12544.



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

Reply via email to