Skip to content
This repository was archived by the owner on Feb 20, 2019. It is now read-only.

Fix issue in runtime picklers with singleton objects #170

Open
wants to merge 2 commits into
base: 0.9.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion core/src/main/scala/pickling/FastTags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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 {
Expand Down
31 changes: 26 additions & 5 deletions core/src/main/scala/pickling/Pickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This addition should be discussed, because it is currently not uniform with genPickler.

} else {
val runtime = new InterpretedUnpicklerRuntime(mirror, tag)
runtime.genUnpickler
Expand Down
1 change: 0 additions & 1 deletion core/src/main/scala/pickling/Runtime.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 30 additions & 0 deletions core/src/test/scala/pickling/run/runtime-spec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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(",")
}
}
}