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)