package tracematches; import java.util.*; public aspect HashMapTest { /** * This version is better in a way that it checks if the modification really changed the hash code * of the collection in question. Also it works for arbitrary objects, not just collections. * BUT it uses let(). */ tracematch(HashMap s, Object o, int hashCode) { sym add before : call(* HashMap.put(Object,*)) && target(s) && args(o,*) && let(hashCode, o.hashCode()); sym remove before : call(* HashMap.remove(Object)) && target(s) && args(o); sym cont before: call(* HashMap.contai*(..)) && target(s) && args(o); //check after each remove and after each "contains" but only if the object was not removed before add cont* remove | add cont { if(hashCode!=o.hashCode()) { if(System.getProperty("TMTEST_ACTIVE")!=null) { System.out.println("HashCode changed for Object "+o+" while being in a HashSet."); } } } } } class RealTest { public static void main(String[] args) { HashMap m = new HashMap(); Collection c = new ArrayList(); Object value = new Object(); //safe but not detectable (need must-point-to info) m.put(c,value); m.remove(c); System.out.println("allowed modification"); c.add("allowed modification"); System.out.println(m.containsKey(c)); c.clear(); //unsafe m.put(c,value); System.out.println("this should trigger an error"); c.add("this should trigger an error"); System.out.println(m.containsKey(c)); //safe Collection c2 = new ArrayList(); m.containsKey(c2); //safe HashMap m2 = new HashMap(); m2.containsKey(c); } }