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)