You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Eric Milles (Jira)" <ji...@apache.org> on 2020/02/05 15:51:00 UTC

[jira] [Assigned] (GROOVY-9385) Access to outer class private field from closure leads to ClassCastException (@CompileStatic)

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

Eric Milles reassigned GROOVY-9385:
-----------------------------------

    Assignee: Eric Milles

> Access to outer class private field from closure leads to ClassCastException (@CompileStatic)
> ---------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-9385
>                 URL: https://issues.apache.org/jira/browse/GROOVY-9385
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static compilation
>    Affects Versions: 3.0.0-rc-3
>         Environment: openjdk version "11.0.5" 2019-10-15 LTS
> OpenJDK Runtime Environment Zulu11.35+15-CA (build 11.0.5+10-LTS)
> OpenJDK 64-Bit Server VM Zulu11.35+15-CA (build 11.0.5+10-LTS, mixed mode)
> Gradle 6.1.1
> Windows 10
> JAVA_TOOL_OPTIONS: "-XX:+UsePerfData" "-Duser.language=en" "-Duser.country=US" "-Duser.timezone=UTC" "-Dfile.encoding=UTF-8"
> ASM 7.3.1
>            Reporter: Frédéric Chuong
>            Assignee: Eric Milles
>            Priority: Major
>
> Given the following code:
> {code:groovy}
> package org.example
> import groovy.transform.CompileStatic
> import org.objectweb.asm.ClassReader
> import org.objectweb.asm.util.TraceClassVisitor
> @CompileStatic
> class OuterClass {
>     private int counter
>     void call() {
>         { ->
>             def closureClassName = new Throwable().stackTrace[0].className
>             counter++
>             printClass(closureClassName)
>         }.call()
>     }
>     static void printClass(String className) {
>         new ClassReader(className).accept(new TraceClassVisitor(new PrintWriter(System.err)), ClassReader.EXPAND_FRAMES)
>     }
>     static void main(String[] args) {
>         try {
>             new OuterClass().call()
>         } catch (ClassCastException e) {
>             printClass(e.stackTrace[0].className)
>             throw e
>         }
>     }
> }
> {code}
> {panel:title=Groovy 3.0.0-rc-3|borderColor=red}
> (/) Compilation OK
> (x) Runtime {{ClassCastException}}
> {noformat}
> Exception in thread "main" java.lang.ClassCastException: class org.example.OuterClass$_call_closure1 cannot be cast to class org.example.OuterClass (org.example.OuterClass$_call_closure1 and org.example.OuterClass are in unnamed module of loader 'app')
> 	at org.example.OuterClass$_call_closure1.doCall(OuterClass.groovy:14)
> 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> 	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> 	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
> 	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
> 	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
> 	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:263)
> 	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
> 	at groovy.lang.Closure.call(Closure.java:412)
> 	at groovy.lang.Closure.call(Closure.java:406)
> 	at org.example.OuterClass.call(OuterClass.groovy:12)
> 	at org.example.OuterClass.main(OuterClass.groovy:26)
> Execution failed for task ':OuterClass.main()'.
> > Process 'command 'C:/Program Files/Java/zulu11.35.15-ca-jdk11.0.5-win_x64/bin/java.exe'' finished with non-zero exit value 1
> {noformat}
> Disassembled view from ASM (preceding the exception):
> {noformat}
> // class version 55.0 (55)
> // access flags 0x31
> public final class org/example/OuterClass$_call_closure1 extends groovy/lang/Closure implements org/codehaus/groovy/runtime/GeneratedClosure {
>   // compiled from: OuterClass.groovy
>   OUTERCLASS org/example/OuterClass call ()V
>   // access flags 0x11
>   public final INNERCLASS org/example/OuterClass$_call_closure1 null _call_closure1
>   // access flags 0x100A
>   private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo
>   // access flags 0x1089
>   public static transient synthetic Z __$stMC
>   // access flags 0x1
>   public <init>(Ljava/lang/Object;Ljava/lang/Object;)V
>    L0
>     ALOAD 0
>     ALOAD 1
>     ALOAD 2
>     INVOKESPECIAL groovy/lang/Closure.<init> (Ljava/lang/Object;Ljava/lang/Object;)V
>    L1
>     RETURN
>     LOCALVARIABLE this Lorg/example/OuterClass$_call_closure1; L0 L1 0
>     LOCALVARIABLE _outerInstance Ljava/lang/Object; L0 L1 1
>     LOCALVARIABLE _thisObject Ljava/lang/Object; L0 L1 2
>     MAXSTACK = 3
>     MAXLOCALS = 3
>   // access flags 0x1
>   public doCall()Ljava/lang/Object;
>    L0
>     LINENUMBER 13 L0
>     NEW java/lang/Throwable
>     DUP
>     INVOKESPECIAL java/lang/Throwable.<init> ()V
>     INVOKEVIRTUAL java/lang/Throwable.getStackTrace ()[Ljava/lang/StackTraceElement;
>     ICONST_0
>     INVOKESTATIC org/codehaus/groovy/runtime/BytecodeInterface8.objectArrayGet ([Ljava/lang/Object;I)Ljava/lang/Object;
>     CHECKCAST java/lang/StackTraceElement
>     INVOKEVIRTUAL java/lang/StackTraceElement.getClassName ()Ljava/lang/String;
>     ASTORE 1
>    L1
>     ALOAD 1
>     POP
>    L2
>     LINENUMBER 14 L2
>     ALOAD 0
>     CHECKCAST org/example/OuterClass
>     LDC "counter"
>     INVOKEINTERFACE groovy/lang/GroovyObject.getProperty (Ljava/lang/String;)Ljava/lang/Object; (itf)
>     DUP
>     ASTORE 2
>     INVOKESTATIC org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.intUnbox (Ljava/lang/Object;)I
>     ICONST_1
>     IADD
>     DUP
>     INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
>     LDC Lorg/example/OuterClass$_call_closure1;.class
>     ALOAD 0
>     LDC "counter"
>     CHECKCAST java/lang/String
>     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.setGroovyObjectProperty (Ljava/lang/Object;Ljava/lang/Class;Lgroovy/lang/GroovyObject;Ljava/lang/String;)V
>     POP
>     ALOAD 2
>     POP
>    L3
>     LINENUMBER 16 L3
>     ALOAD 1
>     INVOKESTATIC org/example/OuterClass.printClass (Ljava/lang/String;)V
>     ACONST_NULL
>     ARETURN
>    L4
>    FRAME FULL [] [java/lang/Throwable]
>     NOP
>     ATHROW
>     LOCALVARIABLE this Lorg/example/OuterClass$_call_closure1; L0 L4 0
>     LOCALVARIABLE closureClassName Ljava/lang/String; L1 L4 1
>     MAXSTACK = 5
>     MAXLOCALS = 3
>   // access flags 0x1004
>   protected synthetic $getStaticMetaClass()Lgroovy/lang/MetaClass;
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     LDC Lorg/example/OuterClass$_call_closure1;.class
>     IF_ACMPEQ L0
>     ALOAD 0
>     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass (Ljava/lang/Object;)Lgroovy/lang/MetaClass;
>     ARETURN
>    L0
>    FRAME FULL [org/example/OuterClass$_call_closure1] []
>     GETSTATIC org/example/OuterClass$_call_closure1.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
>     ASTORE 1
>     ALOAD 1
>     IFNONNULL L1
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     INVOKESTATIC org/codehaus/groovy/reflection/ClassInfo.getClassInfo (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;
>     DUP
>     ASTORE 1
>     PUTSTATIC org/example/OuterClass$_call_closure1.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
>    L1
>    FRAME FULL [org/example/OuterClass$_call_closure1 org/codehaus/groovy/reflection/ClassInfo] []
>     ALOAD 1
>     INVOKEVIRTUAL org/codehaus/groovy/reflection/ClassInfo.getMetaClass ()Lgroovy/lang/MetaClass;
>     ARETURN
>     MAXSTACK = 2
>     MAXLOCALS = 2
> }
> {noformat}
> {panel}
> {panel:title=Groovy 2.5.9|borderColor=green}
> (/) Compilation OK
> (/) No runtime exception
> Disassembled view of the closure
> {noformat}
> // class version 55.0 (55)
> // access flags 0x31
> public final class org/example/OuterClass$_call_closure1 extends groovy/lang/Closure implements org/codehaus/groovy/runtime/GeneratedClosure {
>   // compiled from: OuterClass.groovy
>   OUTERCLASS org/example/OuterClass call ()V
>   // access flags 0x11
>   public final INNERCLASS org/example/OuterClass$_call_closure1 null _call_closure1
>   // access flags 0x100A
>   private static synthetic Lorg/codehaus/groovy/reflection/ClassInfo; $staticClassInfo
>   // access flags 0x1089
>   public static transient synthetic Z __$stMC
>   // access flags 0x1
>   public <init>(Ljava/lang/Object;Ljava/lang/Object;)V
>    L0
>     ALOAD 0
>     ALOAD 1
>     ALOAD 2
>     INVOKESPECIAL groovy/lang/Closure.<init> (Ljava/lang/Object;Ljava/lang/Object;)V
>    L1
>     RETURN
>     LOCALVARIABLE this Lorg/example/OuterClass$_call_closure1; L0 L1 0
>     LOCALVARIABLE _outerInstance Ljava/lang/Object; L0 L1 1
>     LOCALVARIABLE _thisObject Ljava/lang/Object; L0 L1 2
>     MAXSTACK = 3
>     MAXLOCALS = 3
>   // access flags 0x1
>   public doCall()Ljava/lang/Object;
>    L0
>     LINENUMBER 13 L0
>     NEW java/lang/Throwable
>     DUP
>     INVOKESPECIAL java/lang/Throwable.<init> ()V
>     INVOKEVIRTUAL java/lang/Throwable.getStackTrace ()[Ljava/lang/StackTraceElement;
>     ICONST_0
>     INVOKESTATIC org/codehaus/groovy/runtime/BytecodeInterface8.objectArrayGet ([Ljava/lang/Object;I)Ljava/lang/Object;
>     CHECKCAST java/lang/StackTraceElement
>     INVOKEVIRTUAL java/lang/StackTraceElement.getClassName ()Ljava/lang/String;
>     ASTORE 1
>    L1
>     ALOAD 1
>     POP
>    L2
>     LINENUMBER 14 L2
>     ALOAD 0
>     INVOKEVIRTUAL groovy/lang/Closure.getThisObject ()Ljava/lang/Object;
>     CHECKCAST org/example/OuterClass
>     INVOKESTATIC org/example/OuterClass.pfaccess$0 (Lorg/example/OuterClass;)I
>     DUP
>     ISTORE 2
>     ICONST_1
>     IADD
>     DUP
>     INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
>     LDC Lorg/example/OuterClass$_call_closure1;.class
>     ALOAD 0
>     LDC "counter"
>     CHECKCAST java/lang/String
>     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.setGroovyObjectProperty (Ljava/lang/Object;Ljava/lang/Class;Lgroovy/lang/GroovyObject;Ljava/lang/String;)V
>     POP
>     ILOAD 2
>     POP
>    L3
>     LINENUMBER 16 L3
>     ALOAD 1
>     INVOKESTATIC org/example/OuterClass.printClass (Ljava/lang/String;)V
>     ACONST_NULL
>     ARETURN
>    L4
>    FRAME FULL [] [java/lang/Throwable]
>     NOP
>     ATHROW
>     LOCALVARIABLE this Lorg/example/OuterClass$_call_closure1; L0 L4 0
>     LOCALVARIABLE closureClassName Ljava/lang/String; L1 L4 1
>     MAXSTACK = 5
>     MAXLOCALS = 3
>   // access flags 0x1004
>   protected synthetic $getStaticMetaClass()Lgroovy/lang/MetaClass;
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     LDC Lorg/example/OuterClass$_call_closure1;.class
>     IF_ACMPEQ L0
>     ALOAD 0
>     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass (Ljava/lang/Object;)Lgroovy/lang/MetaClass;
>     ARETURN
>    L0
>    FRAME FULL [org/example/OuterClass$_call_closure1] []
>     GETSTATIC org/example/OuterClass$_call_closure1.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
>     ASTORE 1
>     ALOAD 1
>     IFNONNULL L1
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     INVOKESTATIC org/codehaus/groovy/reflection/ClassInfo.getClassInfo (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo;
>     DUP
>     ASTORE 1
>     PUTSTATIC org/example/OuterClass$_call_closure1.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
>    L1
>    FRAME FULL [org/example/OuterClass$_call_closure1 org/codehaus/groovy/reflection/ClassInfo] []
>     ALOAD 1
>     INVOKEVIRTUAL org/codehaus/groovy/reflection/ClassInfo.getMetaClass ()Lgroovy/lang/MetaClass;
>     ARETURN
>     MAXSTACK = 2
>     MAXLOCALS = 2
> }
> {noformat}
> {panel}
> It seems the call to {{getThisObject}} is missing (not sure whether Groovy 3 expects that part to change):
> {panel:title=Groovy 3.0.0-rc-3}
> {noformat}
>     LINENUMBER 14 L2
>     ALOAD 0
>     CHECKCAST org/example/OuterClass
> {noformat}
> {panel}
> {panel:title=Groovy 2.5.9}
> {noformat}
>     LINENUMBER 14 L2
>     ALOAD 0
>     INVOKEVIRTUAL groovy/lang/Closure.getThisObject ()Ljava/lang/Object;
>     CHECKCAST org/example/OuterClass
> {noformat}
> {panel}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)