[
https://issues.apache.org/jira/browse/GROOVY-12552?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
James Daugherty updated GROOVY-12552:
-------------------------------------
Description:
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}
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.
was:
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.
> 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
> Priority: Major
>
> 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}
> 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)