[
https://issues.apache.org/jira/browse/GROOVY-12631?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Paul King updated GROOVY-12631:
-------------------------------
Description:
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-12632: {{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.
was:
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.
> 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
> Priority: Major
>
> 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-12632:
> {{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)