From 936430c1057fd9c6f731d89021ca47ad7b947163 Mon Sep 17 00:00:00 2001 From: jvican Date: Sun, 17 Apr 2016 16:33:04 +0200 Subject: [PATCH 1/3] Generate Pickler/Unpickler together by default * Make `Any` and `Either` to generate pickler/unpickler at the same time. * Improve some documentation by adding more concrete explanations. --- .../internal/AutoDefaultRuntimePicklers.scala | 6 +- .../scala/scala/pickling/pickler/Any.scala | 48 +++++++---- .../scala/scala/pickling/pickler/Either.scala | 81 +++++++++---------- .../scala/pickling/pickler/Iterable.scala | 4 +- .../scala/pickling/pickler/Primitives.scala | 25 +++--- 5 files changed, 88 insertions(+), 76 deletions(-) diff --git a/core/src/main/scala/scala/pickling/internal/AutoDefaultRuntimePicklers.scala b/core/src/main/scala/scala/pickling/internal/AutoDefaultRuntimePicklers.scala index 71e47635c0..034df36c38 100644 --- a/core/src/main/scala/scala/pickling/internal/AutoDefaultRuntimePicklers.scala +++ b/core/src/main/scala/scala/pickling/internal/AutoDefaultRuntimePicklers.scala @@ -1,7 +1,7 @@ package scala.pickling package internal -import scala.pickling.pickler.AnyUnpickler +import scala.pickling.pickler.AnyPicklerUnpickler import scala.pickling.runtime.{Tuple2RTKnownTagUnpickler, Tuple2RTPickler} import scala.pickling.spi.PicklerRegistry @@ -59,9 +59,9 @@ trait RuntimePicklerRegistryHelper extends PicklerRegistry { def tupleUnpicklerGenerator: FastTypeTag[_] => Unpickler[(Any,Any)] = { case FastTypeTag(_, List(left, right)) => val lhs = - currentRuntime.picklers.lookupUnpickler(left.toString).getOrElse(AnyUnpickler).asInstanceOf[Unpickler[Any]] + currentRuntime.picklers.lookupUnpickler(left.toString).getOrElse(AnyPicklerUnpickler).asInstanceOf[Unpickler[Any]] val rhs = - currentRuntime.picklers.lookupUnpickler(right.toString).getOrElse(AnyUnpickler).asInstanceOf[Unpickler[Any]] + currentRuntime.picklers.lookupUnpickler(right.toString).getOrElse(AnyPicklerUnpickler).asInstanceOf[Unpickler[Any]] new Tuple2RTKnownTagUnpickler(lhs, rhs) case tpe => new Tuple2RTPickler() } diff --git a/core/src/main/scala/scala/pickling/pickler/Any.scala b/core/src/main/scala/scala/pickling/pickler/Any.scala index 93a6933943..05a7f2eda2 100644 --- a/core/src/main/scala/scala/pickling/pickler/Any.scala +++ b/core/src/main/scala/scala/pickling/pickler/Any.scala @@ -1,34 +1,48 @@ package scala.pickling package pickler -/** An pickler for "Any" value (will look up pickler at runtime, or generate it. */ -object AnyPickler extends Pickler[Any] with AutoRegisterPickler[Any] { +import scala.pickling.internal.GRL +import scala.pickling.internal.currentRuntime +import scala.reflect.runtime.currentMirror + +/** Generate a [[Pickler]] and [[Unpickler]] for [[Any]]. + * + * It will look up pickler/unpickler at runtime, or generate it. + */ +object AnyPicklerUnpickler extends Pickler[Any] with Unpickler[Any] + with AutoRegister[Any] { + override def tag: FastTypeTag[Any] = FastTypeTag.Any + + /** Pickle [[Any]] by getting its class at runtime and looking + * up the correct [[Pickler]] for that class if available or + * generate it. + * + * Don't use [[AnyPicklerUnpickler]] for pickling classes with generic + * types. Otherwise, it will fail because of the type erasure + * and the lookup will replace the unknown type by [[Any]]. + */ override def pickle(picklee: Any, builder: PBuilder): Unit = { - // Here we just look up the pickler. val clazz = picklee.getClass val classLoader = this.getClass.getClassLoader - internal.GRL.lock() + GRL.lock() val tag = try FastTypeTag.makeRaw(clazz) - finally internal.GRL.unlock() - val p = internal.currentRuntime.picklers.genPickler(classLoader, clazz, tag) + finally GRL.unlock() + val p = currentRuntime.picklers.genPickler(classLoader, clazz, tag) p.asInstanceOf[Pickler[Any]].pickle(picklee, builder) } - override def toString = "AnyPickler" -} -/** An unpickler for "Any" value (will look up unpickler at runtime, or generate it. */ -object AnyUnpickler extends Unpickler[Any] with AutoRegisterUnpickler[Any] { - def tag: FastTypeTag[Any] = FastTypeTag.Any + + /** Unpickle something as [[Any]] by looking up registered + * unpicklers for [[tag]] or using runtime unpickler generation. + */ def unpickle(tag: String, reader: PReader): Any = { - val actualUnpickler = internal.currentRuntime.picklers.genUnpickler(scala.reflect.runtime.currentMirror, tag) + val actualUnpickler = currentRuntime.picklers.genUnpickler(currentMirror, tag) actualUnpickler.unpickle(tag, reader) } - override def toString = "AnyUnPickler" + + override def toString = "AnyPicklerUnpickler" } -/** Attempts to unpickle Any by looking up registered unpicklers using `currentMirror`. - */ trait AnyUnpicklers { - // Any - implicit val anyUnpickler: Unpickler[Any] = AnyUnpickler + implicit val anyUnpickler: Unpickler[Any] = AnyPicklerUnpickler } diff --git a/core/src/main/scala/scala/pickling/pickler/Either.scala b/core/src/main/scala/scala/pickling/pickler/Either.scala index c811d72f6f..d7fd1c4985 100644 --- a/core/src/main/scala/scala/pickling/pickler/Either.scala +++ b/core/src/main/scala/scala/pickling/pickler/Either.scala @@ -1,76 +1,69 @@ package scala.pickling package pickler -/** Picklers for either trait. */ +/** Generate [[Pickler]]s and [[Unpickler]]s for [[Either]] + * and its subclasses [[Right]] and [[Left]]. + */ trait EitherPicklers { // TODO(jsuereth) - Register pickler generators - implicit def pickleLeft[L, R](implicit lp: Pickler[L], t: FastTypeTag[Left[L,R]]): Pickler[Left[L, R]] = - new AbstractPickler[Left[L, R]] with AutoRegisterPickler[Left[L, R]] { + implicit def pickleUnpickleLeft[L, R](implicit lp: Pickler[L], lu: Unpickler[L], + t: FastTypeTag[Left[L,R]]): AbstractPicklerUnpickler[Left[L, R]] = + new AbstractPicklerUnpickler[Left[L, R]] with AutoRegister[Left[L, R]] { + override lazy val tag: FastTypeTag[Left[L, R]] = t override def pickle(picklee: Left[L, R], builder: PBuilder): Unit = { builder.beginEntry(picklee, tag) if(lp.tag.isEffectivelyPrimitive) builder.hintElidedType(lp.tag) builder.putField("a", b => lp.pickle(picklee.a, b)) builder.endEntry() } - override def tag: FastTypeTag[Left[L, R]] = t - override def toString = s"LeftPickler($tag)" + override def unpickle(tag: String, reader: PReader): Any = { + if (t.key == tag) { + val rr = reader.readField("a") + if(lp.tag.isEffectivelyPrimitive) rr.hintElidedType(lp.tag) + Left(lu.unpickleEntry(rr).asInstanceOf[R]) + } else throw new PicklingException(s"LeftUnpickler can't unpickle: $tag") + } + override def toString = s"LeftPicklerUnpickler($tag)" } - implicit def pickleRight[L,R](implicit rp: Pickler[R], t: FastTypeTag[Right[L,R]]): Pickler[Right[L,R]] = - new AbstractPickler[Right[L, R]] with AutoRegisterPickler[Right[L, R]] { + + implicit def pickleUnpickleRight[L,R](implicit rp: Pickler[R], ru: Unpickler[R], + t: FastTypeTag[Right[L,R]]): AbstractPicklerUnpickler[Right[L,R]] = + new AbstractPicklerUnpickler[Right[L, R]] with AutoRegister[Right[L, R]] { + override lazy val tag: FastTypeTag[Right[L, R]] = t override def pickle(picklee: Right[L, R], builder: PBuilder): Unit = { builder.beginEntry(picklee, tag) if(rp.tag.isEffectivelyPrimitive) builder.hintElidedType(rp.tag) builder.putField("b", b => rp.pickle(picklee.b, b)) builder.endEntry() } - override def tag: FastTypeTag[Right[L, R]] = t - override def toString = s"RightPickler($tag)" + override def unpickle(tag: String, reader: PReader): Any = { + if (t.key == tag) { + val rr = reader.readField("b") + if(rp.tag.isEffectivelyPrimitive) rr.hintElidedType(rp.tag) + Right(ru.unpickleEntry(rr).asInstanceOf[R]) + } else throw new PicklingException(s"RightUnpickler can't unpickle: $tag") + } + override def toString = s"RightPicklerUnpickler($tag)" } - implicit def pickleEither[L,R](implicit rp: Pickler[Right[L,R]], lp: Pickler[Left[L,R]], t: FastTypeTag[Either[L,R]]): Pickler[Either[L,R]] = - new AbstractPickler[Either[L, R]] with AutoRegisterPickler[Either[L, R]] { + implicit def pickleUnpickleEither[L,R](implicit rp: Pickler[Right[L,R]], ru: Unpickler[Right[L, R]], + lp: Pickler[Left[L,R]], lu: Unpickler[Left[L, R]], + t: FastTypeTag[Either[L,R]]): AbstractPicklerUnpickler[Either[L,R]] = + new AbstractPicklerUnpickler[Either[L, R]] with AutoRegister[Either[L, R]] { override def pickle(picklee: Either[L, R], builder: PBuilder): Unit = { picklee match { case l: Left[L,R] => lp.pickle(l, builder) case r: Right[L,R] => rp.pickle(r, builder) } } - override def tag: FastTypeTag[Either[L, R]] = t - override def toString = s"EitherPickler($t)" - } - - implicit def unpickleLeft[L, R](implicit lp: Unpickler[L], t: FastTypeTag[Left[L,R]]): Unpickler[Left[L, R]] = - new AbstractUnpickler[Left[L, R]] with AutoRegisterUnpickler[Left[L, R]] { - override def toString = s"LeftUnpickler($tag)" - override def unpickle(tag: String, reader: PReader): Any = { - // TODO - check tag == our tag? - val rr = reader.readField("a") - if(lp.tag.isEffectivelyPrimitive) rr.hintElidedType(lp.tag) - Left(lp.unpickleEntry(rr).asInstanceOf[R]) - } - override def tag: FastTypeTag[Left[L, R]] = t - } - implicit def unpickleRight[L, R](implicit rp: Unpickler[R], t: FastTypeTag[Right[L,R]]): Unpickler[Right[L, R]] = - new AbstractUnpickler[Right[L, R]] with AutoRegisterUnpickler[Right[L, R]] { override def unpickle(tag: String, reader: PReader): Any = { - // TODO - check tag == our tag? - val rr = reader.readField("b") - if(rp.tag.isEffectivelyPrimitive) rr.hintElidedType(rp.tag) - Right(rp.unpickleEntry(rr).asInstanceOf[R]) - } - override def tag: FastTypeTag[Right[L, R]] = t - override def toString = s"RightUnpickler($tag)" - } - implicit def unpickleEither[L, R](implicit rp: Unpickler[Right[L,R]], lp: Unpickler[Left[L,R]], - t: FastTypeTag[Either[L,R]]): Unpickler[Either[L, R]] = - new AbstractUnpickler[Either[L, R]] with AutoRegisterUnpickler[Either[L, R]] { - override def unpickle(tag: String, reader: PReader): Any = { - if(tag == rp.tag.key) rp.unpickle(tag,reader) - else if(tag == lp.tag.key) lp.unpickle(tag, reader) - else throw new PicklingException(s"Unknown type tag for Either: $tag") + if(tag == rp.tag.key) ru.unpickle(tag,reader) + else if(tag == lp.tag.key) lu.unpickle(tag, reader) + else throw new PicklingException(s"Unknown type tag for Either: $tag") } override def tag: FastTypeTag[Either[L, R]] = t - override def toString = s"EitherUnpickler($tag)" + override def toString = s"EitherPicklerUnpickler($t)" } + } \ No newline at end of file diff --git a/core/src/main/scala/scala/pickling/pickler/Iterable.scala b/core/src/main/scala/scala/pickling/pickler/Iterable.scala index 66d59c24f5..4c6106702b 100644 --- a/core/src/main/scala/scala/pickling/pickler/Iterable.scala +++ b/core/src/main/scala/scala/pickling/pickler/Iterable.scala @@ -53,11 +53,11 @@ object TravPickler { def generate[T, C](cbf: CanBuildFrom[C,T,C], asTraversable: C => Traversable[_])(elementTagExtractor: FastTypeTag[_] => FastTypeTag[T])(tpe: FastTypeTag[_]): AbstractPicklerUnpickler[C] = { val elementType = elementTagExtractor(tpe) val elemPickler = - if(elementType.key == ANY_TAG.key) AnyPickler + if(elementType.key == ANY_TAG.key) AnyPicklerUnpickler else currentRuntime.picklers.lookupPickler(elementType.key).getOrElse( throw new PicklingException(s"Cannnot generate a pickler/unpickler for $tpe, cannot find a pickler for $elementType")) val elemUnpickler = - if(elementType.key == ANY_TAG.key) AnyUnpickler + if(elementType.key == ANY_TAG.key) AnyPicklerUnpickler else currentRuntime.picklers.lookupUnpickler(elementType.key).getOrElse( throw new PicklingException(s"Cannnot generate a pickler/unpickler for $tpe, cannot find an unpickler for $elementType")) val colTag = tpe diff --git a/core/src/main/scala/scala/pickling/pickler/Primitives.scala b/core/src/main/scala/scala/pickling/pickler/Primitives.scala index 80c2ffc4b7..80884c4c95 100644 --- a/core/src/main/scala/scala/pickling/pickler/Primitives.scala +++ b/core/src/main/scala/scala/pickling/pickler/Primitives.scala @@ -1,8 +1,11 @@ package scala.pickling package pickler -/** Picklers for primitive types. - */ +/** Generate [[Pickler]]s and [[Unpickler]]s for the primitive types. + * + * The primitive types are [[Byte]], [[Short]], [[Char]], [[Int]], [[Long]], + * [[Boolean]], [[Float]], [[Double]], [[Null]], [[String]] and [[Unit]]. + */ trait PrimitivePicklers { implicit val bytePickler: Pickler[Byte] with Unpickler[Byte] = PrimitivePickler[Byte] implicit val shortPickler: Pickler[Short] with Unpickler[Short] = PrimitivePickler[Short] @@ -27,14 +30,16 @@ class PrimitivePickler[T: FastTypeTag](name: String) } override def unpickle(tag: String, reader: PReader): Any = { try { - // TODO - beginEntry/endEntry? - reader.readPrimitive() - } catch { - case PicklingException(msg, cause) => - throw PicklingException(s"""error in unpickle of primitive unpickler '$name': - |tag in unpickle: '${tag}' - |message: - |$msg""".stripMargin, cause) + reader.beginEntry() + val readValue = reader.readPrimitive() + reader.endEntry() + readValue + } catch { case PicklingException(msg, cause) => + throw PicklingException( + s"""error in unpickle of primitive unpickler '$name': + |tag in unpickle: '${tag}' + |message: + |$msg""".stripMargin, cause) } } } From eaa7c2a5858acbbea7a63a5a8f266f4dca7d5fd2 Mon Sep 17 00:00:00 2001 From: jvican Date: Sun, 17 Apr 2016 20:28:07 +0200 Subject: [PATCH 2/3] Force automatic generation of (un)pickler together * Macros generate `Pickler[T]`/`Unpickler[T]` together instead of creating them separately. This reduces final code size since now there's only one class instead of two and also improves the reuse of already generated picklers (see issue #409). * As now the return type of the implicit method is `AbstractPicklerUnpickler`, there can be some errors because of ambiguous implicit values (e.g. sealed-trait-static) when (un)picklers are explicitly called via `generate`. The fix is to only use `PicklerUnpickler.generate` and deprecate separate explicit creation with `Pickler.generate` and `Unpickler.generate`. --- .../main/scala/scala/pickling/Pickler.scala | 7 +- .../scala/pickling/generator/Compat.scala | 16 ----- .../scala/pickling/generator/Macros.scala | 67 +++---------------- .../scala/pickling/generator/sourcegen.scala | 65 ------------------ .../scala/scala/pickling/pickler/Any.scala | 2 +- .../scala/pickling/pickler/GenPicklers.scala | 3 +- .../pickling/pickler/GenUnpicklers.scala | 3 +- .../scala/pickling/pickler/Primitives.scala | 9 +-- .../test/scala/pickling/run/issue409.scala | 19 ++++++ .../run/sealed-trait-static-annotated.scala | 11 ++- .../pickling/run/sealed-trait-static.scala | 5 +- 11 files changed, 50 insertions(+), 157 deletions(-) create mode 100644 core/src/test/scala/pickling/run/issue409.scala diff --git a/core/src/main/scala/scala/pickling/Pickler.scala b/core/src/main/scala/scala/pickling/Pickler.scala index 2a1eca45e5..72a540b974 100644 --- a/core/src/main/scala/scala/pickling/Pickler.scala +++ b/core/src/main/scala/scala/pickling/Pickler.scala @@ -26,7 +26,8 @@ trait Pickler[T] { // Shim for Java code. abstract class AbstractPickler[T] extends Pickler[T] object Pickler { - def generate[T]: Pickler[T] = macro generator.Compat.genPickler_impl[T] + @deprecated("Use `PicklerUnpickler.generate` instead", "0.11") + def generate[T]: Pickler[T] = macro generator.Compat.genPicklerUnpickler_impl[T] } /** A dynamic pickler for type `T`. Its `pickle` method takes an object-to-be-pickled of @@ -82,7 +83,8 @@ trait Unpickler[T] { // Shim for Java code. abstract class AbstractUnpickler[T] extends Unpickler[T] object Unpickler { - def generate[T]: Unpickler[T] = macro generator.Compat.genUnpickler_impl[T] + @deprecated("Use `PicklerUnpickler.generate` instead", "0.11") + def generate[T]: Unpickler[T] = macro generator.Compat.genPicklerUnpickler_impl[T] } /* Shim for java code which wants to pickle/unpickle. */ abstract class AbstractPicklerUnpickler[T] extends Pickler[T] with Unpickler[T] @@ -90,7 +92,6 @@ object PicklerUnpickler { def apply[T](p: Pickler[T], u: Unpickler[T]): AbstractPicklerUnpickler[T] = new DelegatingPicklerUnpickler(p, u) //def generate[T]: Pickler[T] with Unpickler[T] = macro Compat.PicklerUnpicklerMacros_impl[T] def generate[T]: AbstractPicklerUnpickler[T] = macro generator.Compat.genPicklerUnpickler_impl[T] - def debugGenerate[T]: String = macro generator.Compat.genPicklerUnpickler_debug[T] /** This is a private implementation of PicklerUnpickler that delegates pickle and unpickle to underlying. */ private class DelegatingPicklerUnpickler[T](p: Pickler[T], u: Unpickler[T]) extends AbstractPicklerUnpickler[T] { // From Pickler diff --git a/core/src/main/scala/scala/pickling/generator/Compat.scala b/core/src/main/scala/scala/pickling/generator/Compat.scala index 9148223506..012b632221 100644 --- a/core/src/main/scala/scala/pickling/generator/Compat.scala +++ b/core/src/main/scala/scala/pickling/generator/Compat.scala @@ -9,11 +9,6 @@ import scala.reflect.macros.Context import scala.reflect.runtime.{universe => ru} private[pickling] object Compat { - def genPickler_impl[T: c.WeakTypeTag](c: Context): c.Expr[Pickler[T] with Generated] = { - val c0: c.type = c - val bundle = new { val c: c0.type = c0 } with PicklingMacros - c.Expr[Pickler[T] with Generated](bundle.genPickler[T]) - } def genPicklerUnpickler_impl[T: c.WeakTypeTag](c: Context): c.Expr[AbstractPicklerUnpickler[T] with Generated] = { val c0: c.type = c @@ -21,15 +16,4 @@ private[pickling] object Compat { c.Expr[AbstractPicklerUnpickler[T] with Generated](bundle.genPicklerUnpickler[T]) } - def genUnpickler_impl[T: c.WeakTypeTag](c: Context): c.Expr[Unpickler[T] with Generated] = { - val c0: c.type = c - val bundle = new { val c: c0.type = c0 } with PicklingMacros - c.Expr[Unpickler[T] with Generated](bundle.genUnPickler[T]) - } - - def genPicklerUnpickler_debug[T: c.WeakTypeTag](c: Context): c.Expr[String] = { - val c0: c.type = c - val bundle = new { val c: c0.type = c0 } with PicklingMacros - c.Expr[String](bundle.debugPicklerUnpickler[T]) - } } diff --git a/core/src/main/scala/scala/pickling/generator/Macros.scala b/core/src/main/scala/scala/pickling/generator/Macros.scala index 2aa91c7388..d60769118f 100644 --- a/core/src/main/scala/scala/pickling/generator/Macros.scala +++ b/core/src/main/scala/scala/pickling/generator/Macros.scala @@ -3,9 +3,17 @@ package generator private[pickling] object PicklingMacros { import scala.language.experimental.macros - def genPickler[T]: Pickler[T] with Generated = macro scala.pickling.generator.Compat.genPickler_impl[T] - def genUnpickler[T]: Unpickler[T] with Generated = macro scala.pickling.generator.Compat.genUnpickler_impl[T] - def genPicklerUnpickler[T]: AbstractPicklerUnpickler[T] with Generated = macro scala.pickling.generator.Compat.genPicklerUnpickler_impl[T] + + @deprecated("Use `genPicklerUnpickler` instead", "0.11") + def genPickler[T]: AbstractPicklerUnpickler[T] with Generated = + macro scala.pickling.generator.Compat.genPicklerUnpickler_impl[T] + + @deprecated("Use `genPicklerUnpickler` instead", "0.11") + def genUnpickler[T]: AbstractPicklerUnpickler[T] with Generated = + macro scala.pickling.generator.Compat.genPicklerUnpickler_impl[T] + + def genPicklerUnpickler[T]: AbstractPicklerUnpickler[T] with Generated = + macro scala.pickling.generator.Compat.genPicklerUnpickler_impl[T] } private[pickling] trait PicklingMacros extends Macro with SourceGenerator with TypeAnalysis { import c.universe._ @@ -54,59 +62,6 @@ private[pickling] trait PicklingMacros extends Macro with SourceGenerator with T } } - def genPickler[T: c.WeakTypeTag]: c.Tree = preferringAlternativeImplicits { - val tpe = computeType[T] - checkClassType(tpe) - val sym = symbols.newClass(tpe) - val impl = PicklingAlgorithm.run(generator)(sym, logger) - val tree2 = impl map { - case PickleUnpickleImplementation(alg2, alg) => generatePicklerClass[T](alg2) - } - tree2 match { - case None => - c.error(c.enclosingPosition, s"Failed to generate pickler for $tpe") - ??? - case Some(tree) => - //System.err.println(s" --=== $tpe ===--\n$tree\n --=== / $tpe ===--") - tree - } - } - def genUnPickler[T: c.WeakTypeTag]: c.Tree = preferringAlternativeImplicits { - val tpe = computeType[T] - checkClassType(tpe) - val sym = symbols.newClass(tpe) - val impl = PicklingAlgorithm.run(generator)(sym, logger) - val tree2 = impl map { - case PickleUnpickleImplementation(alg2, alg) => generateUnpicklerClass[T](alg) - } - tree2 match { - case None => - c.error(c.enclosingPosition, s"Failed to generate unpickler for $tpe") - ??? - case Some(tree) => - //System.err.println(s" --=== $tpe ===--\n$tree\n --=== / $tpe ===--") - tree - } - } - - def debugPicklerUnpickler[T: c.WeakTypeTag]: c.Tree = preferringAlternativeImplicits { - val tpe = computeType[T] - checkClassType(tpe) - val sym = symbols.newClass(tpe) - val impl = PicklingAlgorithm.run(generator)(sym, logger) - //System.err.println(impl) - val tree2 = impl map generatePicklerUnpicklerClass[T] - tree2 match { - case None => - c.error(c.enclosingPosition, s"Failed to generate pickler/unpickler for $tpe") - ??? - case Some(tree) => - //System.err.println(s" --=== $tpe ===--\n$tree\n --=== / $tpe ===--") - val treeString = s"$tree" - q"$treeString" - } - } - def genPicklerUnpickler[T: c.WeakTypeTag]: c.Tree = preferringAlternativeImplicits { val tpe = computeType[T] checkClassType(tpe) diff --git a/core/src/main/scala/scala/pickling/generator/sourcegen.scala b/core/src/main/scala/scala/pickling/generator/sourcegen.scala index 377ac9a803..7693847ea9 100644 --- a/core/src/main/scala/scala/pickling/generator/sourcegen.scala +++ b/core/src/main/scala/scala/pickling/generator/sourcegen.scala @@ -387,18 +387,6 @@ private[pickling] trait SourceGenerator extends Macro with tags.FastTypeTagMacr val picklersRegistry = q"$picklingPath.internal.currentRuntime.picklers" val generated = tq"$picklingPath.Generated" - def lazyInit(lookup: c.Tree, key: c.Tree, tpe: c.Tree, - notInitialized: c.TermName): c.Tree = { - q""" - if($picklersRegistry.isLookupEnabled) { - $lookup($key) match { - case Some(p) => p.asInstanceOf[$tpe] - case None => $notInitialized: $tpe - } - } else $notInitialized: $tpe - """ - } - def picklerUnpicklerLazyInit(lookup1: c.Tree, lookup2: c.Tree, key: c.Tree, tpe: c.Tree, notInitialized: c.TermName) = { q""" @@ -411,59 +399,6 @@ private[pickling] trait SourceGenerator extends Macro with tags.FastTypeTagMacr """ } - /** generates the tree which will construct + return a new instance of a Pickler class, capable of - * pickling an instance of type T, using the behavior outlined by the PicklerAst. - */ - def generatePicklerClass[T: c.WeakTypeTag](picklerAst: PicklerAst): c.Tree = { - - val tpe = computeType[T] - val picklerName = c.fresh(newTermName(syntheticBaseName(tpe) + "Pickler")) - val createTagTree = super[FastTypeTagMacros].impl[T] - val picklerType = tq"$picklingPath.Pickler[$tpe]" - val key = q"$createTagTree.key" - val lookup = q"$picklersRegistry.lookupExistingPickler" - - q""" - _root_.scala.Predef.locally { - implicit object $picklerName extends $picklerType with $picklingPath.Generated - with $picklingPath.AutoRegisterPickler[$tpe]{ - - lazy val tag: $picklingPath.FastTypeTag[$tpe] = $createTagTree - def pickle(picklee: $tpe, builder: $picklingPath.PBuilder): _root_.scala.Unit = ${genPicklerLogic[T](picklerAst)} - - } - ${lazyInit(lookup, key, picklerType, picklerName)} - } - """ - - } - - def generateUnpicklerClass[T: c.WeakTypeTag](unpicklerAst: UnpicklerAst): c.Tree = { - - val tpe = computeType[T] - val unpicklerName = c.fresh(newTermName(syntheticBaseName(tpe) + "Unpickler")) - val createTagTree = super[FastTypeTagMacros].impl[T] - val unpickleLogic = genUnpicklerLogic[T](unpicklerAst) - val unpicklerType = tq"$picklingPath.Unpickler[$tpe]" - val genUnpicklerType = tq"$unpicklerType with $generated" - val key = q"$createTagTree.key" - val lookup = q"$picklersRegistry.lookupExistingUnpickler" - - q""" - _root_.scala.Predef.locally { - implicit object $unpicklerName extends $unpicklerType with $generated - with $picklingPath.AutoRegisterUnpickler[$tpe] { - - lazy val tag: $picklingPath.FastTypeTag[$tpe] = $createTagTree - def unpickle(tagKey: _root_.java.lang.String, reader: $picklingPath.PReader): _root_.scala.Any = $unpickleLogic - - } - ${lazyInit(lookup, key, genUnpicklerType, unpicklerName)} - } - """ - - } - def generatePicklerUnpicklerClass[T: c.WeakTypeTag](impl: PickleUnpickleImplementation): c.Tree = { val tpe = computeType[T] diff --git a/core/src/main/scala/scala/pickling/pickler/Any.scala b/core/src/main/scala/scala/pickling/pickler/Any.scala index 05a7f2eda2..bb3600a5a9 100644 --- a/core/src/main/scala/scala/pickling/pickler/Any.scala +++ b/core/src/main/scala/scala/pickling/pickler/Any.scala @@ -9,7 +9,7 @@ import scala.reflect.runtime.currentMirror * * It will look up pickler/unpickler at runtime, or generate it. */ -object AnyPicklerUnpickler extends Pickler[Any] with Unpickler[Any] +object AnyPicklerUnpickler extends AbstractPicklerUnpickler[Any] with AutoRegister[Any] { override def tag: FastTypeTag[Any] = FastTypeTag.Any diff --git a/core/src/main/scala/scala/pickling/pickler/GenPicklers.scala b/core/src/main/scala/scala/pickling/pickler/GenPicklers.scala index bc19b86f82..15aeeddc05 100644 --- a/core/src/main/scala/scala/pickling/pickler/GenPicklers.scala +++ b/core/src/main/scala/scala/pickling/pickler/GenPicklers.scala @@ -7,5 +7,6 @@ import scala.language.experimental.macros * See also `Pickler.generate`. */ trait GenPicklers { - implicit def genPickler[T]: Pickler[T] = macro generator.Compat.genPickler_impl[T] + implicit def genPickler[T]: AbstractPicklerUnpickler[T] = + macro generator.Compat.genPicklerUnpickler_impl[T] } diff --git a/core/src/main/scala/scala/pickling/pickler/GenUnpicklers.scala b/core/src/main/scala/scala/pickling/pickler/GenUnpicklers.scala index 17499f9c46..c15a867368 100644 --- a/core/src/main/scala/scala/pickling/pickler/GenUnpicklers.scala +++ b/core/src/main/scala/scala/pickling/pickler/GenUnpicklers.scala @@ -9,5 +9,6 @@ trait GenOpenSumUnpicklers { } trait GenUnpicklers extends GenOpenSumUnpicklers { - implicit def genUnpickler[T]: Unpickler[T] with Generated = macro generator.Compat.genUnpickler_impl[T ] + implicit def genUnpickler[T]: AbstractPicklerUnpickler[T] with Generated = + macro generator.Compat.genPicklerUnpickler_impl[T ] } diff --git a/core/src/main/scala/scala/pickling/pickler/Primitives.scala b/core/src/main/scala/scala/pickling/pickler/Primitives.scala index 80884c4c95..6c088af50d 100644 --- a/core/src/main/scala/scala/pickling/pickler/Primitives.scala +++ b/core/src/main/scala/scala/pickling/pickler/Primitives.scala @@ -30,10 +30,11 @@ class PrimitivePickler[T: FastTypeTag](name: String) } override def unpickle(tag: String, reader: PReader): Any = { try { - reader.beginEntry() - val readValue = reader.readPrimitive() - reader.endEntry() - readValue + // Don't use beginEntry/endEntry because + // tag has to be elided and there's some + // incompatibilities between java and scala + // primitive types (like String or Int) + reader.readPrimitive() } catch { case PicklingException(msg, cause) => throw PicklingException( s"""error in unpickle of primitive unpickler '$name': diff --git a/core/src/test/scala/pickling/run/issue409.scala b/core/src/test/scala/pickling/run/issue409.scala new file mode 100644 index 0000000000..12dbae22d9 --- /dev/null +++ b/core/src/test/scala/pickling/run/issue409.scala @@ -0,0 +1,19 @@ +package scala.pickling.generation + +import org.scalatest.FunSuite +import scala.pickling._, Defaults._, json._ + +class CheckGenPicklerUnpicklerTogether extends FunSuite { + + case class Pi(number: Int) + + test("check that pickler and unpickler are generated together (I)") { + + val p = implicitly[Pickler[Pi]] + val u = implicitly[Unpickler[Pi]] + + assert(p === u) + + } + +} diff --git a/core/src/test/scala/pickling/run/sealed-trait-static-annotated.scala b/core/src/test/scala/pickling/run/sealed-trait-static-annotated.scala index e158b5ca7f..2de23d4e8c 100644 --- a/core/src/test/scala/pickling/run/sealed-trait-static-annotated.scala +++ b/core/src/test/scala/pickling/run/sealed-trait-static-annotated.scala @@ -1,6 +1,6 @@ package scala.pickling.test.sealedtraitstaticannotated -import scala.pickling.{PicklingException, directSubclasses, Pickler, Unpickler, Defaults } +import scala.pickling._ import scala.pickling.static._ import scala.pickling.json._ import Defaults.{ stringPickler, intPickler, refUnpickler, nullPickler } @@ -13,15 +13,13 @@ import org.scalatest.FunSuite trait Fruit object Banana { - implicit val pickler = Defaults.genPickler[Banana] - implicit val unpickler = Defaults.genUnpickler[Banana] + implicit val picklerUnpickler = Defaults.genPickler[Banana] } // this is BEFORE the subtypes below so directKnownSubclasses probably // won't work and this would break without the directSubclasses annotation. object Fruit { - implicit val pickler = Defaults.genPickler[Fruit] - implicit val unpickler = Defaults.genUnpickler[Fruit] + implicit val picklerUnpickler = Defaults.genPickler[Fruit] } sealed trait RedOrOrangeFruit extends Fruit @@ -86,8 +84,7 @@ class SealedTraitStaticAnnotatedTest extends FunSuite { } test("manually generate") { - implicit val pumpkinPickler = Pickler.generate[Pumpkin] - implicit val pumpkinUnpickler = Unpickler.generate[Pumpkin] + implicit val pumpkinPicklerUnpickler = PicklerUnpickler.generate[Pumpkin] val pumpkin = Pumpkin("Kabocha") val pumpkinString = pumpkin.pickle.value assert(JSONPickle(pumpkinString).unpickle[Pumpkin] == pumpkin) diff --git a/core/src/test/scala/pickling/run/sealed-trait-static.scala b/core/src/test/scala/pickling/run/sealed-trait-static.scala index b9d6f55a59..cf7ff607f8 100644 --- a/core/src/test/scala/pickling/run/sealed-trait-static.scala +++ b/core/src/test/scala/pickling/run/sealed-trait-static.scala @@ -1,6 +1,6 @@ package scala.pickling.test.sealedtraitstatic -import scala.pickling.{ Pickler, Unpickler, PicklingException } +import scala.pickling.{PicklerUnpickler, Pickler, Unpickler, PicklingException} import scala.pickling.static._ import scala.pickling.json._ import scala.pickling.Defaults._ @@ -18,8 +18,7 @@ final case class Banana(something: Int) extends Fruit final case class Cucumber(something: Int) // does not extend Fruit but same shape as Banana object Fruit { - implicit val pickler = Pickler.generate[Fruit] - implicit val unpickler = Unpickler.generate[Fruit] + implicit val picklerUnpickler = PicklerUnpickler.generate[Banana] } class SealedTraitStaticTest extends FunSuite { From 48e1c4d596ed573ffb9786b548a4916ba84eda04 Mon Sep 17 00:00:00 2001 From: jvican Date: Mon, 18 Apr 2016 11:24:03 +0200 Subject: [PATCH 3/3] Fix performance issue (generation of Pickler[Any]) * When generating (un)picklers, most of the time we look for the pickler of `Any`. By exposing the pickler of `Any`, we don't manually create one pickler/unpickler every time it's needed. This speeds up the compilation time almost by a factor of 2. --- core/src/main/scala/scala/pickling/pickler/Any.scala | 2 +- core/src/test/scala/pickling/run/share-binary-any.scala | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/scala/pickling/pickler/Any.scala b/core/src/main/scala/scala/pickling/pickler/Any.scala index bb3600a5a9..0d82eef507 100644 --- a/core/src/main/scala/scala/pickling/pickler/Any.scala +++ b/core/src/main/scala/scala/pickling/pickler/Any.scala @@ -44,5 +44,5 @@ object AnyPicklerUnpickler extends AbstractPicklerUnpickler[Any] } trait AnyUnpicklers { - implicit val anyUnpickler: Unpickler[Any] = AnyPicklerUnpickler + implicit val anyPicklerUnpickler: AbstractPicklerUnpickler[Any] = AnyPicklerUnpickler } diff --git a/core/src/test/scala/pickling/run/share-binary-any.scala b/core/src/test/scala/pickling/run/share-binary-any.scala index e5b2fc8784..dc1d1e23a6 100644 --- a/core/src/test/scala/pickling/run/share-binary-any.scala +++ b/core/src/test/scala/pickling/run/share-binary-any.scala @@ -40,6 +40,7 @@ class ShareBinaryAnyTest extends FunSuite { intercept[StackOverflowError] { import shareNothing._ + implicit val pu = PicklerUnpickler.generate[C] c1.c = c3 (c2: Any).pickle }