[
https://issues.apache.org/jira/browse/GROOVY-12574?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18134530#comment-18134530
]
ASF GitHub Bot commented on GROOVY-12574:
-----------------------------------------
codeconsole opened a new pull request, #3106:
URL: https://github.com/apache/groovy/pull/3106
An implicit-this call from an inner class to a method of an enclosing class
now runs on the innermost enclosing instance whose class has the method, in
dynamic and statically compiled code, inside and outside closures and lambdas.
Before, a closure in a field initializer of an inner class of a
`@CompileStatic` class failed in each `@CompileDynamic` constructor:
```groovy
@CompileStatic
class Outer {
String pub() { 'pub' }
class Inner {
String v = { -> pub() }()
@CompileDynamic Inner() {} // GroovyCastException: Outer$Inner
cannot be cast to Outer
}
}
```
The static compiler gives the call a method target, and the dynamic
invocation writer, which writes the initializer into the `@CompileDynamic`
constructor, kept the closure's this object as the receiver. Lambdas, nested
closures, private methods, interface default methods and trait methods failed
the same way, as did a `@CompileStatic` inner class of a dynamic class.
A method inherited by an intermediate enclosing class (`Outer > Mid extends
Base > Inner` calling `base()`) failed verification under `@CompileStatic`, or
ran on the outermost instance when that class inherits the method too. Both
invocation writers loaded `DeclaringClass.this`; they now use the innermost
enclosing instance whose class extends or implements the declaring class, as
the static writer already did inside closures.
Tests: `InnerClassTest.testUsageOfOuterMethod11` and
`testUsageOfOuterMethod12` (dynamic, `@TypeChecked` and `@CompileStatic`).
`testUsageOfOuterMethod11` and the `@CompileStatic` case of
`testUsageOfOuterMethod12` fail without the change; the other two cases guard
the dynamic and type-checked paths. The `groovy`, `gls`, `bugs` and
`org.codehaus.groovy` packages pass (15,499 tests). The Checkstyle and CodeNarc
gates pass.
Seen in 4.0.33, 5.0.8 and 6.0.0.
> An outer class method called from an inner class runs on the wrong enclosing
> instance
> -------------------------------------------------------------------------------------
>
> Key: GROOVY-12574
> URL: https://issues.apache.org/jira/browse/GROOVY-12574
> Project: Groovy
> Issue Type: Bug
> Components: Compiler, Static compilation
> Affects Versions: 4.0.33, 5.0.8, 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> An implicit-this call from an inner class to a method of an enclosing class
> can run on the wrong instance.
> A closure in a field initializer of an inner class of a {{@CompileStatic}}
> class fails in each {{@CompileDynamic}} constructor:
> {code:groovy}
> import groovy.transform.*
> @CompileStatic
> class Outer {
> String pub() { 'pub' }
> class Inner {
> String v = { -> pub() }()
> @CompileDynamic Inner() {}
> }
> String test() { new Inner().v }
> }
> new Outer().test() // GroovyCastException: Cannot cast object
> 'Outer$Inner@...' with class 'Outer$Inner' to class 'Outer'
> {code}
> The static compiler gives the call a method target, and the dynamic
> invocation writer, which writes the initializer into the {{@CompileDynamic}}
> constructor, keeps the closure's this object as the receiver. Lambdas, nested
> closures, private methods, interface default methods and trait methods fail
> the same way, as does a {{@CompileStatic}} inner class of a dynamic class.
> A method that an intermediate enclosing class inherits is called on the wrong
> instance:
> {code:groovy}
> import groovy.transform.*
> class Base { String base() { 'base' } }
> @CompileStatic
> class Outer {
> class Mid extends Base {
> class Inner { String m() { base() } }
> }
> String test() { def mid = new Mid(); new Mid.Inner(mid).m() }
> }
> new Outer().test() // VerifyError: Bad type on operand stack ...
> Outer$Mid$Inner.m()
> {code}
> Both writers load {{DeclaringClass.this}}, which walks the enclosing
> instances until it reaches the declaring class itself. When {{Outer}} extends
> {{Base}} too, {{base()}} runs on the {{Outer}} instance; dynamic Groovy and
> Java use the {{Mid}} instance. Property reads through an inherited getter
> behave the same.
> Expected: the innermost enclosing instance whose class extends or implements
> the declaring class, as the static writer already uses inside closures.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)