diff --git a/src/main/java/leon/codegen/runtime/LeonCodeGenRuntimeHashing.java b/src/main/java/leon/codegen/runtime/LeonCodeGenRuntimeHashing.java new file mode 100644 index 0000000000000000000000000000000000000000..5a919af148faf32bffaa6b5bbc3c44d10f3ed72e --- /dev/null +++ b/src/main/java/leon/codegen/runtime/LeonCodeGenRuntimeHashing.java @@ -0,0 +1,43 @@ +package leon.codegen.runtime; + +// MurmurHash3, reproduced from std. Scala lib. +public final class LeonCodeGenRuntimeHashing { + public static final int seqHash(Object[] elements, int seed) { + // I feel good about this line. + int h = avalanche(seed ^ 0xcafebabe); + int sz = elements.length; + if(sz == 0) return h; + for(int i = 0; i < sz; i++) { + h = mix(h, elements[i].hashCode()); + } + return finalizeHash(h, sz); + } + + private static final int mix(int hash, int data) { + int h = mixLast(hash, data); + h = Integer.rotateLeft(h, 13); + return h * 5 + 0xe6546b64; + } + + private static final int mixLast(int hash, int data) { + int k = data; + k *= 0xcc9e2d51; + k = Integer.rotateLeft(k, 15); + k *= 0x1b873593; + return hash ^ k; + } + + private static final int finalizeHash(int hash, int length) { + return avalanche(hash ^ length); + } + + private static final int avalanche(int hash) { + int h = hash; + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + return h; + } +} diff --git a/src/main/java/leon/codegen/runtime/Map.java b/src/main/java/leon/codegen/runtime/Map.java index cd9c2eb6aa428f762193b112640085d177913ee2..514a3e1b46a6c9f5364803bafb40fab3150cea8d 100644 --- a/src/main/java/leon/codegen/runtime/Map.java +++ b/src/main/java/leon/codegen/runtime/Map.java @@ -4,22 +4,22 @@ package leon.codegen.runtime; import java.util.Arrays; import java.util.Iterator; -import java.util.TreeMap; +import java.util.HashMap; /** If someone wants to make it an efficient implementation of immutable * maps, go ahead. */ public final class Map { - private final TreeMap<Object,Object> _underlying; + private final HashMap<Object,Object> _underlying; - protected final TreeMap<Object,Object> underlying() { + protected final HashMap<Object,Object> underlying() { return _underlying; } public Map() { - _underlying = new TreeMap<Object,Object>(); + _underlying = new HashMap<Object,Object>(); } - private Map(TreeMap<Object,Object> u) { + private Map(HashMap<Object,Object> u) { _underlying = u; } @@ -49,7 +49,7 @@ public final class Map { } public Map union(Map s) { - TreeMap<Object,Object> n = new TreeMap<Object,Object>(_underlying); + HashMap<Object,Object> n = new HashMap<Object,Object>(_underlying); n.putAll(s.underlying()); return new Map(n); } diff --git a/src/main/java/leon/codegen/runtime/Set.java b/src/main/java/leon/codegen/runtime/Set.java index f271679532f17817a347cabd4381f169f06d00cb..2bd466af8f8b437507c7d6e0c333dd13721e631f 100644 --- a/src/main/java/leon/codegen/runtime/Set.java +++ b/src/main/java/leon/codegen/runtime/Set.java @@ -4,23 +4,23 @@ package leon.codegen.runtime; import java.util.Arrays; import java.util.Iterator; -import java.util.TreeSet; +import java.util.HashSet; /** If someone wants to make it an efficient implementation of immutable * sets, go ahead. */ public final class Set { - private final TreeSet<Object> _underlying; + private final HashSet<Object> _underlying; - protected final TreeSet<Object> underlying() { + protected final HashSet<Object> underlying() { return _underlying; } public Set() { - _underlying = new TreeSet<Object>(); + _underlying = new HashSet<Object>(); } public Set(Object[] elements) { - _underlying = new TreeSet<Object>(Arrays.asList(elements)); + _underlying = new HashSet<Object>(Arrays.asList(elements)); } // Uses mutation! Useful at building time. @@ -32,7 +32,7 @@ public final class Set { return _underlying.iterator(); } - private Set(TreeSet<Object> u) { + private Set(HashSet<Object> u) { _underlying = u; } @@ -54,13 +54,13 @@ public final class Set { } public Set union(Set s) { - TreeSet<Object> n = new TreeSet<Object>(_underlying); + HashSet<Object> n = new HashSet<Object>(_underlying); n.addAll(s.underlying()); return new Set(n); } public Set intersect(Set s) { - TreeSet<Object> n = new TreeSet<Object>(); + HashSet<Object> n = new HashSet<Object>(); for(Object o : _underlying) { if(s.underlying().contains(o)) { n.add(o); @@ -70,7 +70,7 @@ public final class Set { } public Set minus(Set s) { - TreeSet<Object> n = new TreeSet<Object>(_underlying); + HashSet<Object> n = new HashSet<Object>(_underlying); for(Object o : s.underlying()) { n.remove(o); } diff --git a/src/main/java/leon/codegen/runtime/Tuple.java b/src/main/java/leon/codegen/runtime/Tuple.java index 958a2340fc31b6789812adaacc8752b24e6631fb..ea1e0880559ee7084a1f3ff9c7295aed42955958 100644 --- a/src/main/java/leon/codegen/runtime/Tuple.java +++ b/src/main/java/leon/codegen/runtime/Tuple.java @@ -32,8 +32,18 @@ public final class Tuple { Tuple other = (Tuple)that; if(other.getArity() != this.getArity()) return false; for(int i = 0; i < this.getArity(); i++) { - if(other.get(i) != this.get(i)) return false; + if(!other.get(i).equals(this.get(i))) return false; } return true; } + + private int _hash = 0; + @Override + final public int hashCode() { + if(_hash != 0) return _hash; + int seed = (new String("Tuple" + getArity())).hashCode(); + int h = LeonCodeGenRuntimeHashing.seqHash(elements, seed); + _hash = h; + return h; + } } diff --git a/src/main/scala/leon/codegen/CodeGeneration.scala b/src/main/scala/leon/codegen/CodeGeneration.scala index 80d55ab9eef90931627ac82d1fd8ff3dd775899d..5752b140b825bde0da19e4fe059322e00be8f16c 100644 --- a/src/main/scala/leon/codegen/CodeGeneration.scala +++ b/src/main/scala/leon/codegen/CodeGeneration.scala @@ -25,6 +25,7 @@ object CodeGeneration { private val CaseClassClass = "leon/codegen/runtime/CaseClass" private val ErrorClass = "leon/codegen/runtime/LeonCodeGenRuntimeException" private val ImpossibleEvaluationClass = "leon/codegen/runtime/LeonCodeGenEvaluationException" + private val HashingClass = "leon/codegen/runtime/LeonCodeGenRuntimeHashing" def defToJVMName(p : Program, d : Definition) : String = "Leon$CodeGen$" + d.id.uniqueName @@ -645,6 +646,8 @@ object CodeGeneration { // definition of hashcode locally { + val hashFieldName = "$leon$hashCode" + cf.addField("I", hashFieldName).setFlags((FIELD_ACC_PRIVATE).asInstanceOf[U2]) val hmh = cf.addMethod("I", "hashCode", "") hmh.setFlags(( METHOD_ACC_PUBLIC | @@ -652,8 +655,20 @@ object CodeGeneration { ).asInstanceOf[U2]) val hch = hmh.codeHandler - // TODO FIXME. Look at Scala for inspiration. - hch << Ldc(42) << IRETURN + + val wasNotCached = hch.getFreshLabel("wasNotCached") + + hch << ALoad(0) << GetField(cName, hashFieldName, "I") << DUP + hch << IfEq(wasNotCached) + hch << IRETURN + hch << Label(wasNotCached) << POP + hch << ALoad(0) << InvokeVirtual(cName, "productElements", "()[Ljava/lang/Object;") + hch << ALoad(0) << InvokeVirtual(cName, "productName", "()Ljava/lang/String;") + hch << InvokeVirtual("java/lang/String", "hashCode", "()I") + hch << InvokeStatic(HashingClass, "seqHash", "([Ljava/lang/Object;I)I") << DUP + hch << ALoad(0) << SWAP << PutField(cName, hashFieldName, "I") + hch << IRETURN + hch.freeze } diff --git a/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala b/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala index 6cd6d9a88c96915eb1c2c7e90d1ef34209e5dd01..cda418e96a4b5c80f718622365f6253696e8eb7a 100644 --- a/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala +++ b/src/test/scala/leon/test/evaluators/EvaluatorsTests.scala @@ -407,6 +407,28 @@ class EvaluatorsTests extends FunSuite { } } + test("Sets and maps of structures") { + val p = """|object Program { + | case class MyPair(x : Int, y : Boolean) + | + | def buildPairCC(x : Int, y : Boolean) : MyPair = MyPair(x,y) + | def mkSingletonCC(p : MyPair) : Set[MyPair] = Set(p) + | def containsCC(s : Set[MyPair], p : MyPair) : Boolean = s.contains(p) + | + | def buildPairT(x : Int, y : Boolean) : (Int,Boolean) = (x,y) + | def mkSingletonT(p : (Int,Boolean)) : Set[(Int,Boolean)] = Set(p) + | def containsT(s : Set[(Int,Boolean)], p : (Int,Boolean)) : Boolean = s.contains(p) + |}""".stripMargin + + implicit val progs = parseString(p) + val evaluators = prepareEvaluators + + for(e <- evaluators) { + checkComp(e, mkCall("containsCC", mkCall("mkSingletonCC", mkCall("buildPairCC", IL(42), T)), mkCall("buildPairCC", IL(42), T)), T) + checkComp(e, mkCall("containsT", mkCall("mkSingletonT", mkCall("buildPairT", IL(42), T)), mkCall("buildPairT", IL(42), T)), T) + } + } + test("Misc") { val p = """|object Program { | import leon.Utils._