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

Eric Milles commented on GROOVY-11367:
--------------------------------------

There is a subtle case where {{GroovyObject}} is not yet applied (see 
GROOVY-10540) so "metaClass" field, getter, and setter are not available the 
same as for a binary reference.  Creating a virtual field in {{existsProperty}} 
(see below) prevents the loop(s) from searching interfaces for {{getMetaClass}} 
(see GROOVY-11381).

{code:java}
                FieldNode field = current.getDeclaredField(propertyName);
                if (field == null && propertyName.equals("metaClass") // 
GROOVY-TODO
                        && (current.getCompileUnit() != null && 
!current.isInterface())
                        && 
!Optional.ofNullable(current.getField("metaClass")).filter(f -> 
!f.isPrivate()).isPresent()) {
                    field = new FieldNode("metaClass", Opcodes.ACC_PRIVATE | 
Opcodes.ACC_SYNTHETIC | Opcodes.ACC_TRANSIENT, METACLASS_TYPE, current, null);
                    field.setDeclaringClass(current);
                    field.setSynthetic(true);
                }
                field = allowStaticAccessToMember(field, staticOnly);
{code}

> 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.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 6954)
>   map.pro  = null // entry (changed in 6954)
>   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-662, GROOVY-5001, GROOVY-5491, GROOVY-5517, GROOVY-5985, GROOVY-6144, 
> GROOVY-6277, GROOVY-6954, GROOVY-8065, GROOVY-8074, GROOVY-8555, GROOVY-9127, 
> GROOVY-11319, GROOVY-11223, GROOVY-11368, GROOVY-11369, GROOVY-11370, 
> GROOVY-11376



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

Reply via email to