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

Commit 3bc60b8

Browse files
committed
Add custom runtime generators for Either
* Add custom runtime generators for Either and tests checking the new custom runtime generation. * Make `AnyPicklerUnpickler` deal with more corner cases. * Modify PicklingProtocol in sandbox-test and reuse it for both formats. * Add error for disabled runtime generation. * Improve the generator helper, make some renamings and add more documentation.
1 parent 4121da4 commit 3bc60b8

File tree

11 files changed

+327
-141
lines changed

11 files changed

+327
-141
lines changed

core/src/main/scala/scala/pickling/PicklingErrors.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ private[pickling] object Feedback {
88
def failedParsingMsg(x: String, format: String) =
99
s"Failed to parse $x as $format."
1010

11+
def disabledRuntimeGenerationMsg(tpe: String, failed: String) =
12+
s"Couldn't create $failed for $tpe because runtime generation is disabled."
13+
1114
}
1215

1316
object PicklingErrors {
@@ -55,7 +58,14 @@ object PicklingErrors {
5558
failedGenerationMsg(tpe, concreteTpe, "unpickler"), cause
5659
)
5760

58-
/** Represent any error related to parsgin. */
61+
/** Exception thrown when it's not possible to generated a [[Pickler]]
62+
* or [[Unpickler]] at runtime because runtime generation has been disabled.*/
63+
final case class RuntimeGenerationDisabled(tpe: String, failed: String)
64+
extends UnsupportedOperationException(
65+
disabledRuntimeGenerationMsg(tpe, failed)
66+
)
67+
68+
/** Represent any error related to parsing. */
5969
class ParsingException(msg: String) extends BasePicklingException(msg)
6070

6171
/** Exception thrown when the parsing of a message is not successful.

core/src/main/scala/scala/pickling/internal/DefaultPicklerRegistry.scala

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ import scala.pickling.spi.{PicklerRegistry, RuntimePicklerGenerator}
1212
final class DefaultPicklerRegistry(generator: RuntimePicklerGenerator)
1313
extends PicklerRegistry with RuntimePicklerRegistry {
1414

15-
type PicklerGen = FastTypeTag[_] => Pickler[_]
16-
type UnpicklerGen = FastTypeTag[_] => Unpickler[_]
15+
type PicklerGenerator = FastTypeTag[_] => Pickler[_]
16+
type UnpicklerGenerator = FastTypeTag[_] => Unpickler[_]
1717

1818
private val picklerMap: mutable.Map[String, Pickler[_]] = new TrieMap[String, Pickler[_]]
19-
private val picklerGenMap: mutable.Map[String, PicklerGen] = new TrieMap[String, PicklerGen]
19+
private val picklerGenMap: mutable.Map[String, PicklerGenerator] = new TrieMap[String, PicklerGenerator]
2020
private val unpicklerMap: mutable.Map[String, Unpickler[_]] = new TrieMap[String, Unpickler[_]]
21-
private val unpicklerGenMap: mutable.Map[String, UnpicklerGen] = new TrieMap[String, UnpicklerGen]
21+
private val unpicklerGenMap: mutable.Map[String, UnpicklerGenerator] = new TrieMap[String, UnpicklerGenerator]
2222

2323
registerRuntimePicklersAtInit()
2424

@@ -162,13 +162,10 @@ final class DefaultPicklerRegistry(generator: RuntimePicklerGenerator)
162162
*/
163163
private[pickling] def dumpStateTo(r: PicklerRegistry): Unit = {
164164

165-
type AnyPicklerGen = FastTypeTag[_] => Pickler[Any]
166-
type AnyUnpicklerGen = FastTypeTag[_] => Unpickler[Any]
167-
168165
for(p <- picklerMap) r.registerPickler(p._1, p._2.asInstanceOf[Pickler[Any]])
169-
for(p <- picklerGenMap) r.registerPicklerGenerator(p._1, p._2.asInstanceOf[AnyPicklerGen])
166+
for(p <- picklerGenMap) r.registerPicklerGenerator(p._1, p._2.asInstanceOf[PicklerGen[Any]])
170167
for(u <- unpicklerMap) r.registerUnpickler(u._1, u._2.asInstanceOf[Unpickler[Any]])
171-
for(u <- unpicklerGenMap) r.registerUnpicklerGenerator(u._1, u._2.asInstanceOf[AnyUnpicklerGen])
168+
for(u <- unpicklerGenMap) r.registerUnpicklerGenerator(u._1, u._2.asInstanceOf[UnpicklerGen[Any]])
172169

173170
}
174171

core/src/main/scala/scala/pickling/internal/RuntimePicklerRegistry.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import scala.pickling.spi.PicklerRegistry
1010
* Related to [[scala.pickling.runtime.CustomRuntime]].
1111
* Note: Currently this only handles Tuple2s.
1212
*/
13-
trait RuntimePicklerRegistry extends PicklerRegistry with CustomRuntime {
13+
trait RuntimePicklerRegistry extends CustomRuntime {
14+
this: PicklerRegistry =>
1415

1516
val tupleGenerators = (tuplePicklerGenerator, tupleUnpicklerGenerator)
1617

core/src/main/scala/scala/pickling/pickler/Any.scala

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,44 @@ import scala.reflect.runtime.currentMirror
1212
object AnyPicklerUnpickler extends AbstractPicklerUnpickler[Any]
1313
with AutoRegister[Any] {
1414

15+
final val nullPickler = Defaults.nullPickler.asInstanceOf[Pickler[Any]]
16+
1517
override def tag: FastTypeTag[Any] = FastTypeTag.Any
1618

1719
/** Pickle [[Any]] by getting its class at runtime and looking
1820
* up the correct [[Pickler]] for that class if available or
1921
* generate it.
2022
*
2123
* Don't use [[AnyPicklerUnpickler]] for pickling classes with generic
22-
* types. Otherwise, it will fail because of the type erasure
23-
* and the lookup will replace the unknown type by [[Any]].
24+
* types. Otherwise, it will fail because of the type erasure.
25+
* The lookup will replace the unknown type parameters by [[Any]].
2426
*/
2527
override def pickle(picklee: Any, builder: PBuilder): Unit = {
26-
val clazz = picklee.getClass
27-
val classLoader = this.getClass.getClassLoader
28-
GRL.lock()
29-
val tag = try FastTypeTag.makeRaw(clazz)
30-
finally GRL.unlock()
31-
val p = currentRuntime.picklers.genPickler(classLoader, clazz, tag)
32-
p.asInstanceOf[Pickler[Any]].pickle(picklee, builder)
28+
29+
// Use nullPickler if null, get pickler otherwise
30+
val pickler = if (picklee == null) nullPickler else {
31+
val clazz = picklee.getClass
32+
val classLoader = this.getClass.getClassLoader
33+
GRL.lock()
34+
val tag = try FastTypeTag.makeRaw(clazz)
35+
finally GRL.unlock()
36+
val p = currentRuntime.picklers.genPickler(classLoader, clazz, tag)
37+
p.asInstanceOf[Pickler[Any]]
38+
}
39+
40+
pickler.pickle(picklee, builder)
41+
3342
}
3443

3544
/** Unpickle something as [[Any]] by looking up registered
3645
* unpicklers for [[tag]] or using runtime unpickler generation.
3746
*/
3847
def unpickle(tag: String, reader: PReader): Any = {
39-
val actualUnpickler = currentRuntime.picklers.genUnpickler(currentMirror, tag)
40-
actualUnpickler.unpickle(tag, reader)
48+
if (reader.atPrimitive) reader.readPrimitive()
49+
else {
50+
val actualUnpickler = currentRuntime.picklers.genUnpickler(currentMirror, tag)
51+
actualUnpickler.unpickle(tag, reader)
52+
}
4153
}
4254

4355
override def toString = "AnyPicklerUnpickler"

core/src/main/scala/scala/pickling/pickler/Either.scala

Lines changed: 149 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,180 @@
11
package scala.pickling
22
package pickler
33

4+
import scala.pickling.PicklingErrors.TypeMismatch
5+
46
/** Generate [[Pickler]]s and [[Unpickler]]s for [[Either]]
57
* and its subclasses [[Right]] and [[Left]].
68
*/
7-
trait EitherPicklers {
8-
// TODO(jsuereth) - Register pickler generators
9-
10-
implicit def pickleUnpickleLeft[L, R](implicit lp: Pickler[L], lu: Unpickler[L],
11-
t: FastTypeTag[Left[L,R]]): AbstractPicklerUnpickler[Left[L, R]] =
12-
new AbstractPicklerUnpickler[Left[L, R]] with AutoRegister[Left[L, R]] {
13-
override lazy val tag: FastTypeTag[Left[L, R]] = t
14-
override def pickle(picklee: Left[L, R], builder: PBuilder): Unit = {
15-
builder.beginEntry(picklee, tag)
16-
if(lp.tag.isEffectivelyPrimitive) builder.hintElidedType(lp.tag)
17-
builder.putField("a", b => lp.pickle(picklee.a, b))
18-
builder.endEntry()
19-
}
20-
override def unpickle(tag: String, reader: PReader): Any = {
21-
if (t.key == tag) {
22-
val rr = reader.readField("a")
23-
if(lp.tag.isEffectivelyPrimitive) rr.hintElidedType(lp.tag)
24-
Left(lu.unpickleEntry(rr).asInstanceOf[R])
25-
} else throw new PicklingException(s"LeftUnpickler can't unpickle: $tag")
26-
}
27-
override def toString = s"LeftPicklerUnpickler($tag)"
28-
}
29-
30-
implicit def pickleUnpickleRight[L,R](implicit rp: Pickler[R], ru: Unpickler[R],
31-
t: FastTypeTag[Right[L,R]]): AbstractPicklerUnpickler[Right[L,R]] =
9+
trait EitherPicklers extends EitherPicklersRuntime with GeneratorRegistry {
10+
11+
implicit def pickleUnpickleLeft[L, R]
12+
(implicit lp: Pickler[L], lu: Unpickler[L],
13+
t: FastTypeTag[Left[L,R]]): AbstractPicklerUnpickler[Left[L, R]] = {
14+
new AbstractPicklerUnpickler[Left[L, R]] with AutoRegister[Left[L, R]] {
15+
override lazy val tag: FastTypeTag[Left[L, R]] = t
16+
17+
override def pickle(picklee: Left[L, R], builder: PBuilder): Unit = {
18+
builder.beginEntry(picklee, tag)
19+
if (lp.tag.isEffectivelyPrimitive) builder.hintElidedType(lp.tag)
20+
builder.putField("a", b => lp.pickle(picklee.a, b))
21+
builder.endEntry()
22+
}
23+
24+
override def unpickle(tag: String, reader: PReader): Any = {
25+
if (t.key == tag) {
26+
val rr = reader.readField("a")
27+
if (lp.tag.isEffectivelyPrimitive) rr.hintElidedType(lp.tag)
28+
Left(lu.unpickleEntry(rr).asInstanceOf[R])
29+
} else throw TypeMismatch(List(t), List(FastTypeTag(tag)))
30+
}
31+
32+
override def toString = s"LeftPicklerUnpickler($tag)"
33+
}
34+
}
35+
36+
implicit def pickleUnpickleRight[L,R]
37+
(implicit rp: Pickler[R], ru: Unpickler[R],
38+
t: FastTypeTag[Right[L,R]]): AbstractPicklerUnpickler[Right[L,R]] = {
3239
new AbstractPicklerUnpickler[Right[L, R]] with AutoRegister[Right[L, R]] {
3340
override lazy val tag: FastTypeTag[Right[L, R]] = t
41+
3442
override def pickle(picklee: Right[L, R], builder: PBuilder): Unit = {
3543
builder.beginEntry(picklee, tag)
36-
if(rp.tag.isEffectivelyPrimitive) builder.hintElidedType(rp.tag)
44+
if (rp.tag.isEffectivelyPrimitive) builder.hintElidedType(rp.tag)
3745
builder.putField("b", b => rp.pickle(picklee.b, b))
3846
builder.endEntry()
3947
}
48+
4049
override def unpickle(tag: String, reader: PReader): Any = {
4150
if (t.key == tag) {
4251
val rr = reader.readField("b")
43-
if(rp.tag.isEffectivelyPrimitive) rr.hintElidedType(rp.tag)
52+
if (rp.tag.isEffectivelyPrimitive) rr.hintElidedType(rp.tag)
4453
Right(ru.unpickleEntry(rr).asInstanceOf[R])
45-
} else throw new PicklingException(s"RightUnpickler can't unpickle: $tag")
54+
} else throw TypeMismatch(List(t), List(FastTypeTag(tag)))
4655
}
56+
4757
override def toString = s"RightPicklerUnpickler($tag)"
4858
}
59+
}
4960

50-
implicit def pickleUnpickleEither[L,R](implicit rp: Pickler[Right[L,R]], ru: Unpickler[Right[L, R]],
51-
lp: Pickler[Left[L,R]], lu: Unpickler[Left[L, R]],
52-
t: FastTypeTag[Either[L,R]]): AbstractPicklerUnpickler[Either[L,R]] =
61+
implicit def pickleUnpickleEither[L,R]
62+
(implicit rp: Pickler[Right[L,R]], ru: Unpickler[Right[L, R]],
63+
lp: Pickler[Left[L,R]], lu: Unpickler[Left[L, R]],
64+
t: FastTypeTag[Either[L,R]]): AbstractPicklerUnpickler[Either[L,R]] = {
5365
new AbstractPicklerUnpickler[Either[L, R]] with AutoRegister[Either[L, R]] {
5466
override def pickle(picklee: Either[L, R], builder: PBuilder): Unit = {
5567
picklee match {
56-
case l: Left[L,R] => lp.pickle(l, builder)
57-
case r: Right[L,R] => rp.pickle(r, builder)
68+
case l: Left[L, R] => lp.pickle(l, builder)
69+
case r: Right[L, R] => rp.pickle(r, builder)
5870
}
5971
}
72+
6073
override def unpickle(tag: String, reader: PReader): Any = {
61-
if(tag == rp.tag.key) ru.unpickle(tag,reader)
62-
else if(tag == lp.tag.key) lu.unpickle(tag, reader)
63-
else throw new PicklingException(s"Unknown type tag for Either: $tag")
74+
if (tag == rp.tag.key) ru.unpickle(tag, reader)
75+
else if (tag == lp.tag.key) lu.unpickle(tag, reader)
76+
else throw TypeMismatch(List(rp.tag, lp.tag), List(FastTypeTag(tag)))
6477
}
78+
6579
override def tag: FastTypeTag[Either[L, R]] = t
66-
override def toString = s"EitherPicklerUnpickler($t)"
80+
81+
override def toString = s"EitherPicklerUnpickler($tag)"
6782
}
83+
}
84+
85+
locally {
86+
87+
registerPicklerAsGen(RuntimeLeftPicklerUnpickler)
88+
registerPicklerAsGen(RuntimeRightPicklerUnpickler)
89+
registerPicklerAsGen(RuntimeEitherPicklerUnpickler)
90+
91+
}
92+
93+
}
94+
95+
trait EitherPicklersRuntime extends GeneratorHelper {
96+
97+
/** Pickle as [[Any]] because the scala pickling
98+
* use [[Any]] as a placeholder for the type params.
99+
*/
100+
private def pickleAsAny[S <: Either[_, _]]
101+
(picklee: S, fullTpe: FastTypeTag[S],
102+
name: String, field: Any, builder: PBuilder) = {
103+
104+
builder.beginEntry(picklee, fullTpe)
105+
builder.putField(name, { b =>
106+
AnyPicklerUnpickler.pickle(field, b)
107+
})
108+
builder.endEntry()
109+
110+
}
111+
112+
/** Unpickle with a given unpickler a concrete field. */
113+
private def unpickleAsAny[T](unpickler: Unpickler[T],
114+
reader: PReader, name: String) = {
115+
val field = reader.readField(name)
116+
unpickler.unpickleEntry(field)
117+
}
118+
119+
120+
/** Custom runtime [[Pickler]] and [[Unpickler]] generator of [[Left]]. */
121+
object RuntimeLeftPicklerUnpickler
122+
extends AbstractPicklerUnpickler[Left[Any, Any]] {
123+
124+
val tag = FastTypeTag[Left[Any, Any]]("scala.util.Left[scala.Any,scala.Any]")
125+
126+
def pickle(picklee: Left[Any, Any], builder: PBuilder): Unit =
127+
pickleAsAny(picklee, tag, "a", picklee.a, builder)
128+
129+
def unpickle(tagTpe: String, reader: PReader): Any = {
130+
131+
val tpe = FastTypeTag.apply(tagTpe)
132+
val (leftTpe, _) = twoArgumentTagExtractor[Any, Any](tpe)
133+
Left(unpickleAsAny(getUnpickler(leftTpe, tag), reader, "a"))
134+
135+
}
136+
137+
}
138+
139+
/** Custom runtime [[Pickler]] and [[Unpickler]] generator of [[Right]]. */
140+
object RuntimeRightPicklerUnpickler
141+
extends AbstractPicklerUnpickler[Right[Any, Any]] {
142+
143+
val tag = FastTypeTag[Right[Any, Any]]("scala.util.Right[scala.Any,scala.Any]")
144+
145+
def pickle(picklee: Right[Any, Any], builder: PBuilder): Unit =
146+
pickleAsAny(picklee, tag, "b", picklee.b, builder)
147+
148+
def unpickle(tagTpe: String, reader: PReader): Any = {
149+
150+
val tpe = FastTypeTag.apply(tagTpe)
151+
val (_, rightTpe) = twoArgumentTagExtractor[Any, Any](tpe)
152+
Right(unpickleAsAny(getUnpickler(rightTpe, tag), reader, "b"))
153+
154+
}
155+
156+
}
157+
/** Custom runtime [[Pickler]] and [[Unpickler]] generator of [[Either]]. */
158+
object RuntimeEitherPicklerUnpickler
159+
extends AbstractPicklerUnpickler[Either[Any, Any]] {
160+
161+
val tag = FastTypeTag[Either[Any, Any]]("scala.util.Either[scala.Any,scala.Any]")
162+
163+
def pickle(picklee: Either[Any, Any], builder: PBuilder): Unit = {
164+
picklee match {
165+
case r: Right[Any, Any] =>
166+
RuntimeRightPicklerUnpickler.pickle(r, builder)
167+
case l: Left[Any, Any] =>
168+
RuntimeLeftPicklerUnpickler.pickle(l, builder)
169+
}
170+
}
171+
172+
def unpickle(tag: String, reader: PReader): Any = {
173+
// Tag is expected to be a concrete subclass of Either
174+
AnyPicklerUnpickler.unpickle(tag, reader)
175+
}
176+
177+
}
178+
179+
}
68180

69-
}

0 commit comments

Comments
 (0)