[ 
https://issues.apache.org/jira/browse/GROOVY-12574?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Scott Murphy Heiberg updated GROOVY-12574:
------------------------------------------
    Description: 
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.

PR: https://github.com/apache/groovy/pull/3106

  was:
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.


> 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.
> PR: https://github.com/apache/groovy/pull/3106



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

Reply via email to