Paul King created GROOVY-12631:
----------------------------------

             Summary: CS: assigning to an attribute obj.@name = value where the 
receiver class has no field name, but there is an extension getter getName(), 
crashes the compiler with an ArrayIndexOutOfBoundsException
                 Key: GROOVY-12631
                 URL: https://issues.apache.org/jira/browse/GROOVY-12631
             Project: Groovy
          Issue Type: Bug
          Components: Static compilation, Static Type Checker
    Affects Versions: 6.0.0
            Reporter: Paul King


h2. Description

Under {{@CompileStatic}}, assigning to an attribute ({{obj.@name = value}}) 
where the receiver class has no field {{name}}, but there is an extension 
getter {{getName()}}, crashes the compiler with an 
{{ArrayIndexOutOfBoundsException}}. A "No such attribute" error is expected 
instead, as for a class with no such field and no extension getter.

{code:groovy}
@groovy.transform.CompileStatic
void update(File file) {
    file.@text = 'b'     // Internal compiler error: 
ArrayIndexOutOfBoundsException
}
{code}

The same crash occurs for the compound form ({{file.@text += 'b'}}), and since 
5.0 for {{sb.@length = 1}} on a {{StringBuilder}}, where the GDK's extension 
{{getLength()}} matches.

For comparison, a class whose accessors are real methods is rejected correctly:

{code:groovy}
class B { int getX() { 1 }; void setX(int v) {} }

@groovy.transform.CompileStatic
void update(B b) {
    b.@x = 1    // [Static type checking] - No such attribute: x for class: B
}
{code}

Under {{@TypeChecked}}, {{file.@text = 'b'}} compiles and then fails at run 
time with {{MissingFieldException: No such field: text for class: 
java.io.File}}, as dynamic code does.

h2. Versions

Seen on 4.0.32, 5.0.6, 6.0.0 and master. The {{StringBuilder}} variant gives 
the expected error on 4.0.32, because the extension {{getLength()}} did not 
exist there.

h2. Cause

The type checker resolves the attribute expression to the extension *getter* 
and records it as the {{DIRECT_METHOD_CALL_TARGET}} of the 
{{AttributeExpression}}:

{noformat}
file.@text  ->  java.io.File#getText()  [extension]
sb.@length  ->  java.lang.StringBuilder#getLength()  [extension]
{noformat}

For an assignment, static compilation then writes a direct call to that 
zero-argument method and passes the assigned value as an argument. It fails 
while loading arguments:

{noformat}
java.lang.ArrayIndexOutOfBoundsException
    at 
org.codehaus.groovy.classgen.asm.OperandStack.doConvertAndCast(OperandStack.java:378)
    at 
org.codehaus.groovy.classgen.asm.OperandStack.doGroovyCast(OperandStack.java:331)
    at 
org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.visitArgument(StaticInvocationWriter.java:579)
    at 
org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.loadArguments(StaticInvocationWriter.java:522)
    at 
org.codehaus.groovy.classgen.asm.sc.StaticInvocationWriter.writeDirectMethodCall(StaticInvocationWriter.java:335)
    ...
    at 
org.codehaus.groovy.classgen.asm.BinaryExpressionHelper.evaluateEqual(BinaryExpressionHelper.java:665)
{noformat}

Attribute access is field access, so an accessor method, extension or 
otherwise, should never satisfy it.

h2. Suggested fix

When resolving an {{AttributeExpression}}, the type checker should not fall 
back to extension accessors. With no field, it should report {{No such 
attribute: <name> for class: <type>}}, as it already does when there is neither 
a field nor an extension getter. That turns this crash into a compile error 
under {{@CompileStatic}}, and the run-time {{MissingFieldException}} under 
{{@TypeChecked}} into a compile error too.

The read side of the same resolution is tracked in GROOVY-XXXXX: {{sb.@length}} 
compiles under {{@CompileStatic}} and calls {{getLength()}}, while dynamic code 
throws {{MissingFieldException}}. A fix in the type checker would likely cover 
both.

h2. Tests

{{bugs.Groovy12524.testAttributeCompoundAssignOfExtensionPropertyIsRejected}} 
passes today only because of this crash. Once fixed, it should assert the "No 
such attribute" message.



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

Reply via email to