[ 
https://issues.apache.org/jira/browse/GROOVY-11367?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17854521#comment-17854521
 ] 

Eric Milles edited comment on GROOVY-11367 at 6/12/24 9:31 PM:
---------------------------------------------------------------

Step 1 from above -- put map entry not write protected field -- completed.

https://github.com/apache/groovy/commit/9d6b551a76b16536c31302d6463bf7272263dbaa

Also the missed read-only enforcement of public fields or getter-based 
properties has been fixed.  For example, "def p = map.properties" links to 
{{getProperties()}}, so "map.properties = null" now fails instead of performing 
a write-behind.


was (Author: emilles):
Step 1 (from above) -- put map entry not write protected field -- completed.

https://github.com/apache/groovy/commit/9d6b551a76b16536c31302d6463bf7272263dbaa

Also the missed read-only enforcement of public fields or getter-based 
properties has been fixed.  For example, "def p = map.properties" links to 
{{getProperties()}}, so "map.properties = null" now fails instead of performing 
a write-behind.

> property semantics of map-based types
> -------------------------------------
>
>                 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.any  // getter (all visibilities -- fixed in 5001)
>     this.class // entry (dynamic) or isser (static)
>     this.empty // entry or getter (static)
>     this.metaClass // field (dynamic) or getter (static)
>     this.pub = null // field (same for "pro", "pack", "priv" and "prop")
>     this.any = null // setter (all visibilities) or entry for subclass of M 
> (private or package-private other-package subclass -- fixed in 11357)
>     this.empty = null // entry (dynamic) or error (static)
>     this.class = null // entry (dynamic) or error (static)
>     this.metaClass = null // field (dynamic) or setter (static)
>     def that = this
>     that.* // see test1 (except "empty", "class" and "metaClass")
>     that.empty // entry (dynamic) or isser (static)
>     that.class // entry (dynamic) or getter (static)
>     that.metaClass // entry (dynamic) or getter (static)
>   }
> {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 6954) or field (fixed in 11376)
>   map.pro  = null // entry (changed in 6954) or field (fixed in 11376)
>   map.pack = null // entry
>   map.priv = null // entry
>   map.prop = null // property
>   map.any  = null // setter (accessible) or error (inaccessible)
>   map.empty = null // error "Cannot set read-only property: empty" (and 
> "class") -- GROOVY-11369
>   map.metaClass = null // setter via DefaultGroovyMethods#setMetaClass
> }
> {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 (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-662, GROOVY-5001, GROOVY-5491, GROOVY-5517, GROOVY-5985, GROOVY-6144, 
> GROOVY-6277, GROOVY-6954, GROOVY-8065, GROOVY-8074, GROOVY-8265, GROOVY-8555, 
> GROOVY-8978, GROOVY-9127, GROOVY-11144, GROOVY-11319, GROOVY-11223, 
> GROOVY-11357, GROOVY-11360, GROOVY-11368, GROOVY-11369, GROOVY-11370, 
> GROOVY-11376 GROOVY-11384, GROOVY-11386, GROOVY-11387, GROOVY-11390, 
> GROOVY-11393, GROOVY-11401, GROOVY-11402



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to