Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-9086

@CompileStatic of closure calls with OWNER_FIRST fail at runtime with ClassCastException

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 2.5.6
    • Fix Version/s: 3.0.0-beta-1, 2.5.7
    • Component/s: Static compilation
    • Labels:
      None
    • Environment:
      WIndows7, Groovy 2.5.6, Java 8 + 11

      Description

      The attached script fails with a ClassCastException, when method m2 is annotated with 

       

      strategy = Closure.OWNER_FIRST
      

       but work with 

       

      strategy = Closure.DELEGATE_FIRST
      

       The expected system-out should be 

       

      c1Method called

       in both cases. The generated bytecode for closure Test$_test_closure1$_closure2.class contains a call to getDelegate() instead of getOwner() in case of Closure.OWNER_FIRST. But correctly generates code getOwner() in case of Closure.DELEGATE_FIRST.

      The resulting ClassCastException at runtime is:

      java.lang.ClassCastException: Test$C2 cannot be cast to groovy.lang.Closure
      at Test$_test_closure1$_closure2.doCall(TestScript.groovy:28)
      at Test$_test_closure1$_closure2.doCall(TestScript.groovy)
      at Test.m2(TestScript.groovy:20)
      at Test$_test_closure1.doCall(TestScript.groovy:26)
      at Test$_test_closure1.doCall(TestScript.groovy)
      at Test.m1(TestScript.groovy:8)
      at Test.test(TestScript.groovy:25)
      at Test$test.call(Unknown Source)
      at TestScript.run(TestScript.groovy:42)
      

      The wrong bytecode for case Closure.OWNER_FIRST:

      public java.lang.Object doCall(java.lang.Object);
      descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
      flags: ACC_PUBLIC
      Code:
      stack=1, locals=2, args_size=2
      0: aload_0
      1: checkcast #2 // class Test$_test_closure1$_closure2
      4: invokevirtual #29 // Method getDelegate:()Ljava/lang/Object;
      7: checkcast #4 // class groovy/lang/Closure
      10: invokevirtual #30 // Method groovy/lang/Closure.getDelegate:()Ljava/lang/Object;
      13: checkcast #32 // class Test$C1
      16: invokevirtual #36 // Method Test$C1.c1Method:()V
      19: aconst_null
      20: areturn
      

      The correct bytecode for case Closure.DELEGATE_FIRST:

      public java.lang.Object doCall(java.lang.Object);
      descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
      flags: ACC_PUBLIC
      Code:
      stack=1, locals=2, args_size=2
      0: aload_0
      1: checkcast #2 // class Test$_test_closure1$_closure2
      4: invokevirtual #29 // Method getOwner:()Ljava/lang/Object;
      7: checkcast #4 // class groovy/lang/Closure
      10: invokevirtual #32 // Method groovy/lang/Closure.getDelegate:()Ljava/lang/Object;
      13: checkcast #34 // class Test$C1
      16: invokevirtual #38 // Method Test$C1.c1Method:()V
      19: aconst_null
      20: areturn
      

        Attachments

        1. TestScript.groovy
          1.0 kB
          Andreas Turban
        2. StaticTypeCheckingVisitor.java
          292 kB
          Eric Milles

          Issue Links

            Activity

              People

              • Assignee:
                paulk Paul King
                Reporter:
                AndreasTu Andreas Turban
              • Votes:
                1 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:

                  Time Tracking

                  Estimated:
                  Original Estimate - Not Specified
                  Not Specified
                  Remaining:
                  Remaining Estimate - 0h
                  0h
                  Logged:
                  Time Spent - 0.5h
                  0.5h