Paul King created GROOVY-12595:
----------------------------------

             Summary: @CompileStatic: calling clone() on an element of a 
multi-dimensional array fails bytecode verification
                 Key: GROOVY-12595
                 URL: https://issues.apache.org/jira/browse/GROOVY-12595
             Project: Groovy
          Issue Type: Bug
            Reporter: Paul King


Under {{@CompileStatic}}, calling {{clone()}} directly on an element read from 
an array of arrays generates bytecode that the JVM verifier rejects. This 
happens for both primitive and reference element types.

{code:groovy}
import groovy.transform.CompileStatic

@CompileStatic
class C {
    static float[] one(float[][] a) { a[0].clone() }
    static String[] two(String[][] a) { a[0].clone() }
}

float[][] f = [[1f, 2f] as float[]]
assert C.one(f) == [1f, 2f] as float[]
{code}

Loading {{C}} fails:
{noformat}
java.lang.VerifyError: Bad access to protected data in invokevirtual
Exception Details:
  Location:
    C.one([[F)[F @5: invokevirtual
  Reason:
    Type 'java/lang/Object' (current frame, stack[0]) is not assignable to 'C'
{noformat}

h3. Generated bytecode
{noformat}
0: aload_0
1: iconst_0
2: invokestatic  
org/codehaus/groovy/runtime/BytecodeInterface8.objectArrayGet:([Ljava/lang/Object;I)Ljava/lang/Object;
5: invokevirtual java/lang/Object.clone:()Ljava/lang/Object;    <-- receiver is 
still typed Object; no checkcast [F
8: invokedynamic cast:(Ljava/lang/Object;)[F
{noformat}

h3. Cause
The type checker models array {{clone()}} as {{Object#clone()}} (see 
GROOVY-10319). When it computes the receiver cast, 
{{StaticInvocationWriter.CheckcastReceiverExpression#getType()}} skips the 
{{checkcast}} whenever the target is declared on {{Object}}. But 
{{Object#clone()}} is protected, so the verifier only accepts the 
{{invokevirtual}} if the receiver is the calling class or an array type. An 
element read with {{objectArrayGet}} is typed {{Object}} on the stack, so the 
call is rejected.

These forms are unaffected because the receiver is already typed as the array:
* {{a.clone()}} where {{a}} is a {{float[]}} or {{float[][]}} parameter or local
* {{float[] b = a[0]; b.clone()}}
* {{def b = a[0]; b.clone()}}

h3. Workaround
Assign the element to a local variable before calling {{clone()}}.

h3. Affects
6.0.0, and current master (7.0.0-SNAPSHOT) and GROOVY_6_0_X.

h3. Proposed fix
In {{CheckcastReceiverExpression#getType()}}, keep the array receiver type (and 
so emit {{checkcast}}) when the target is a no-arg {{clone()}} on an array, 
instead of falling back to {{Object}}. Regression test: 
{{ArraysAndCollectionsSTCTest#testArrayCloneOfArrayElement}}, which also runs 
under static compilation via {{ArraysAndCollectionsStaticCompileTest}}.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to