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

STC: "putAt" shortcut notation to outer class private field from Closure leads to GroovyCastException

    XMLWordPrintableJSON

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 3.0.6
    • Fix Version/s: 3.0.7, 4.0.0-alpha-2, 2.5.15
    • Component/s: Static compilation
    • Labels:
      None
    • Environment:

      Description

      Given the following usage of the "putAt" shortcut notation to alter an outer class private field:

      import groovy.transform.CompileStatic
      import org.junit.jupiter.api.Test
      import org.objectweb.asm.ClassReader
      import org.objectweb.asm.util.TraceClassVisitor
      
      @CompileStatic
      class Bar {
          private Map<String, Boolean> map = [:]
      
          @Test
          void test() {
              { ->
                  // troubleshooting only
                  printClass(new Exception().stackTrace[0].className)
      
                  map['foo'] = true // line 16
              }.call()
          }
      
          static void printClass(String className) {
              new ClassReader(className).accept(new TraceClassVisitor(new PrintWriter(System.err)), ClassReader.EXPAND_FRAMES)
          }
      }
      

      With Groovy 3.0.6, execution leads to the following error:

      org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'Bar$_test_closure1@2a65fe7c' with class 'Bar$_test_closure1' to class 'Bar'
      	at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnSAM(DefaultTypeTransformation.java:415)
      	at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.continueCastOnNumber(DefaultTypeTransformation.java:329)
      	at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:243)
      	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:615)
      	at Bar$_test_closure1.doCall(Bar.groovy:16)
      	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: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:1034)
      	at groovy.lang.Closure.call(Closure.java:412)
      	at groovy.lang.Closure.call(Closure.java:406)
      	at Bar.test(Bar.groovy:12)
      

      This does not fail with Groovy 3.0.5.

      The bytecode for line 16 is different between 3.0.5 and 3.0.6:

      Groovy 3.0.6
         L1
          LINENUMBER 16 L1
          ICONST_1
          ISTORE 1
          LDC LBar$_test_closure1;.class
          ALOAD 0
          LDC LBar;.class
          INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType (Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
          CHECKCAST Bar
          INVOKESTATIC Bar.pfaccess$0 (LBar;)Ljava/util/Map;
          LDC "putAt"
          CHECKCAST java/lang/String
          ICONST_2
          ANEWARRAY java/lang/Object
          DUP
          ICONST_0
          LDC "foo"
          AASTORE
          DUP
          ICONST_1
          ILOAD 1
          INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;
          AASTORE
          INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.invokeMethodN (Ljava/lang/Class;Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
          POP
          ILOAD 1
          INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;
          ARETURN
      
      Groovy 3.0.5, no error
         L1
          LINENUMBER 16 L1
          ICONST_1
          ISTORE 1
          ALOAD 0
          LDC "map"
          INVOKEINTERFACE groovy/lang/GroovyObject.getProperty (Ljava/lang/String;)Ljava/lang/Object; (itf)
          LDC "foo"
          ILOAD 1
          INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;
          INVOKESTATIC org/codehaus/groovy/runtime/DefaultGroovyMethods.putAt (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V
          ACONST_NULL
          POP
          ILOAD 1
          INVOKESTATIC java/lang/Boolean.valueOf (Z)Ljava/lang/Boolean;
          ARETURN
      

      There was a previously fixed issue, GROOVY-9385 fixed in 3.0.1, having similar conditions (@CompileStatic, closure, private member), I'm not sure whether it's related.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                emilles Eric Milles
                Reporter:
                chuong_f Frédéric Chuong
              • Votes:
                0 Vote for this issue
                Watchers:
                2 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 - 50m
                  50m