Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-7390

@EqualsAndHashCode incorrect when using non-field properties

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Critical
    • Resolution: Fixed
    • 2.4.3
    • 2.5.0-beta-3
    • None
    • None

    Description

      I have the following class representing a location in Amazon S3. Depending on some inner business logic, I often need to split the object key into a prefix and a file name, so I access them all through the getKey() method; with S3, the only thing that matters is the final concatenated string.

      The generated equals method incorrectly returns true for any two objects with the same bucket, ignoring the key property. (I discovered this when I got some interesting results out of a JSR-330 cache.) I have marked this issue critical because it is likely to cause immediate security vulnerabilities and data loss when unequal objects are found equal.

      @CompileStatic
      @EqualsAndHashCode(includes = ['bucket', 'key'])
      final class S3ImageLocation implements ImageLocation {
          @NotNull
          final String bucket
      
          final String prefix
      
          @NotNull
          final String subKey
      
          @PersistenceConstructor
          S3ImageLocation(String bucket, String prefix, String subKey) {
              this.bucket = bucket
              this.prefix = prefix
              this.subKey = subKey
          }
      
          S3ImageLocation(String bucket, String subKey) {
              this(bucket, null, subKey)
          }
      
          @JsonIgnore
          String getKey() {
              prefix ? "$prefix/$subKey" : subKey
          }
      
          @Override
          String toString() {
              "s3://$bucket/$key"
          }
      
          S3Location toBlitlineLocation() {
              new S3Location(bucket, key)
          }
      }
      

      Both of the generated methods appear to be including bucket twice instead of including bucket and key.

      see :7 and :29

        public int hashCode();
          Code:
             0: invokestatic  #93                 // Method org/codehaus/groovy/util/HashCodeHelper.initHash:()I
             3: istore_1
             4: iload_1
             5: pop
             6: aload_0
             7: ldc           #94                 // String bucket
             9: invokestatic  #100                // Method org/codehaus/groovy/runtime/InvokerHelper.getProperty:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
            12: aload_0
            13: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
            16: ifne          23
            19: iconst_1
            20: goto          24
            23: iconst_0
            24: ifeq          42
            27: iload_1
            28: aload_0
            29: ldc           #94                 // String bucket
            31: invokestatic  #100                // Method org/codehaus/groovy/runtime/InvokerHelper.getProperty:(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
            34: invokestatic  #110                // Method org/codehaus/groovy/util/HashCodeHelper.updateHash:(ILjava/lang/Object;)I
            37: istore_2
            38: iload_2
            39: istore_1
            40: iload_2
            41: pop
            42: iload_1
            43: ireturn
            44: ldc           #113                // int 0
            46: ireturn
      

      see :152/:171 and :208/:219

        public boolean equals(java.lang.Object);
          Code:
             0: aload_1
             1: ifnonnull     8
             4: iconst_1
             5: goto          9
             8: iconst_0
             9: ifeq          19
            12: getstatic     #129                // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
            15: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
            18: ireturn
            19: aload_0
            20: aload_1
            21: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
            24: ifeq          34
            27: getstatic     #132                // Field java/lang/Boolean.TRUE:Ljava/lang/Boolean;
            30: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
            33: ireturn
            34: aload_1
            35: instanceof    #2                  // class com/artsquare/studio/img/s3/S3ImageLocation
            38: ifne          45
            41: iconst_1
            42: goto          46
            45: iconst_0
            46: ifeq          56
            49: getstatic     #129                // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
            52: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
            55: ireturn
            56: aload_1
            57: ldc           #2                  // class com/artsquare/studio/img/s3/S3ImageLocation
            59: invokestatic  #138                // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
            62: checkcast     #2                  // class com/artsquare/studio/img/s3/S3ImageLocation
            65: astore_2
            66: aload_2
            67: pop
            68: aload_2
            69: aload_0
            70: invokevirtual #140                // Method canEqual:(Ljava/lang/Object;)Z
            73: ifne          80
            76: iconst_1
            77: goto          81
            80: iconst_0
            81: ifeq          91
            84: getstatic     #129                // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
            87: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
            90: ireturn
            91: aload_0
            92: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
            95: aload_2
            96: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
            99: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
           102: ifne          109
           105: iconst_1
           106: goto          110
           109: iconst_0
           110: ifeq          274
           113: aload_0
           114: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           117: aload_0
           118: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
           121: ifeq          147
           124: aload_2
           125: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           128: aload_2
           129: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
           132: ifne          139
           135: iconst_1
           136: goto          140
           139: iconst_0
           140: ifeq          147
           143: iconst_1
           144: goto          148
           147: iconst_0
           148: ifne          189
           151: aload_0
           152: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           155: aload_0
           156: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
           159: ifne          166
           162: iconst_1
           163: goto          167
           166: iconst_0
           167: ifeq          185
           170: aload_2
           171: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           174: aload_2
           175: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
           178: ifeq          185
           181: iconst_1
           182: goto          186
           185: iconst_0
           186: ifeq          193
           189: iconst_1
           190: goto          194
           193: iconst_0
           194: ifeq          207
           197: getstatic     #129                // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
           200: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
           203: ireturn
           204: goto          274
           207: aload_0
           208: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           211: aload_0
           212: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
           215: ifeq          233
           218: aload_2
           219: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           222: aload_2
           223: invokestatic  #106                // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.is:(Ljava/lang/Object;Ljava/lang/Object;)Z
           226: ifeq          233
           229: iconst_1
           230: goto          234
           233: iconst_0
           234: ifne          241
           237: iconst_1
           238: goto          242
           241: iconst_0
           242: ifeq          274
           245: aload_0
           246: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           249: aload_2
           250: invokevirtual #143                // Method getBucket:()Ljava/lang/String;
           253: invokestatic  #146                // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.compareEqual:(Ljava/lang/Object;Ljava/lang/Object;)Z
           256: ifne          263
           259: iconst_1
           260: goto          264
           263: iconst_0
           264: ifeq          274
           267: getstatic     #129                // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
           270: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
           273: ireturn
           274: getstatic     #132                // Field java/lang/Boolean.TRUE:Ljava/lang/Boolean;
           277: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
           280: ireturn
           281: ldc           #113                // int 0
           283: invokestatic  #122                // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
           286: invokestatic  #58                 // Method org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.booleanUnbox:(Ljava/lang/Object;)Z
           289: ireturn
      

      Attachments

        Issue Links

          Activity

            People

              paulk Paul King
              chrylis Christopher Smith
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: