You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Frédéric Chuong (JIRA)" <ji...@apache.org> on 2019/04/03 04:32:00 UTC

[jira] [Updated] (GROOVY-9063) Groovy 2.5.6 @CompileStatic generates invalid bytecode (leading to runtime ClassCastException) when accessing protected instance member from 2 level of nested closures

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

Frédéric Chuong updated GROOVY-9063:
------------------------------------
    Summary: Groovy 2.5.6 @CompileStatic generates invalid bytecode (leading to runtime ClassCastException) when accessing protected instance member from 2 level of nested closures  (was: @CompileStatic generates invalid bytecode (leading to runtime ClassCastException) when accessing protected instance member from 2 level of nested closures)

> Groovy 2.5.6 @CompileStatic generates invalid bytecode (leading to runtime ClassCastException) when accessing protected instance member from 2 level of nested closures
> -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-9063
>                 URL: https://issues.apache.org/jira/browse/GROOVY-9063
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static compilation
>            Reporter: Frédéric Chuong
>            Priority: Major
>
> Provided the following class:
> {code:groovy}
> import groovy.transform.CompileStatic
> import org.objectweb.asm.ClassReader
> import org.objectweb.asm.util.TraceClassVisitor
> @CompileStatic
> class NoBug {
>     protected String message = "hello"
>     void noBug() {
>         { ->
>             { ->
>                 printClass(getClass().name)
>                 message.length()
>             }.call()
>         }.call()
>     }
>     static void printClass(String className) {
>         new ClassReader(className).accept(new TraceClassVisitor(new PrintWriter(System.out)), 0)
>     }
>     static void main(String[] args) {
>         new NoBug().noBug()
>     }
> }
> {code}
> {panel:title=No problem with 2.5.5|borderColor=green}
> Compilation ans execution with Groovy 2.5.5 works fine and produce the following output:
> {noformat}
> // class version 52.0 (52)
> // access flags 0x31
> public final class groovy255/NoBug$_noBug_closure1$_closure2 extends groovy/lang/Closure implements org/codehaus/groovy/runtime/GeneratedClosure {
>   // compiled from: NoBug.groovy
>   OUTERCLASS groovy255/NoBug$_noBug_closure1 doCall ()Ljava/lang/Object;
>   // access flags 0x11
>   public final INNERCLASS groovy255/NoBug$_noBug_closure1$_closure2 null _closure2
>   // 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 Lgroovy255/NoBug$_noBug_closure1$_closure2; 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 14 L0
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     INVOKEVIRTUAL java/lang/Class.getName ()Ljava/lang/String;
>     INVOKESTATIC groovy255/NoBug.printClass (Ljava/lang/String;)V
>     ACONST_NULL
>     POP
>    L1
>     LINENUMBER 15 L1
>     ALOAD 0
>     LDC "message"
>     INVOKEINTERFACE groovy/lang/GroovyObject.getProperty (Ljava/lang/String;)Ljava/lang/Object; (itf)
>     CHECKCAST java/lang/String
>     INVOKEVIRTUAL java/lang/String.length ()I
>     INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
>     ARETURN
>    L2
>     LINENUMBER 14 L2
>    FRAME FULL [] [java/lang/Throwable]
>     NOP
>     ATHROW
>     LOCALVARIABLE this Lgroovy255/NoBug$_noBug_closure1$_closure2; L0 L2 0
>     MAXSTACK = 2
>     MAXLOCALS = 1
>   // access flags 0x1004
>   protected synthetic $getStaticMetaClass()Lgroovy/lang/MetaClass;
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     LDC Lgroovy255/NoBug$_noBug_closure1$_closure2;.class
>     IF_ACMPEQ L0
>     ALOAD 0
>     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass (Ljava/lang/Object;)Lgroovy/lang/MetaClass;
>     ARETURN
>    L0
>    FRAME SAME
>     GETSTATIC groovy255/NoBug$_noBug_closure1$_closure2.$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 groovy255/NoBug$_noBug_closure1$_closure2.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
>    L1
>    FRAME APPEND [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=Problem with 2.5.6|borderColor=red}
> Now with Groovy 2.5.6, compilation seems fine but execution leads to {{ClassCastException}}, and inspection of the inner closure reveals that the bytecode differs:
> {noformat}
> // class version 52.0 (52)
> // access flags 0x31
> public final class groovy256/Bug$_bug_closure1$_closure2 extends groovy/lang/Closure implements org/codehaus/groovy/runtime/GeneratedClosure {
>   // compiled from: Bug.groovy
>   OUTERCLASS groovy256/Bug$_bug_closure1 doCall ()Ljava/lang/Object;
>   // access flags 0x11
>   public final INNERCLASS groovy256/Bug$_bug_closure1$_closure2 null _closure2
>   // 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 Lgroovy256/Bug$_bug_closure1$_closure2; 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 14 L0
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     INVOKEVIRTUAL java/lang/Class.getName ()Ljava/lang/String;
>     INVOKESTATIC groovy256/Bug.printClass (Ljava/lang/String;)V
>     ACONST_NULL
>     POP
>    L1
>     LINENUMBER 15 L1
>     ALOAD 0
>     CHECKCAST groovy256/Bug$_bug_closure1$_closure2
>     INVOKEVIRTUAL groovy256/Bug$_bug_closure1$_closure2.getOwner ()Ljava/lang/Object;
>     CHECKCAST groovy256/Bug
>     GETFIELD groovy256/Bug.message : Ljava/lang/String;
>     INVOKEVIRTUAL java/lang/String.length ()I
>     INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
>     ARETURN
>    L2
>     LINENUMBER 14 L2
>    FRAME FULL [] [java/lang/Throwable]
>     NOP
>     ATHROW
>     LOCALVARIABLE this Lgroovy256/Bug$_bug_closure1$_closure2; L0 L2 0
>     MAXSTACK = 1
>     MAXLOCALS = 1
>   // access flags 0x1004
>   protected synthetic $getStaticMetaClass()Lgroovy/lang/MetaClass;
>     ALOAD 0
>     INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class;
>     LDC Lgroovy256/Bug$_bug_closure1$_closure2;.class
>     IF_ACMPEQ L0
>     ALOAD 0
>     INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass (Ljava/lang/Object;)Lgroovy/lang/MetaClass;
>     ARETURN
>    L0
>    FRAME SAME
>     GETSTATIC groovy256/Bug$_bug_closure1$_closure2.$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 groovy256/Bug$_bug_closure1$_closure2.$staticClassInfo : Lorg/codehaus/groovy/reflection/ClassInfo;
>    L1
>    FRAME APPEND [org/codehaus/groovy/reflection/ClassInfo]
>     ALOAD 1
>     INVOKEVIRTUAL org/codehaus/groovy/reflection/ClassInfo.getMetaClass ()Lgroovy/lang/MetaClass;
>     ARETURN
>     MAXSTACK = 2
>     MAXLOCALS = 2
> }
> {noformat}
> {noformat}
> groovy256.Bug$_bug_closure1 cannot be cast to groovy256.Bug
> java.lang.ClassCastException: groovy256.Bug$_bug_closure1 cannot be cast to groovy256.Bug
> 	at groovy256.Bug$_bug_closure1$_closure2.doCall(Bug.groovy:15)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> 	at java.lang.reflect.Method.invoke(Method.java:498)
> 	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:101)
> 	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:1041)
> 	at groovy.lang.Closure.call(Closure.java:405)
> 	at groovy.lang.Closure.call(Closure.java:399)
> 	at groovy256.Bug$_bug_closure1.doCall(Bug.groovy:13)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> 	at java.lang.reflect.Method.invoke(Method.java:498)
> 	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:101)
> 	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:1041)
> 	at groovy.lang.Closure.call(Closure.java:405)
> 	at groovy.lang.Closure.call(Closure.java:399)
> 	at groovy256.Bug.bug(Bug.groovy:12)
> 	at groovy256.BugTest.bug(BugTest.java:11)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> 	at java.lang.reflect.Method.invoke(Method.java:498)
> 	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
> 	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
> 	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
> 	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
> 	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
> 	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
> 	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
> 	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
> 	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
> 	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
> 	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
> 	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
> 	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
> 	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
> 	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
> 	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
> 	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
> 	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> 	at java.lang.reflect.Method.invoke(Method.java:498)
> 	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
> 	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
> 	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
> 	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
> 	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
> 	at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> 	at java.lang.reflect.Method.invoke(Method.java:498)
> 	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
> 	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
> 	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:175)
> 	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:157)
> 	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
> 	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
> 	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
> 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
> 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
> 	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
> 	at java.lang.Thread.run(Thread.java:748)
> {noformat}
> {panel}
> {panel:title=Note}
> Replacing
> {noformat}
>                 message.length()
> {noformat}
> with
> {noformat}
>                 this.@message.length()
> {noformat}
> does not exhibit that problem.
> {panel}
> This may be related to [GROOVY-7687] which relates to similar conditions (nested closure and {{\@CompileStatic}}



--
This message was sent by Atlassian JIRA
(v7.6.3#76005)