You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@groovy.apache.org by "Frédéric Chuong (Jira)" <ji...@apache.org> on 2020/10/07 00:35:00 UTC

[jira] [Created] (GROOVY-9771) STC: "putAt" shortcut notation to outer class private field from Closure leads to GroovyCastException

Frédéric Chuong created GROOVY-9771:
---------------------------------------

             Summary: STC: "putAt" shortcut notation to outer class private field from Closure leads to GroovyCastException
                 Key: GROOVY-9771
                 URL: https://issues.apache.org/jira/browse/GROOVY-9771
             Project: Groovy
          Issue Type: Bug
          Components: Static compilation
    Affects Versions: 3.0.6
         Environment: openjdk version "1.8.0_265"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_265-b01)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.265-b01, mixed mode)

Gradle 6.6.1
JUnit 5.7.0
ASM 9.0
            Reporter: Frédéric Chuong


Given the following usage of the "putAt" shortcut notation to alter an outer class private field:
{code:Groovy}
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)
    }
}
{code}

With Groovy 3.0.6, execution leads to the following error:
{noformat}
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)
{noformat}


This does not fail with Groovy 3.0.5.


The bytecode for line 16 is different between 3.0.5 and 3.0.6:
{panel:title=Groovy 3.0.6|borderColor=red}
{noformat}
   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
{noformat}
{panel}

{panel:title=Groovy 3.0.5, no error|borderColor=green}
{noformat}
   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
{noformat}
{panel}



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.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)