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:29:02 UTC

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

Frédéric Chuong created GROOVY-9063:
---------------------------------------

             Summary: @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


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)