From a3bf3f136caaefa98268607a3529b7554df5fc80 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 1 Dec 2009 18:28:55 +0000 Subject: [This patch submitted by ismael juma - commit m... [This patch submitted by ismael juma - commit message his words, but condensed.] Fix ticket #1600: Serialization and deserialization of hash-based collections should not re-use hashCode. The collection is rebuilt on deserialization - note that this is not compatible with the previous serialization format. All @SerialVersionUIDs have been reset to 1. WeakHashMap is not Serializable and should not be so. TreeHashMap has not been reintegrated yet. OpenHashMap has not been updated. (I think this collection is flawed and should be removed or reimplemented.) --- test/files/jvm/t1600.scala | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 test/files/jvm/t1600.scala (limited to 'test/files/jvm/t1600.scala') diff --git a/test/files/jvm/t1600.scala b/test/files/jvm/t1600.scala new file mode 100644 index 0000000000..1cdcee8547 --- /dev/null +++ b/test/files/jvm/t1600.scala @@ -0,0 +1,76 @@ + +/** + * Checks that serialization of hash-based collections works correctly if the hashCode + * changes on deserialization. + */ +object Test { + + import collection._ + def main(args: Array[String]) { + for (i <- Seq(0, 1, 2, 10, 100)) { + def entries = (0 until i).map(i => (new Foo, i)).toList + def elements = entries.map(_._1) + + val maps = Seq[Map[Foo, Int]](new mutable.HashMap, new mutable.LinkedHashMap, + new immutable.HashMap).map(_ ++ entries) + test[Map[Foo, Int]](maps, entries.size, assertMap _) + + val sets = Seq[Set[Foo]](new mutable.HashSet, new mutable.LinkedHashSet, + new immutable.HashSet).map(_ ++ elements) + test[Set[Foo]](sets, entries.size, assertSet _) + } + } + + private def test[A <: AnyRef](collections: Seq[A], expectedSize: Int, assertFunction: (A, Int) => Unit) { + for (collection <- collections) { + assertFunction(collection, expectedSize) + + val bytes = toBytes(collection) + Foo.hashCodeModifier = 1 + val deserializedCollection = toObject[A](bytes) + + assertFunction(deserializedCollection, expectedSize) + assert(deserializedCollection.getClass == collection.getClass, + "collection class should remain the same after deserialization") + Foo.hashCodeModifier = 0 + } + } + + private def toObject[A](bytes: Array[Byte]): A = { + val in = new java.io.ObjectInputStream(new java.io.ByteArrayInputStream(bytes)) + in.readObject.asInstanceOf[A] + } + + private def toBytes(o: AnyRef): Array[Byte] = { + val bos = new java.io.ByteArrayOutputStream + val out = new java.io.ObjectOutputStream(bos) + out.writeObject(o) + out.close + bos.toByteArray + } + + private def assertMap[A, B](map: Map[A, B], expectedSize: Int) { + assert(expectedSize == map.size, "expected map size: " + expectedSize + ", actual size: " + map.size) + map.foreach { case (k, v) => + assert(map.contains(k), "contains should return true for key in the map, key: " + k) + assert(map(k) == v) + } + } + + private def assertSet[A](set: Set[A], expectedSize: Int) { + assert(expectedSize == set.size, "expected set size: " + expectedSize + ", actual size: " + set.size) + set.foreach { e => assert(set.contains(e), "contains should return true for element in the set, element: " + e) } + } + + object Foo { + /* Used to simulate a hashCode change caused by deserializing an instance with an + * identity-based hashCode in another JVM. + */ + var hashCodeModifier = 0 + } + + @serializable + class Foo { + override def hashCode = System.identityHashCode(this) + Foo.hashCodeModifier + } +} -- cgit v1.2.3