Details
-
Bug
-
Status: Closed
-
Critical
-
Resolution: Fixed
-
2.4.5
-
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 }