[ 
https://issues.apache.org/jira/browse/GROOVY-7526?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14647334#comment-14647334
 ] 

Jochen Theodorou commented on GROOVY-7526:
------------------------------------------

The reduced bytecode of this is the following (using asm){code}
  // access flags 0x1
  public foo()Ljava/lang/Object;
   L0
    INVOKESTATIC JavaTool.getData ()LJavaTool$Data;
    ASTORE 1
   L2
    ALOAD 1
    DUP
    IFNONNULL L3
    GOTO L4
   L3
   FRAME FULL [test JavaTool$Data] [JavaTool$Data]
    GETFIELD JavaTool$Data.fieldData : Ljava/lang/String;
   L4
   FRAME SAME1 java/lang/Object
    INVOKEDYNAMIC cast(Ljava/lang/String;)Z [...]
    IFEQ L5
   L6
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "HAS GROOVY TOOL DATA"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
    ACONST_NULL
    ARETURN
{code}
The actual error is then to be found in the invokedynamic call for cast. The 
call expects a String on the stack, but the frame claims the stack element is 
of type Object. The @CompileStatic version does not have this problem since it 
will call booleanUnbox(Object)Z, then there is no type mismatch. But simulating 
this method call, will provoke the same error:{code:Java}
public class JavaTool {
    public static Data getData() {
        return new Data("fieldDataString")
    }

    public static class Data {
        public String fieldData;

        public Data(String fieldData) {
            this.fieldData = fieldData
        }
    }
}
boolean bar(String s){true}

@CompileStatic
def foo() {
    JavaTool.Data jdata = JavaTool.getData()
    if ( bar(jdata?.fieldData) ) {
        System.out.println("HAS GROOVY TOOL DATA")
    }
}
foo(){code]
now the method bar will make the boolean conversion and expect a String.  
Without frame information this will then lead to "java.lang.VerifyError: 
(class: test, method: foo signature: ()Ljava/lang/Object;) Incompatible 
argument to function" and forcing higher bytecodes, by for example using 
groovy.target.bytecode=1.7, will produce "java.lang.VerifyError: Bad type on 
operand stack". Since this is without indy I can identify this as a static 
compilation problem.


> Safe navigation broken on java fields with indy and @CompileStatic
> ------------------------------------------------------------------
>
>                 Key: GROOVY-7526
>                 URL: https://issues.apache.org/jira/browse/GROOVY-7526
>             Project: Groovy
>          Issue Type: Bug
>          Components: Compiler
>    Affects Versions: 2.4.3
>            Reporter: Matthew Turnbull
>            Priority: Minor
>         Attachments: groovy_test.tar.gz
>
>
> Trying to directly access a native Java object field, from a Groovy class 
> compiled with invokedynamic and @CompileStatic, causes a 
> "java.lang.VerifyError: Bad type on operand stack" error.
> I have reproduced this on Linux with Groovy 2.4.3/2.4.4 and Java 1.7.0_80 
> (64bit).
> {noformat}
> ~ $ cd /tmp/groovy_test/
> /tmp/groovy_test $ rm -f com/*.class
> /tmp/groovy_test $ javac com/*.java
> /tmp/groovy_test $ /opt/groovy-2.4.4/bin/groovyc --indy --configscript 
> config.groovyc com/*.groovy
> /tmp/groovy_test $ java -cp .:/opt/groovy-2.4.4/lib/groovy-2.4.4.jar com.Test
> {noformat}
> {noformat}
> Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
> Exception Details:
>   Location:
>     com/Controller.run()V @55: invokedynamic
>   Reason:
>     Type 'java/lang/Object' (current frame, stack[0]) is not assignable to 
> 'java/lang/String'
>   Current Frame:
>     bci: @55
>     flags: { }
>     locals: { 'com/Controller', 'com/GroovyTool$Data', 'com/GroovyTool$Data', 
> 'com/JavaTool$Data' }
>     stack: { 'java/lang/Object' }
>   Bytecode:
>     0000000: b800 224c 2b57 2b59 4dc6 000a 2cb6 0028
>     0000010: a700 0401 ba00 3600 0099 000d b200 3c12
>     0000020: 3eb6 0044 0157 b800 494e 2d57 2d59 c700
>     0000030: 06a7 0006 b400 4fba 0036 0000 9900 0db2
>     0000040: 003c 1251 b600 4401 57b1               
>   Stackmap Table:
>     append_frame(@19,Object[#36],Object[#36])
>     same_locals_1_stack_item_frame(@20,Object[#87])
>     same_frame(@38)
>     
> full_frame(@52,{Object[#2],Object[#36],Object[#36],Object[#75]},{Object[#75]})
>     same_locals_1_stack_item_frame(@55,Object[#4])
>     same_frame(@73)
>       at java.lang.Class.getDeclaredConstructors0(Native Method)
>       at java.lang.Class.privateGetDeclaredConstructors(Class.java:2595)
>       at java.lang.Class.getConstructor0(Class.java:2895)
>       at java.lang.Class.newInstance(Class.java:354)
>       at com.Test.loadController(Test.java:20)
>       at com.Test.test(Test.java:30)
>       at com.Test.main(Test.java:10)
> {noformat}
> Work arounds that I found:
> * Remove @CompileStatic
> * Disable --indy
> * Cast the safely navigated field to it's object type i.e. (String)data?.field
> * Define a getter method to use in lieu of the field.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to