Details
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
- Blocked
-
GROOVY-9872 GroovyCastException when using a map while iterating over something in a @CompileStatic object
- Closed