[
https://issues.apache.org/jira/browse/GROOVY-12671?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18137445#comment-18137445
]
ASF GitHub Bot commented on GROOVY-12671:
-----------------------------------------
codeconsole opened a new pull request, #3192:
URL: https://github.com/apache/groovy/pull/3192
An implicit-this property read or write in a closure with resolve strategy
`OWNER_FIRST` and a null owner never reaches the delegate. It throws
`NullPointerException: Cannot get property 'bar' on null object`:
```groovy
class D { def bar = 'delegate' }
def c = { -> bar }.dehydrate()
c.delegate = new D()
assert c() == 'delegate' // NPE
```
`Closure.resolveGetProperty` and `resolveSetProperty` skipped a null
receiver only for `DELEGATE_FIRST` with a null delegate (GROOVY-1974). Every
other null owner or delegate went to `NullObject`. The same NPE replaced
`MissingPropertyException` when both were null, and when an `OWNER_ONLY` owner
or a `DELEGATE_ONLY` delegate was null. In addition, `setPropertyTryThese`
guarded its fallback with `o1 != null` instead of `o2 != null`, so writing a
property the first candidate lacks went on to set it on a null second
candidate. Seen in every release back to 2.4.15.
The private `getProperty`/`setProperty` helpers now throw
`MissingPropertyException` for a null receiver, and the `*TryThese` helpers
move straight to the second candidate when the first is null. The
`DELEGATE_FIRST` special case is covered by the general rule and is removed.
Property resolution now matches method dispatch as fixed by #3188
(GROOVY-12667): a null owner or delegate is skipped, and if nothing is left, a
missing-member exception (naming the closure class) is thrown. The indy and
classic call-site paths both go through these methods. Code that caught the NPE
for an `OWNER_ONLY`/`DELEGATE_ONLY` closure with a null target now sees
`MissingPropertyException`.
This touches `Closure` only; #3188 touches `ClosureMetaClass`, and the two
merge cleanly.
## Tests
`ClosureTest` gains `testOwnerFirstWithNullOwnerProperty` (read, write,
compound assignment), `testOwnerFirstWithNullOwnerPropertyTypeChecked`,
`testDehydratedClosureWithDelegateProperty`,
`testMissingPropertyWithNullOwnerAndDelegate`,
`testMissingPropertyWithNullOnlyCandidate` and
`testMissingPropertyWithNullSecondCandidate`. All 11 cases fail without the
change. `groovy.lang.*Closure*`, `groovy.*Closure*`, `gls.invocation.*`,
`org.codehaus.groovy.runtime.*`, `groovy.transform.stc.*`,
`org.codehaus.groovy.classgen.asm.sc.*` and `groovy.ThisAndSuperTest` pass. The
Checkstyle and CodeNarc gates pass.
> Closure property access throws NullPointerException instead of falling
> through to the delegate when the owner is null
> ---------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12671
> URL: https://issues.apache.org/jira/browse/GROOVY-12671
> Project: Groovy
> Issue Type: Bug
> Affects Versions: 4.0.33, 5.0.8, 7.0.0-alpha-1, 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> {code:groovy}
> class D { def bar = 'delegate' }
> def c = { -> bar }.dehydrate()
> c.delegate = new D()
> assert c() == 'delegate'
> {code}
> *Actual:* {{java.lang.NullPointerException: Cannot get property 'bar' on null
> object}}. A write ({{bar = 1}}) throws {{Cannot set property 'bar' on null
> object}}, and so does {{rehydrate(d, null, null)}}.
> *Expected:* with OWNER_FIRST (the default) a null owner is skipped and the
> delegate supplies the property, the same as for method calls (GROOVY-12667).
> When no non-null candidate remains (both null, or a null target under
> OWNER_ONLY or DELEGATE_ONLY), MissingPropertyException is thrown, matching
> the MissingMethodException that method calls give.
> {{Closure.resolveGetProperty}} and {{resolveSetProperty}} skip a null
> receiver only for DELEGATE_FIRST with a null delegate (GROOVY-1974). Every
> other null receiver reaches {{NullObject}}. Separately,
> {{setPropertyTryThese}} tests {{o1 != null}} where it should test {{o2 !=
> null}}, so a missing property on the owner with a null delegate also throws
> NPE instead of MissingPropertyException.
> Verified on 2.4.15, 3.0.25, 4.0.33, 5.0.8, 6.0.0 and master.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)