Skip to content

Commit

Permalink
Add big improvement in the implicit search
Browse files Browse the repository at this point in the history
* Change all the implicit definitions to return
  `AbstractPicklerUnpickler` instead of `Pickler[T] with Unpickler[T]`.
  The former one has more precedence than the latter one. This avoids to
  call the generator macro EVERY TIME we ask for an implicit
  pickler/unpickler.

* Make sure that the `LowPriorityImplicits` are placed in the right
  location of the `Defaults` trait.

* This addresses a big deal of scala#422 although more changes will come,
  especially affecting `preferringAlternativeImplicits`.

* Do some minor cleanup and formatting changes (trying out scalafmt) in
  the project.

* Remove explicit registerings of picklers that were not necessary
  because of `AutoRegister`.

* Register myself to the TODOs that are concerned with the creation of
  runtime picklers/unpicklers. @jsuereth, I've stolen you some TODOs ;)
  • Loading branch information
jvican committed May 23, 2016
1 parent a1a9f4d commit f38f027
Show file tree
Hide file tree
Showing 22 changed files with 271 additions and 204 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ import scala.pickling.Defaults.{ pickleOps, unpickleOps }
// import scala.pickling.functions._

// Import picklers for specific types
import scala.pickling.Defaults.{ stringPickler, intPickler, refUnpickler, nullPickler }
import scala.pickling.Defaults.{ stringPickler, intPickler, refPicklerUnpickler, nullPickler }

case class Pumpkin(kind: String)
// Manually generate a pickler using macro
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package scala.pickling
package pickler

/** All pickler instances.
*/
trait AllPicklers extends PrimitivePicklers
/** All pickler instances, including the low priority implicits. */
trait AllPicklers extends LowPriorityPicklers
with PrimitivePicklers
with DatePicklers
with JavaBigDecimalPicklers
with JavaBigIntegerPicklers
Expand All @@ -15,11 +15,8 @@ trait AllPicklers extends PrimitivePicklers
with CollectionPicklers {}
object AllPicklers extends AllPicklers {}

/** All picklers for collections with exception of List which is handled by macro.
*
* These need to be between the big picklers and the gen picklers.
*/
trait CollectionPicklers extends AllGenPicklers with MutableMapPicklers
/** All collection picklers except [[List]] which is handled by a macro. */
trait CollectionPicklers extends MutableMapPicklers
with ImmutableSortedMapPicklers
with MapPicklers
with MutableSortedSetPicklers
Expand All @@ -34,9 +31,5 @@ trait CollectionPicklers extends AllGenPicklers with MutableMapPicklers
with SeqPicklers
with IterablePicklers {}

// Gen picklers need to be BELOW the collection implicits so that we can use the collection ones.
trait AllGenPicklers extends LowPriorityPicklers
with GenPicklersUnpicklers {}

// We force any to be the last pickler.
trait LowPriorityPicklers extends AnyUnpicklers {}
// Force `Any` to be the last pickler to be picked in the implicit search
trait LowPriorityPicklers extends GenPicklersUnpicklers with AnyUnpicklers {}
9 changes: 6 additions & 3 deletions core/src/main/scala/scala/pickling/pickler/Array.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ package pickler
import scala.collection.generic.CanBuildFrom

trait ArrayPicklers {
implicit def arrayPickler[T >: Null: FastTypeTag](implicit elemPickler: Pickler[T], elemUnpickler: Unpickler[T],
collTag: FastTypeTag[Array[T]], cbf: CanBuildFrom[Array[T], T, Array[T]]):
Pickler[Array[T]] with Unpickler[Array[T]] = TravPickler[T, Array[T]]
implicit def arrayPickler[T >: Null : FastTypeTag](
implicit elemPickler: Pickler[T],
elemUnpickler: Unpickler[T],
collTag: FastTypeTag[Array[T]],
cbf: CanBuildFrom[Array[T], T, Array[T]]
): AbstractPicklerUnpickler[Array[T]] = TravPickler[T, Array[T]]
}
12 changes: 7 additions & 5 deletions core/src/main/scala/scala/pickling/pickler/ArrayBuffer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import scala.collection.generic.CanBuildFrom
import scala.collection.mutable.ArrayBuffer

