diff --git a/core/src/main/scala/pickling/FastTags.scala b/core/src/main/scala/pickling/FastTags.scala index 3eba4cfd9b..ccbd4d0895 100644 --- a/core/src/main/scala/pickling/FastTags.scala +++ b/core/src/main/scala/pickling/FastTags.scala @@ -67,6 +67,7 @@ object FastTypeTag { } def apply(mirror: ru.Mirror, key: String): FastTypeTag[_] = apply(mirror, typeFromString(mirror, key), key) + def apply(key: String): FastTypeTag[_] = macro Compat.FastTypeTagMacros_apply def valueTypeName(tag: FastTypeTag[_]): String = { @@ -138,7 +139,12 @@ object FastTypeTag { val key = "scala.Array[" + elemClass.getName + "]" apply(mirror, tpe, key) } else { - apply(mirror, clazz.getName()) + val className = clazz.getName() + val key = if (className.endsWith("$")) + className.substring(0, className.length-1) + ".type" + else + className + apply(mirror, key) } }) } catch { diff --git a/core/src/main/scala/pickling/Pickler.scala b/core/src/main/scala/pickling/Pickler.scala index 41b21ca862..b30406c868 100644 --- a/core/src/main/scala/pickling/Pickler.scala +++ b/core/src/main/scala/pickling/Pickler.scala @@ -45,9 +45,8 @@ object DPickler { trait GenPicklers extends CorePicklersUnpicklers { implicit def genPickler[T](implicit format: PickleFormat): SPickler[T] = macro Compat.PicklerMacros_impl[T] - // TODO: the primitive pickler hack employed here is funny, but I think we should fix this one - // since people probably would also have to deal with the necessity to abstract over pickle formats - def genPickler(classLoader: ClassLoader, clazz: Class[_], tag: FastTypeTag[_])(implicit format: PickleFormat, share: refs.Share): SPickler[_] = { + + def genPickler(classLoader: ClassLoader, clazz: Class[_], tag: FastTypeTag[_])(implicit pf: PickleFormat, share: refs.Share): SPickler[_] = { // println(s"generating runtime pickler for $clazz") // NOTE: needs to be an explicit println, so that we don't occasionally fallback to runtime in static cases val className = if (clazz == null) "null" else clazz.getName GlobalRegistry.picklerMap.get(className) match { @@ -60,6 +59,16 @@ trait GenPicklers extends CorePicklersUnpicklers { val elemPickler = genPickler(classLoader, elemClass, elemTag) mkRuntimeTravPickler[Array[AnyRef]](mirror, elemClass, elemTag, tag, elemPickler, null) + } else if (className.endsWith("$")) { + // Return an SPickler[_] + // Note: creating a RuntimePickler has too much overhead in this case + new SPickler[Any] { + val format: PickleFormat = pf + def pickle(picklee: Any, builder: PBuilder): Unit = { + builder.beginEntry(picklee) + builder.endEntry() + } + } } else { val runtime = new RuntimePickler(classLoader, clazz) runtime.mkPickler @@ -86,13 +95,15 @@ trait Unpickler[T] { } trait GenUnpicklers extends CorePicklersUnpicklers { + implicit def genUnpickler[T](implicit format: PickleFormat): Unpickler[T] = macro Compat.UnpicklerMacros_impl[T] - def genUnpickler(mirror: Mirror, tag: FastTypeTag[_])(implicit format: PickleFormat, share: refs.Share): Unpickler[_] = { + + def genUnpickler(mirror: Mirror, tag: FastTypeTag[_])(implicit pf: PickleFormat, share: refs.Share): Unpickler[_] = { // println(s"generating runtime unpickler for ${tag.key}") // NOTE: needs to be an explicit println, so that we don't occasionally fallback to runtime in static cases val className = tag.key GlobalRegistry.unpicklerMap.get(className) match { case None => - // debug(s"!!! could not find registered pickler for class $className !!!") + // debug(s"!!! could not find registered unpickler for class $className !!!") val unpickler = if (className.startsWith("scala.Array")) { // debug(s"runtime unpickling of an array: $className") val len = className.length @@ -103,6 +114,16 @@ trait GenUnpicklers extends CorePicklersUnpicklers { val elemUnpickler = Unpickler.genUnpickler(mirror, elemTag) mkRuntimeTravPickler[Array[AnyRef]](mirror, elemClass, elemTag, tag, null, elemUnpickler) + } else if (className.endsWith(".type")) { + // Return an Unpickler[_] + // Note: creating an InterpretedUnpicklerRuntime has too much overhead in this case + new Unpickler[Any] { + val format: PickleFormat = pf + val moduleSym = mirror.staticModule(className.stripSuffix(".type")) + val moduleMirror = mirror.reflectModule(moduleSym) + def unpickle(tagDontAccess: => FastTypeTag[_], reader: PReader): Any = + moduleMirror.instance + } } else { val runtime = new InterpretedUnpicklerRuntime(mirror, tag) runtime.genUnpickler diff --git a/core/src/main/scala/pickling/Runtime.scala b/core/src/main/scala/pickling/Runtime.scala index 5eb74ed0c1..32eb84fbd4 100644 --- a/core/src/main/scala/pickling/Runtime.scala +++ b/core/src/main/scala/pickling/Runtime.scala @@ -159,7 +159,6 @@ class InterpretedUnpicklerRuntime(mirror: Mirror, tag: FastTypeTag[_])(implicit import scala.reflect.runtime.{universe => ru} val tpe = tag.tpe - val sym = tpe.typeSymbol.asType // debug("UnpicklerRuntime: tpe = " + tpe) val clazz = mirror.runtimeClass(tpe.erasure) val irs = new IRs[ru.type](ru) diff --git a/core/src/test/scala/pickling/run/runtime-spec.scala b/core/src/test/scala/pickling/run/runtime-spec.scala index 6a2dd21d35..8b38f3cd7e 100644 --- a/core/src/test/scala/pickling/run/runtime-spec.scala +++ b/core/src/test/scala/pickling/run/runtime-spec.scala @@ -75,6 +75,10 @@ object RuntimeJsonSpec extends Properties("runtime-json") { roundTrip(p) } + property("Option[Int]") = Prop forAll { (x: Option[Int]) => + roundTrip(x) + } + property("C") = Prop forAll { (i: Int) => roundTrip(C(i)) } @@ -102,6 +106,17 @@ object RuntimeJsonSpec extends Properties("runtime-json") { val t2 = up.asInstanceOf[(Int, Array[Double])] t._1 == t2._1 && t._2.mkString(",") == t2._2.mkString(",") } + + property("Array[(Int, Array[Double])]") = Prop forAll { (t: Array[(Int, Array[Double])]) => + val obj: Any = t + val p = obj.pickle + val up = p.unpickle[Any] + val t2 = up.asInstanceOf[Array[(Int, Array[Double])]] + t2.zipWithIndex.forall { case (pair, index) => + val otherPair = t(index) + otherPair._1 == pair._1 && otherPair._2.mkString(",") == pair._2.mkString(",") + } + } } object RuntimeBinarySpec extends Properties("runtime-binary") { @@ -168,6 +183,10 @@ object RuntimeBinarySpec extends Properties("runtime-binary") { roundTrip(p) } + property("Option[Int]") = Prop forAll { (x: Option[Int]) => + roundTrip(x) + } + property("C") = Prop forAll { (i: Int) => roundTrip(C(i)) } @@ -195,4 +214,15 @@ object RuntimeBinarySpec extends Properties("runtime-binary") { val t2 = up.asInstanceOf[(Int, Array[Double])] t._1 == t2._1 && t._2.mkString(",") == t2._2.mkString(",") } + + property("Array[(Int, Array[Double])]") = Prop forAll { (t: Array[(Int, Array[Double])]) => + val obj: Any = t + val p = obj.pickle + val up = p.unpickle[Any] + val t2 = up.asInstanceOf[Array[(Int, Array[Double])]] + t2.zipWithIndex.forall { case (pair, index) => + val otherPair = t(index) + otherPair._1 == pair._1 && otherPair._2.mkString(",") == pair._2.mkString(",") + } + } }