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 2019/04/18 15:28:00 UTC
[jira] [Commented] (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:comment-tabpanel&focusedCommentId=16821218#comment-16821218 ]
Eric Milles commented on GROOVY-9063:
-------------------------------------
I believe the changes indicated in GROOVY-9086 fix this bug as well.
> 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: Compiler, Static compilation
> Affects Versions: 2.5.6
> Environment: Groovy 2.5.6
> @groovy.transform.CompileStatic
> 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}
> The observed change in class structure is as follows:
> {code}
> 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
> {code}
> becomes the following with 2.5.6:
> {code}
> L1
> LINENUMBER 15 L1
> ALOAD 0
> CHECKCAST groovy256/Bug$_bug_closure1$_closure2
> INVOKEVIRTUAL groovy256/Bug$_bug_closure1$_closure2.getOwner ()Ljava/lang/Object; << retrieve owner of the 2nd closure, ie. the 1st closure
> CHECKCAST groovy256/Bug <<<<<<< trying to cast owner (1st closure) to outer class type
> GETFIELD groovy256/Bug.message : Ljava/lang/String;
> INVOKEVIRTUAL java/lang/String.length ()I
> INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
> ARETURN
> {code}
> {panel:title=Note}
> Replacing
> {noformat}
> message.length()
> {noformat}
> with
> {noformat}
> this.@message.length()
> {noformat}
> does not exhibit that problem.
> {panel}
> This might be related to [GROOVY-7687] or [GROOVY-7996] (both affected [2.5.6|https://issues.apache.org/jira/projects/GROOVY/versions/12344751]) which relates to similar conditions (nested closure and {{\@CompileStatic}}).
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)