trait ArrayBufferPicklers {
// TODO(jsuereth) - Add pickler generator
implicit def arrayBufferPickler[T: FastTypeTag](implicit elemPickler: Pickler[T], elemUnpickler: Unpickler[T],
collTag: FastTypeTag[ArrayBuffer[T]], cbf: CanBuildFrom[ArrayBuffer[T], T, ArrayBuffer[T]]):
Pickler[ArrayBuffer[T]] with Unpickler[ArrayBuffer[T]] =
SeqSetPickler[T, ArrayBuffer]
// TODO(jvican) - Add pickler generator
implicit def arrayBufferPickler[T : FastTypeTag](
implicit elemPickler: Pickler[T],
elemUnpickler: Unpickler[T],
collTag: FastTypeTag[ArrayBuffer[T]],
cbf: CanBuildFrom[ArrayBuffer[T], T, ArrayBuffer[T]]
): AbstractPicklerUnpickler[ArrayBuffer[T]] = SeqSetPickler[T, ArrayBuffer]
}
50 changes: 27 additions & 23 deletions core/src/main/scala/scala/pickling/pickler/Date.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,36 @@ import java.text.SimpleDateFormat

trait DatePicklers extends PrimitivePicklers {
implicit val datePickler: Pickler[Date] with Unpickler[Date] =
new AbstractPicklerUnpickler[Date] with AutoRegister[Date] {
private val dateFormatTemplate = {
val format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") //use ISO_8601 format
format.setLenient(false)
format.setTimeZone(TimeZone.getTimeZone("UTC"))
format
}
private def dateFormat = dateFormatTemplate.clone.asInstanceOf[SimpleDateFormat]
new AbstractPicklerUnpickler[Date] with AutoRegister[Date] {
private val dateFormatTemplate = {
val format =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") //use ISO_8601 format
format.setLenient(false)
format.setTimeZone(TimeZone.getTimeZone("UTC"))
format
}
private def dateFormat =
dateFormatTemplate.clone.asInstanceOf[SimpleDateFormat]

lazy val tag = FastTypeTag[Date]("java.util.Date")
def pickle(picklee: Date, builder: PBuilder): Unit = {
builder.beginEntry(picklee, tag)
lazy val tag = FastTypeTag[Date]("java.util.Date")
def pickle(picklee: Date, builder: PBuilder): Unit = {
builder.beginEntry(picklee, tag)

builder.putField("value", b => {
b.hintElidedType(implicitly[FastTypeTag[String]])
stringPickler.pickle(dateFormat.format(picklee), b)
})
builder.putField(
"value",
b => {
b.hintElidedType(implicitly[FastTypeTag[String]])
stringPickler.pickle(dateFormat.format(picklee), b)
})

builder.endEntry()
}
def unpickle(tag: String, reader: PReader): Any = {
val reader1 = reader.readField("value")
reader1.hintElidedType(implicitly[FastTypeTag[String]])
val result = stringPickler.unpickleEntry(reader1)
dateFormat.parse(result.asInstanceOf[String])
builder.endEntry()
}
def unpickle(tag: String, reader: PReader): Any = {
val reader1 = reader.readField("value")
reader1.hintElidedType(implicitly[FastTypeTag[String]])
val result = stringPickler.unpickleEntry(reader1)
dateFormat.parse(result.asInstanceOf[String])
}
}
}
internal.currentRuntime.picklers.registerPicklerUnpickler(datePickler)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import scala.language.experimental.macros
*/
trait GenPicklersUnpicklers {

def genPickler[T]: AbstractPicklerUnpickler[T] =
implicit def genPickler[T]: AbstractPicklerUnpickler[T] =
macro generator.Compat.genPicklerUnpickler_impl[T]

def genUnpickler[T]: AbstractPicklerUnpickler[T] with Generated =
macro generator.Compat.genPicklerUnpickler_impl[T]

// The only implicit definition, otherwise there's a clash
implicit def genPicklerUnpickler[T]: AbstractPicklerUnpickler[T] with Generated =
def genPicklerUnpickler[T]: AbstractPicklerUnpickler[T] with Generated =
macro generator.Compat.genPicklerUnpickler_impl[T]

}
Expand Down
20 changes: 18 additions & 2 deletions core/src/main/scala/scala/pickling/pickler/GeneratorRegistry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ trait GeneratorHelper {
else query(key).getOrElse(throw error).asInstanceOf[PU[S]]
}


/** Get a pickler from the registry or throw exception otherwise. */
def getPickler[T, S](tpe: FastTypeTag[T], fullTpe: FastTypeTag[S]) =
get[Pickler, T](currentRuntime.picklers.lookupPickler, tpe.key,
Expand All @@ -73,13 +72,30 @@ trait GeneratorHelper {
def specialize[T](tag: FastTypeTag[_]): FastTypeTag[T] =
tag.asInstanceOf[FastTypeTag[T]]

/** Extract one type parameter from a type constructor and
* cast them to a concrete type [[T]].
*
* @tparam T Type we want to convert to
*
* @return A tag holding information about [[T]]
*/
def oneArgumentTagExtractor[T](tpe: FastTypeTag[_]): FastTypeTag[T] = {
val typeArgs = tpe.typeArgs
typeArgs match {
case List(one) => one.asInstanceOf[FastTypeTag[T]]
// TODO: Remove this hack to handle `Nil.type` as a tag
case List() => FastTypeTag.Any.asInstanceOf[FastTypeTag[T]]
case x => throw TypeMismatch(List(tpe), typeArgs)
}
}

/** Extract two type parameters from a type constructor and
* cast them to some concrete types [[T]] and [[S]].
*
* @tparam T First type we want to convert to
* @tparam S Second type we want to convert to
*
* @return A tuple of tags of (T, S)
* @return A tuple of tags of ([[T]], [[S]])
*/
def twoArgumentTagExtractor[T, S](tpe: FastTypeTag[_]): (FastTypeTag[T], FastTypeTag[S]) = {
val typeArgs = tpe.typeArgs
Expand Down
96 changes: 44 additions & 52 deletions core/src/main/scala/scala/pickling/pickler/Iterable.scala
Original file line number Diff line number Diff line change
@@ -1,82 +1,70 @@
package scala.pickling
package pickler

import scala.pickling.PicklingErrors.TypeMismatch
import scala.collection.generic.CanBuildFrom
import scala.language.higherKinds
import scala.pickling.spi.PicklerRegistry.PicklerUnpicklerGen

trait IterablePicklers extends GeneratorRegistry {

implicit def iterablePickler[T: FastTypeTag]
(implicit elemPickler: Pickler[T], elemUnpickler: Unpickler[T],
collTag: FastTypeTag[Iterable[T]],
cbf: CanBuildFrom[Iterable[T], T, Iterable[T]]
): Pickler[Iterable[T]] with Unpickler[Iterable[T]] = TravPickler[T, Iterable[T]]
implicit def iterablePickler[T : FastTypeTag](
implicit elemPickler: Pickler[T],
elemUnpickler: Unpickler[T],
collTag: FastTypeTag[Iterable[T]],
cbf: CanBuildFrom[Iterable[T], T, Iterable[T]]
): AbstractPicklerUnpickler[Iterable[T]] = TravPickler[T, Iterable[T]]

implicit def listPickler[T: FastTypeTag]
(implicit elemPickler: Pickler[T], elemUnpickler: Unpickler[T],
colTag: FastTypeTag[List[T]],
cbf: CanBuildFrom[List[T], T, List[T]]
): Pickler[List[T]] with Unpickler[List[T]] = TravPickler[T, List[T]]
implicit def listPickler[T : FastTypeTag](
implicit elemPickler: Pickler[T],
elemUnpickler: Unpickler[T],
colTag: FastTypeTag[List[T]],
cbf: CanBuildFrom[List[T], T, List[T]]
): AbstractPicklerUnpickler[List[T]] = TravPickler[T, List[T]]

locally {

val cbf = implicitly[CanBuildFrom[List[Any], Any, List[Any]]]
val generator: PicklerUnpicklerGen[List[Any]] =
val generator: PicklerUnpicklerGen[List[Any]] = {
TravPickler.generate(cbf, identity[List[Any]]) { tpe =>
TravPickler.oneArgumentTagExtractor(tpe)
}
}

// TODO: Deserialize directly Nil and remove this hack.
registerGen("scala.collection.immutable.Nil.type", generator)
registerGen("scala.collection.immutable.$colon$colon", generator)
registerGen("scala.collection.immutable.List", generator)

}

locally {

val cbf = implicitly[CanBuildFrom[Iterable[Any], Any, Iterable[Any]]]
val generator: PicklerUnpicklerGen[Iterable[Any]] =
val generator: PicklerUnpicklerGen[Iterable[Any]] = {
TravPickler.generate(cbf, identity[Iterable[Any]]) { tpe =>
TravPickler.oneArgumentTagExtractor(tpe)
}
}

registerGen("scala.collection.Iterable", generator)

}

}

object TravPickler extends GeneratorHelper {

private val ANY_TAG = FastTypeTag.Any

def oneArgumentTagExtractor[T](tpe: FastTypeTag[_]): FastTypeTag[T] = {
val typeArgs = tpe.typeArgs
typeArgs match {
case List(one) => one.asInstanceOf[FastTypeTag[T]]
// TODO: Remove this hack to handle `Nil.type` as a tag
case List() => ANY_TAG.asInstanceOf[FastTypeTag[T]]
case x => throw TypeMismatch(List(tpe), typeArgs)
}
}

/** Creates a pickling generator for any [[Traversable]] to be used at runtime. */
def generate[T, C](cbf: CanBuildFrom[C,T,C], asTrav: C => Traversable[_])
(elementTagExtractor: FastTypeTagSpecializer[T])
(tpe: FastTypeTag[_]): AbstractPicklerUnpickler[C] = {
def generate[T, C](cbf: CanBuildFrom[C, T, C], asTrav: C => Traversable[_])(
elementTagExtractor: FastTypeTagSpecializer[T])(
tpe: FastTypeTag[_]): AbstractPicklerUnpickler[C] = {

val (elemPickler, elemUnpickler) = generateHelper(elementTagExtractor)(tpe)
apply[T,C](asTrav, elemPickler, elemUnpickler, cbf, specialize[C](tpe))

apply[T, C](asTrav, elemPickler, elemUnpickler, cbf, specialize[C](tpe))
}


def apply[T, C](implicit asTrav: C => Traversable[_], elemPickler: Pickler[T],
elemUnpickler: Unpickler[T], cbf: CanBuildFrom[C, T, C],
collTag: FastTypeTag[C]): AbstractPicklerUnpickler[C] = {
def apply[T, C](implicit asTrav: C => Traversable[_],
elemPickler: Pickler[T],
elemUnpickler: Unpickler[T],
cbf: CanBuildFrom[C, T, C],
collTag: FastTypeTag[C]): AbstractPicklerUnpickler[C] = {
new AbstractPicklerUnpickler[C] with AutoRegister[C] {

val elemTag = elemPickler.tag
Expand All @@ -86,7 +74,8 @@ object TravPickler extends GeneratorHelper {

def pickle(coll: C, builder: PBuilder): Unit = {
// TODO Can we do the same for other type tags?
if (elemTag == FastTypeTag.Int) builder.hintKnownSize(coll.size * 4 + 100)
if (elemTag == FastTypeTag.Int)
builder.hintKnownSize(coll.size * 4 + 100)
builder.beginEntry(coll, tag)
builder.beginCollection(coll.size)

Expand All @@ -96,10 +85,11 @@ object TravPickler extends GeneratorHelper {
builder.pinHints()
}

(coll: Traversable[_]).asInstanceOf[Traversable[T]].foreach { (elem: T) =>
builder putElement { b =>
elemPickler.pickle(elem, b)
}
(coll: Traversable[_]).asInstanceOf[Traversable[T]].foreach {
(elem: T) =>
builder putElement { b =>
elemPickler.pickle(elem, b)
}
}

builder.popHints()
Expand Down Expand Up @@ -135,17 +125,19 @@ object TravPickler extends GeneratorHelper {
}

object SeqSetPickler {
def apply[T: FastTypeTag, Coll[_] <: Traversable[_]]
(implicit elemPickler: Pickler[T], elemUnpickler: Unpickler[T],
cbf: CanBuildFrom[Coll[T], T, Coll[T]],
collTag: FastTypeTag[Coll[T]]): Pickler[Coll[T]] with Unpickler[Coll[T]] =
TravPickler[T, Coll[T]]
def apply[T : FastTypeTag, Coll[_] <: Traversable[_]](
implicit elemPickler: Pickler[T],
elemUnpickler: Unpickler[T],
cbf: CanBuildFrom[Coll[T], T, Coll[T]],
collTag: FastTypeTag[Coll[T]]
): AbstractPicklerUnpickler[Coll[T]] = TravPickler[T, Coll[T]]
}

object MapPickler {
def apply[K: FastTypeTag, V: FastTypeTag, M[_, _] <: collection.Map[_, _]]
(implicit elemPickler: Pickler[(K, V)], elemUnpickler: Unpickler[(K, V)],
cbf: CanBuildFrom[M[K, V], (K, V), M[K, V]],
collTag: FastTypeTag[M[K, V]]): Pickler[M[K, V]] with Unpickler[M[K, V]] =
TravPickler[(K, V), M[K, V]]
def apply[K : FastTypeTag, V : FastTypeTag, M[_, _] <: collection.Map[_, _]](
implicit elemPickler: Pickler[(K, V)],
elemUnpickler: Unpickler[(K, V)],
cbf: CanBuildFrom[M[K, V], (K, V), M[K, V]],
collTag: FastTypeTag[M[K, V]]
): AbstractPicklerUnpickler[M[K, V]] = TravPickler[(K, V), M[K, V]]
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import java.math.BigDecimal
* Note; This currently serialzies as a string.
*/
trait JavaBigDecimalPicklers extends PrimitivePicklers {
implicit val javaBigDecimalPickler: Pickler[BigDecimal] with Unpickler[BigDecimal] =

implicit val javaBigDecimalPickler: AbstractPicklerUnpickler[BigDecimal] = {
new AbstractPicklerUnpickler[BigDecimal] with AutoRegister[BigDecimal] {
lazy val tag = FastTypeTag[BigDecimal]("java.math.BigDecimal")

def pickle(picklee: BigDecimal, builder: PBuilder): Unit = {
builder.beginEntry(picklee, tag)
builder.putField("value", b => {
Expand All @@ -18,12 +20,14 @@ trait JavaBigDecimalPicklers extends PrimitivePicklers {
})
builder.endEntry()
}

def unpickle(tag: String, reader: PReader): Any = {
val reader1 = reader.readField("value")
reader1.hintElidedType(implicitly[FastTypeTag[String]])
val result = stringPickler.unpickleEntry(reader1)
new BigDecimal(result.asInstanceOf[String])
}
}
internal.currentRuntime.picklers.registerPicklerUnpickler(javaBigDecimalPickler)
}

}
Loading

0 comments on commit f38f027

Please sign in to comment.