jaxp/src/com/sun/org/apache/bcel/internal/generic/LocalVariableGen.java
changeset 17538 d8d911c4e5d4
parent 12457 c348e06f0e82
--- a/jaxp/src/com/sun/org/apache/bcel/internal/generic/LocalVariableGen.java	Fri May 10 09:23:22 2013 -0700
+++ b/jaxp/src/com/sun/org/apache/bcel/internal/generic/LocalVariableGen.java	Fri May 17 10:40:21 2013 +0200
@@ -60,6 +60,7 @@
 
 import com.sun.org.apache.bcel.internal.Constants;
 import com.sun.org.apache.bcel.internal.classfile.*;
+import java.util.Objects;
 
 /**
  * This class represents a local variable within a method. It contains its
@@ -75,7 +76,7 @@
   implements InstructionTargeter, NamedAndTyped, Cloneable,
              java.io.Serializable
 {
-  private int         index;
+  private final int   index;
   private String      name;
   private Type        type;
   private InstructionHandle start, end;
@@ -131,30 +132,96 @@
                              signature_index, index, cp.getConstantPool());
   }
 
-  public void        setIndex(int index)           { this.index = index; }
-  public int         getIndex()                   { return index; }
+  public int         getIndex()                  { return index; }
+  @Override
   public void        setName(String name)        { this.name = name; }
+  @Override
   public String      getName()                   { return name; }
+  @Override
   public void        setType(Type type)          { this.type = type; }
+  @Override
   public Type        getType()                   { return type; }
 
   public InstructionHandle getStart()                  { return start; }
   public InstructionHandle getEnd()                    { return end; }
 
-  public void setStart(InstructionHandle start) {
-    BranchInstruction.notifyTarget(this.start, start, this);
-    this.start = start;
+  /**
+   * Remove this from any known HashSet in which it might be registered.
+   */
+  void notifyTargetChanging() {
+    // hashCode depends on 'index', 'start', and 'end'.
+    // Therefore before changing any of these values we
+    // need to unregister 'this' from any HashSet where
+    // this is registered, and then we need to add it
+    // back...
+
+    // Unregister 'this' from the HashSet held by 'start'.
+    BranchInstruction.notifyTargetChanging(this.start, this);
+    if (this.end != this.start) {
+        // Since hashCode() is going to change we need to unregister
+        // 'this' both form 'start' and 'end'.
+        // Unregister 'this' from the HashSet held by 'end'.
+        BranchInstruction.notifyTargetChanging(this.end, this);
+    }
   }
 
-  public void setEnd(InstructionHandle end) {
-    BranchInstruction.notifyTarget(this.end, end, this);
+  /**
+   * Add back 'this' in all HashSet in which it should be registered.
+   **/
+  void notifyTargetChanged() {
+    // hashCode depends on 'index', 'start', and 'end'.
+    // Therefore before changing any of these values we
+    // need to unregister 'this' from any HashSet where
+    // this is registered, and then we need to add it
+    // back...
+
+    // Register 'this' in the HashSet held by start.
+    BranchInstruction.notifyTargetChanged(this.start, this);
+    if (this.end != this.start) {
+        // Since hashCode() has changed we need to register
+        // 'this' again in 'end'.
+        // Add back 'this' in the HashSet held by 'end'.
+        BranchInstruction.notifyTargetChanged(this.end, this);
+    }
+  }
+
+  public final void setStart(InstructionHandle start) {
+
+    // Call notifyTargetChanging *before* modifying this,
+    // as the code triggered by notifyTargetChanging
+    // depends on this pointing to the 'old' start.
+    notifyTargetChanging();
+
+    this.start = start;
+
+    // call notifyTargetChanged *after* modifying this,
+    // as the code triggered by notifyTargetChanged
+    // depends on this pointing to the 'new' start.
+    notifyTargetChanged();
+  }
+
+  public final void setEnd(InstructionHandle end) {
+    // call notifyTargetChanging *before* modifying this,
+    // as the code triggered by notifyTargetChanging
+    // depends on this pointing to the 'old' end.
+    // Unregister 'this' from the HashSet held by the 'old' end.
+    notifyTargetChanging();
+
     this.end = end;
+
+    // call notifyTargetChanged *after* modifying this,
+    // as the code triggered by notifyTargetChanged
+    // depends on this pointing to the 'new' end.
+    // Register 'this' in the HashSet held by the 'new' end.
+    notifyTargetChanged();
+
   }
 
   /**
    * @param old_ih old target, either start or end
    * @param new_ih new target
    */
+  @Override
   public void updateTarget(InstructionHandle old_ih, InstructionHandle new_ih) {
     boolean targeted = false;
 
@@ -176,15 +243,20 @@
   /**
    * @return true, if ih is target of this variable
    */
+  @Override
   public boolean containsTarget(InstructionHandle ih) {
     return (start == ih) || (end == ih);
   }
 
   /**
-   * We consider to local variables to be equal, if the use the same index and
+   * We consider two local variables to be equal, if they use the same index and
    * are valid in the same range.
    */
+  @Override
   public boolean equals(Object o) {
+    if (o==this)
+      return true;
+
     if(!(o instanceof LocalVariableGen))
       return false;
 
@@ -192,10 +264,21 @@
     return (l.index == index) && (l.start == start) && (l.end == end);
   }
 
+  @Override
+  public int hashCode() {
+    int hash = 7;
+    hash = 59 * hash + this.index;
+    hash = 59 * hash + Objects.hashCode(this.start);
+    hash = 59 * hash + Objects.hashCode(this.end);
+    return hash;
+  }
+
+  @Override
   public String toString() {
     return "LocalVariableGen(" + name +  ", " + type +  ", " + start + ", " + end + ")";
   }
 
+  @Override
   public Object clone() {
     try {
       return super.clone();