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

Spread safe method calls on list literals result in the list expression being evaluated twice (SC)

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Critical
    • Resolution: Fixed
    • 2.4.5
    • 2.4.6
    • Static compilation
    • None

    Description

      When a list literal is the receiver for a statically compiled spread safe method call expression, the instructions to create the list are included twice. Example:

      @groovy.transform.CompileStatic
      void test() {
          ['a']*.size()
      }
      

      The bytecode for the method above is:

       L0
          LINENUMBER 3 L0
          NEW java/util/ArrayList
          DUP
          INVOKESPECIAL java/util/ArrayList.<init> ()V
          ASTORE 1
         L1
          ALOAD 1
          POP
          ICONST_1
          ANEWARRAY java/lang/Object
          DUP
          ICONST_0
          LDC "a"
          AASTORE
          INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
          IFNULL L2
          ACONST_NULL
          ASTORE 2
         L3
          ICONST_1
          ANEWARRAY java/lang/Object
          DUP
          ICONST_0
          LDC "a"
          AASTORE
          INVOKESTATIC org/codehaus/groovy/runtime/ScriptBytecodeAdapter.createList ([Ljava/lang/Object;)Ljava/util/List;
          INVOKEINTERFACE java/util/List.iterator ()Ljava/util/Iterator;
          ASTORE 3
         L4
          ALOAD 3
          INVOKEINTERFACE java/util/Iterator.hasNext ()Z
          IFEQ L2
          ALOAD 3
          INVOKEINTERFACE java/util/Iterator.next ()Ljava/lang/Object;
          ASTORE 2
          ALOAD 1
          ALOAD 2
          DUP
          ASTORE 4
          IFNULL L5
          ALOAD 4
          INVOKESTATIC org/codehaus/groovy/runtime/typehandling/ShortTypeHandling.castToString (Ljava/lang/Object;)Ljava/lang/String;
          CHECKCAST java/lang/String
          INVOKESTATIC org/codehaus/groovy/runtime/StringGroovyMethods.size (Ljava/lang/String;)I
          INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
          GOTO L6
         L5
          ACONST_NULL
         L6
          INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
          POP
          GOTO L4
         L2
          ALOAD 1
          POP
      

      If the list expression contains method calls that have side effects, this can cause serious problems. The example below is trivial, but shows the general case:

      @groovy.transform.CompileStatic
      void test() {
          def list = ['abc']
          def lengths = [list.removeAt(0)]*.length() // throws IndexOutOfBoundsException
      }
      

      Attachments

        Activity

          People

            shils Shil Sinha
            shils Shil Sinha
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: