[ https://issues.apache.org/jira/browse/GROOVY-11367?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17843569#comment-17843569 ]
Eric Milles edited comment on GROOVY-11367 at 5/8/24 3:43 PM: -------------------------------------------------------------- The first changes to improve consistency I suggest are: # read and write of protected field: write changes entry for non-{{this}} case -- one can always use {{map.@pro}} to reference protected field # read of non-public fields under static compilation: map entry gains precedence -- as it was before GROOVY-5001 / GROOVY-5491 change # read and write of package-private and private methods: access checks determine field or entry for read, as they do for write -- 5001 made getter pervasive # read and write of {{metaClass}}: remove special case for "metaClass" read being entry With these, outside read and write would be aligned along public / non-public visibility. And dynamic and static compilation would be quite close, with "class" and "empty" as outliers. was (Author: emilles): The first changes to improve consistency I suggest are: # read and write of protected field: write changes entry for non-{{this}} case -- one can always use {{map.@pro}} to reference protected field # read of non-public fields under static compilation: map entry gains precedence -- as it was before GROOVY-5001 / GROOVY-5049 change # read and write of package-private and private methods: access checks determine field or entry for read, as they do for write -- 5001 made getter pervasive # read and write of {{metaClass}}: remove special case for "metaClass" read being entry With these, outside read and write would be aligned along public / non-public visibility. And dynamic and static compilation would be quite close, with "class" and "empty" as outliers. > map-based types property semantics > ---------------------------------- > > Key: GROOVY-11367 > URL: https://issues.apache.org/jira/browse/GROOVY-11367 > Project: Groovy > Issue Type: Bug > Reporter: Eric Milles > Assignee: Eric Milles > Priority: Major > Labels: breaking > > The rules for property read and write for map-based types still have several > inconsistencies: > {code:groovy} > class M extends HashMap<String,Object> { > public pub > protected pro > @PackageScope pack > private priv > def prop > def getAny() {} > void setAny(value) {} > } > {code} > # Read and write do not share a consistent resolution order – noted in 8555. > Should access method selection take visibility into account? I think sender > information is not always available to {{MetaClassImpl}} so the "inside > class" behavior is required. > {code:groovy} > void test1(M map) { > map.pub // field (fixed in 5001) > map.pro // entry > map.pack // entry > map.priv // entry > map.prop // property (fixed in 5001) > map.any // getter (all visibilities -- fixed in 5001) or entry for > subclass of M (private or package-private other-package subclass -- fixed in > 11357) > map.empty // entry ("class" and "metaClass" as well) > map.pub = null // field > map.pro = null // field > map.pack = null // entry > map.priv = null // entry > map.prop = null // property > map.any = null // setter (all visibilities) or entry for subclass of M > (private or package-private other-package subclass -- fixed in 11357) > map.empty = null // entry > map.class = null // entry > map.metaClass = null // setter via > ScriptBytecodeAdapter#setGroovyObjectProperty > } > {code} > # "this" and "super" have different behavior. > {code:groovy} > // add this to the body of M > void test2(M that) { > this.pub // field > this.pro // field > this.pack // field > this.priv // field > this.prop // property > this.prop = null // property > this.any // getter (all visibilities -- fixed in 5001) > this.any = null // setter (all visibilities) or entry for subclass of M > (private or package-private other-package subclass -- fixed in 11357) > this.class // entry > this.empty // entry > this.metaClass // field (added by compiler) > that.* // see test1 > that = this > that.* // see test1 > } > {code} > # Dynamic and static compilation have different behavior. > {code:groovy} > @groovy.transform.CompileStatic > void test3(M map) { > map.pub // field (changed in 5001/5491) > map.pro // field (changed in 5001/5491: in package or subclass) or entry > map.pack // field (changed in 5001/5491: in package) or entry > map.priv // entry > map.prop // property (changed in 5001/5491) > map.any // getter (accessible -- changed in 5001/5491) or entry > (inaccessible) > map.empty // isser (changed in 5001/5491) > map.class // getter (changed in 5001/5491) > map.metaClass // getter (changed in 5001/5491) > map.pub = null // entry (changed in 6549) > map.pro = null // entry (changed in 6549) > map.pack = null // entry > map.priv = null // entry > map.prop = null // property > map.any = null // setter (accessible) or error (inaccessible) -- TODO > map.empty = null // error "Cannot set read-only property: empty" (and > "class") -- GROOVY-11369 > map.metaClass = null // field or setter (need to determine which) > } > {code} > # Closures intercept some properties. > {code:groovy} > void test4(M map) { > map.with { > pub // field > pro // entry > pack // entry > priv // entry > prop // property > directive // closure property > metaClass // closure property (and so on for "owner", "delegate", > "thisObject", ...) > } > } > {code} > # The rules change a bit for fields declared by super class. > {code:groovy} > class MM extends M { > void test5() { > this.pub // field (fixed in 5001) > this.pro // entry > this.pack // entry > this.priv // entry > this.prop // property (fixed in 5001) > this.any // getter (fixed in 5001) (public, protected, package-private > if same-package) or entry (private, package-private if other-package) > this.class // entry > this.empty // entry > this.metaClass // entry > this.pub = null // field > this.pro = null // field > this.pack = null // entry > this.priv = null // entry > this.prop = null // property > this.any = null // setter (public, protected, package-private if > same-package) or entry (private, package-private if other-package -- fixed in > 11357) > this.empty = null // entry > this.class = null // entry > this.metaClass = null // setter via > ScriptBytecodeAdapter#setGroovyObjectProperty > } > } > {code} > # Calling a name bypasses map lookup. > {code:groovy} > void test6(M map) { > map.pack() // field read and call > map.priv() // field read and call > } > {code} > GROOVY-11369, GROOVY-11368, GROOVY-11223, GROOVY-11319, GROOVY-9127, > GROOVY-8555, GROOVY-8065, GROOVY-6954, GROOVY-6277, GROOVY-6144, GROOVY-5985, > GROOVY-5491, GROOVY-5001, GROOVY-662 -- This message was sent by Atlassian Jira (v8.20.10#820010)